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

142 lines
5.5 KiB
EmacsLisp

(defun ambrevar/helm-ff--shell-interactive-buffer-p (buffer)
(with-current-buffer buffer
(and (eq major-mode 'shell-mode)
(save-excursion
(goto-char (point-min))
(comint-next-prompt 1)
(null (eql (point) (point-min)))))))
(defun ambrevar/helm-ff-switch-to-shell (_candidate)
"Like `helm-ff-switch-to-eshell' but for `M-x shell'.
With a numeric prefix arg switch to numbered shell buffer, if no
prefix arg provided and more than one shell buffer exists, provide
completions on those buffers. If only one shell buffer exists,
switch to this one, if no shell buffer exists or if the numeric
prefix arg shell buffer doesn't exists, create it and switch to it."
(let ((cd-shell
(lambda ()
(goto-char (point-max))
(comint-delete-input)
(insert (format "cd %s"
(shell-quote-argument
(if (tramp-tramp-file-p helm-ff-default-directory)
(tramp-file-name-localname
(tramp-dissect-file-name helm-ff-default-directory))
helm-ff-default-directory))))
(comint-send-input)))
(bufs (cl-loop for b in (mapcar 'buffer-name (buffer-list))
when (ambrevar/helm-ff--shell-interactive-buffer-p b)
collect b)))
(helm-aif (and (null helm-current-prefix-arg)
(if (cdr bufs)
(helm-comp-read "Switch to shell buffer: " bufs
:must-match t)
(car bufs)))
(switch-to-buffer it)
(shell))
(funcall cd-shell)))
(advice-add 'helm-ff-switch-to-eshell :override 'ambrevar/helm-ff-switch-to-shell)
;; TODO: Above code can be removed with Helm 3.6.3.
(defun ambrevar/shell-setup ()
(set (make-variable-buffer-local 'comint-input-history-ignore)
(regexp-opt '("^#" "^ " "^cd ")))
;; 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)