ambevar-dotfiles/.emacs.d/lisp/init-shell.el

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)