Input indentation for M-x shell

* lisp/comint.el
(comint-indent-input-line):
(comint-indent-input-line-default):
(comint-indent-input-region):
(comint-indent-input-region-default): New functions that implement a
general mechanism for input indentation through an indirect buffer in
comint derived major modes.
* lisp/shell.el (shell-mode): Set up input indentation according to
sh-mode (bug#51940).
This commit is contained in:
Miha Rihtaršič 2022-09-09 20:13:13 +02:00 committed by Lars Ingebrigtsen
parent c7ebe5a798
commit 3b105e978c
2 changed files with 89 additions and 8 deletions

View file

@ -4012,21 +4012,21 @@ This function is intended to be included as an entry of
(cons (point-marker) (match-string-no-properties 1 text)))))
;;; Input fontification through an indirect buffer
;;; Input fontification and indentation through an indirect buffer
;;============================================================================
;;
;; Modes derived from `comint-mode' can set up fontification input
;; text with the help of an indirect buffer whose major mode and
;; font-lock settings are set accordingly.
;; Modes derived from `comint-mode' can set up fontification and
;; indentation of input text with the help of an indirect buffer whose
;; major mode and font-lock settings are set accordingly.
(defvar-local comint-indirect-setup-function nil
"Function to set up an indirect comint fontification buffer.
This function is called by `comint-indirect-buffer' with zero
arguments after making an indirect buffer. It is usually set to
a major-mode command whose font-locking is desired for input
text. In order to prevent possible mode hooks from running, the
variable `delay-mode-hooks' is set to t prior to calling this
function and `change-major-mode-hook' along with
a major-mode command whose font-locking and indentation are
desired for input text. In order to prevent possible mode hooks
from running, the variable `delay-mode-hooks' is set to t prior
to calling this function and `change-major-mode-hook' along with
`after-change-major-mode-hook' are bound to nil.")
(defcustom comint-indirect-setup-hook nil
@ -4191,6 +4191,83 @@ function called, or nil, if no function was called (if BEG = END)."
(when return-beg
(cons (car return-beg) (car return-end)))))
(defun comint-indent-input-line (fun)
"Indent current line from comint process output or input.
If point is on output, call FUN, otherwise indent the current
line in the indirect buffer created by `comint-indirect-buffer',
which see."
(if (or comint-use-prompt-regexp
(eq (get-text-property (point) 'field) 'output))
(funcall fun)
(let ((point (point))
(min (point-min))
(max (point-max)))
(unwind-protect
(with-current-buffer (comint-indirect-buffer)
(narrow-to-region min max)
(goto-char point)
(narrow-to-region (field-beginning) (field-end))
(unwind-protect (funcall indent-line-function)
(setq point (point))))
(goto-char point)))))
(defun comint-indent-input-region (fun start end)
"Indent comint process output and input between START and END.
Output text between START and END is indented with FUN and input
text is indented in the indirect buffer created by
`comint-indirect-buffer', which see."
(if comint-use-prompt-regexp
(funcall fun start end)
(let ((opoint (copy-marker (point)))
final-point)
(unwind-protect
(comint--intersect-regions
(lambda (start end)
(goto-char opoint)
(if (= opoint (point))
(unwind-protect (funcall fun start end)
(setq final-point (copy-marker (point))))
(funcall fun start end)))
(lambda (start end)
(let ((min (point-min))
(max (point-max))
(final-point1 nil))
(unwind-protect
(with-current-buffer (comint-indirect-buffer)
(narrow-to-region min max)
(goto-char opoint)
(if (= opoint (point))
(unwind-protect
(funcall indent-region-function start end)
(setq final-point1 (point)))
(funcall indent-region-function start end)))
(when final-point1
(setq final-point (copy-marker final-point1))))))
start end)
(if final-point
(progn
(goto-char final-point)
(set-marker final-point nil))
(goto-char opoint))
(set-marker opoint nil)))))
(defun comint-indent-input-line-default ()
"Indent current line from comint process output or input.
If point is on output, indent the current line according to the
default value of `indent-line-function', otherwise indent the
current line in the indirect buffer created by
`comint-indirect-buffer', which see."
(comint-indent-input-line (default-value 'indent-line-function)))
(defun comint-indent-input-region-default (start end)
"Indent comint process output and input between START and END.
Output text between START and END is indented according to the
default value of `indent-region-function' and input text is
indented in the indirect buffer created by
`comint-indirect-buffer', which see."
(comint-indent-input-region (default-value 'indent-line-function)
start end))
(defun comint-indirect-buffer (&optional no-create)
"Return an indirect comint fontification buffer.
If an indirect buffer for the current buffer already exists,

View file

@ -653,6 +653,10 @@ command."
(message-log-max nil))
(sh-mode)))))
(setq-local indent-line-function #'comint-indent-input-line-default)
(setq-local indent-region-function
#'comint-indent-input-region-default)
;; This is not really correct, since the shell buffer does not really
;; edit this directory. But it is useful in the buffer list and menus.
(setq list-buffers-directory (expand-file-name default-directory))