140 lines
5.3 KiB
EmacsLisp
140 lines
5.3 KiB
EmacsLisp
(setq helm-ff-preferred-shell-mode 'shell-mode)
|
|
|
|
;;; Shared global history.
|
|
(defvar ambrevar/shell-history-global-ring nil
|
|
"The history ring shared across shell sessions.")
|
|
|
|
(defun ambrevar/shell-use-global-history ()
|
|
"Make shell history shared across different sessions."
|
|
(unless ambrevar/shell-history-global-ring
|
|
(when comint-input-ring-file-name
|
|
(comint-read-input-ring))
|
|
(setq ambrevar/shell-history-global-ring (or comint-input-ring (make-ring comint-input-ring-size))))
|
|
(setq comint-input-ring ambrevar/shell-history-global-ring))
|
|
|
|
(defun ambrevar/shell-history-remove-duplicates ()
|
|
(require 'functions) ; For `ambrevar/ring-delete-first-item-duplicates'.
|
|
(ambrevar/ring-delete-first-item-duplicates comint-input-ring))
|
|
|
|
(defvar ambrevar/comint-input-history-ignore (concat "^" (regexp-opt '("#" " " "cd ")))
|
|
"`comint-input-history-ignore' can only be customized globally
|
|
because `comint-read-input-ring' uses a temp buffer.")
|
|
|
|
(defun ambrevar/shell-remove-ignored-inputs-from-ring ()
|
|
"Discard last command from history if it matches
|
|
`ambrevar/comint-input-history-ignore'."
|
|
(unless (ring-empty-p comint-input-ring)
|
|
(when (string-match ambrevar/comint-input-history-ignore
|
|
(ring-ref comint-input-ring 0))
|
|
(ring-remove comint-input-ring 0))))
|
|
|
|
(defun ambrevar/shell-sync-input-ring (_)
|
|
(ambrevar/shell-history-remove-duplicates)
|
|
(ambrevar/shell-remove-ignored-inputs-from-ring)
|
|
(comint-write-input-ring))
|
|
|
|
(defun ambrevar/shell-setup ()
|
|
(setq comint-input-ring-file-name
|
|
(expand-file-name "shell-history" user-emacs-directory))
|
|
(ambrevar/shell-use-global-history)
|
|
|
|
;; Write history on every command, not just on exit.
|
|
(add-hook 'comint-input-filter-functions 'ambrevar/shell-sync-input-ring nil t)
|
|
|
|
;; Only ending with '#' or '$' but seems slower:
|
|
;; (setq comint-prompt-regexp "^[^#$]*
|
|
;; [^#$]*[#$>] +")
|
|
(setq comint-prompt-regexp "^[^#$%>]*
|
|
\[^#$%>]*[#$%>] +"))
|
|
|
|
(add-hook 'shell-mode-hook 'ambrevar/shell-setup)
|
|
|
|
(defun ambrevar/shell-prompt-begin-position ()
|
|
;; We need this convoluted function because `looking-at-p' does not work on
|
|
;; multiline regexps _and_ `re-search-backward' skips the current line.
|
|
(save-excursion
|
|
(let ((old-point (point)))
|
|
(max
|
|
(save-excursion
|
|
;; Right result if not on prompt.
|
|
(call-interactively #'comint-previous-prompt)
|
|
(re-search-backward comint-prompt-regexp)
|
|
(point))
|
|
(save-excursion
|
|
;; Right result if on first char after prompt.
|
|
(re-search-backward comint-prompt-regexp)
|
|
(point))
|
|
(save-excursion
|
|
;; Right result if on prompt.
|
|
(call-interactively #'comint-next-prompt)
|
|
(re-search-backward comint-prompt-regexp)
|
|
(if (<= (point) old-point)
|
|
(point)
|
|
(point-min)))))))
|
|
|
|
(defun ambrevar/shell-prompt-end-position ()
|
|
(save-excursion
|
|
(goto-char (ambrevar/shell-prompt-begin-position))
|
|
(call-interactively #'comint-next-prompt)
|
|
(point)))
|
|
|
|
(defun ambrevar/shell-prompt ()
|
|
(buffer-substring-no-properties
|
|
(ambrevar/shell-prompt-begin-position)
|
|
(ambrevar/shell-prompt-end-position)))
|
|
|
|
(defun ambrevar/shell-propertize-prompt () ; Inspired by `shx--propertize-prompt'.
|
|
"Add a mouseover timestamp to the last prompt."
|
|
(let ((inhibit-read-only t)
|
|
(inhibit-field-text-motion t))
|
|
(add-text-properties
|
|
(save-excursion
|
|
(re-search-backward comint-prompt-regexp nil :noerror)
|
|
(point))
|
|
(process-mark (get-buffer-process (current-buffer)))
|
|
`(help-echo ,(format-time-string "%F %T")))))
|
|
|
|
(defun ambrevar/shell-send-input ()
|
|
"Send or parse the input currently written at the prompt.
|
|
In normal circumstances this input is additionally filtered by
|
|
`shx-filter-input' via `comint-mode'."
|
|
(interactive)
|
|
(ambrevar/shell-propertize-prompt)
|
|
(comint-send-input))
|
|
(define-key shell-mode-map (kbd "<return>") 'ambrevar/shell-send-input)
|
|
|
|
(defun ambrevar/shell-command-duration ()
|
|
"Return duration of command at point in a `shell' buffer."
|
|
(interactive)
|
|
(let ((begin (ignore-errors (parse-time-string (get-text-property
|
|
(ambrevar/shell-prompt-begin-position)
|
|
'help-echo))))
|
|
(end (parse-time-string (save-excursion
|
|
(goto-char (ambrevar/shell-prompt-end-position))
|
|
(call-interactively #'comint-next-prompt)
|
|
(ambrevar/shell-prompt)))))
|
|
(if begin
|
|
(message "Command took %.0f seconds."
|
|
(- (float-time (apply 'encode-time end))
|
|
(float-time (apply 'encode-time begin))))
|
|
(message "No timestamp."))))
|
|
|
|
(defun ambrevar/shell-narrow-to-prompt ()
|
|
"Narrow buffer to prompt at point."
|
|
(interactive)
|
|
(let ((begin (ambrevar/shell-prompt-begin-position)))
|
|
(narrow-to-region
|
|
begin
|
|
(save-excursion
|
|
(goto-char (ambrevar/shell-prompt-end-position))
|
|
(call-interactively #'comint-next-prompt)
|
|
(if (= begin
|
|
(ambrevar/shell-prompt-begin-position))
|
|
(point-max)
|
|
(ambrevar/shell-prompt-begin-position))))))
|
|
(define-key shell-mode-map (kbd "C-x n d") 'ambrevar/shell-narrow-to-prompt)
|
|
|
|
(define-key shell-mode-map (kbd "C-x M-O") 'comint-truncate-buffer)
|
|
|
|
(provide 'init-shell)
|