GNU bug report logs - #51940
29.0.50; [PATCH] Fontification and indentation in M-x shell and ielm

Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.

Package: emacs; Reported by: miha@HIDDEN; Keywords: patch; dated Thu, 18 Nov 2021 09:44:02 UTC; Maintainer for emacs is bug-gnu-emacs@HIDDEN.

Message received at 51940 <at> debbugs.gnu.org:


Received: (at 51940) by debbugs.gnu.org; 28 Nov 2021 12:15:41 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sun Nov 28 07:15:41 2021
Received: from localhost ([127.0.0.1]:35066 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1mrJ5n-00079D-JZ
	for submit <at> debbugs.gnu.org; Sun, 28 Nov 2021 07:15:41 -0500
Received: from kamnitnik.top ([209.250.245.214]:54918)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <miha@HIDDEN>) id 1mrJ5k-000794-LK
 for 51940 <at> debbugs.gnu.org; Sun, 28 Nov 2021 07:15:38 -0500
Received: from localhost (BSN-77-156-43.static.siol.net [193.77.156.43])
 by kamnitnik.top (Postfix) with ESMTPSA id 75C659C717;
 Sun, 28 Nov 2021 12:15:35 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kamnitnik.top;
 s=mail; t=1638101735;
 bh=rwbjzIstmLQiWMibAhkkYqK0TcBChQdQ023mkyIY240=;
 h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
 b=UfzMtvr9aFsnGbDhnR70K+3znvjUm7A1Trt3gxvsLVP7aSWOcOULSfTF7Zs/+AsSk
 ooCqyJSHoOPi/X4PTPEDKzDp6jtj72vo4c6e3zrIqf6WxAEKHIUsCf393ghvzmwbWu
 dKLxc5QVEyKOm+A14OWPUUAAClxXBdWtrjUyaSomuLi9rN1yaxmNRCXYhEOIKdnFU2
 FrAeHxocxRGResHmlOXzWPTloUc/1+6B6hD3mdyY7WaQIWPTE0rl7wLZl02T7iZNT8
 /tqk799ys5e6G1GH9BnmhEC1MUeixm7uZmq9rANZoWfrQH7oWcye0ALJvKgDJf0321
 vmtWtyScWOTVw==
From: <miha@HIDDEN>
To: Eli Zaretskii <eliz@HIDDEN>
Subject: Re: bug#51940: 29.0.50; [PATCH] Fontification and indentation in
 M-x shell and ielm
In-Reply-To: <8335nk7bz1.fsf@HIDDEN>
References: <87sfvteqx2.fsf@miha-pc> <83ee7dyar2.fsf@HIDDEN>
 <87pmqxeghn.fsf@miha-pc> <8335nk7bz1.fsf@HIDDEN>
Date: Sun, 28 Nov 2021 13:20:27 +0100
Message-ID: <871r30jwtw.fsf@miha-pc>
MIME-Version: 1.0
Content-Type: multipart/signed; boundary="==-=-=";
 micalg=pgp-sha256; protocol="application/pgp-signature"
X-Spam-Score: 2.5 (++)
X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 Content preview:  Eli Zaretskii <eliz@HIDDEN> writes: > The doc strings are
 still not specific enough, sorry for being unclear > in what I meant. Let's
 take an example: > >> +(define-minor-mode comint-fl-mode >> + "Enable input
 fontification. > > The fir [...] 
 Content analysis details:   (2.5 points, 10.0 required)
 pts rule name              description
 ---- ---------------------- --------------------------------------------------
 -0.0 SPF_PASS               SPF: sender matches SPF record
 2.0 PDS_OTHER_BAD_TLD      Untrustworthy TLDs
 [URI: kamnitnik.top (top)]
 -0.0 SPF_HELO_PASS          SPF: HELO matches SPF record
 0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
X-Debbugs-Envelope-To: 51940
Cc: 51940 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: 2.5 (++)
X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 
 Content preview:  Eli Zaretskii <eliz@HIDDEN> writes: > The doc strings are
    still not specific enough, sorry for being unclear > in what I meant. Let's
    take an example: > >> +(define-minor-mode comint-fl-mode >> + "Enable input
    fontification. > > The fir [...] 
 
 Content analysis details:   (2.5 points, 10.0 required)
 
  pts rule name              description
 ---- ---------------------- --------------------------------------------------
 -0.0 SPF_PASS               SPF: sender matches SPF record
  2.0 PDS_OTHER_BAD_TLD      Untrustworthy TLDs
                             [URI: kamnitnik.top (top)]
 -0.0 SPF_HELO_PASS          SPF: HELO matches SPF record
  0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
  1.0 BULK_RE_SUSP_NTLD      Precedence bulk and RE: from a suspicious TLD
 -1.0 MAILING_LIST_MULTI     Multiple indicators imply a widely-seen list
                             manager

--==-=-=
Content-Type: multipart/mixed; boundary="=-=-="

--=-=-=
Content-Type: text/plain

Eli Zaretskii <eliz@HIDDEN> writes:

> The doc strings are still not specific enough, sorry for being unclear
> in what I meant.  Let's take an example:
>
>> +(define-minor-mode comint-fl-mode
>> +  "Enable input fontification.
>
> The first line should say something like
>
>   Enable input fontification in comint buffers.
>
>> +(defun comint--fl-off ()
>> +  "Disable input fontification for the current buffer."
>
> Similarly here:
>
>   Disable input fontification in the current comint buffer.
>
>> +(defun comint--fl-fontify-region (fun beg end verbose)
>> +  "Around advice for `font-lock-fontify-region-function'.
>
> Likewise here: mention comint in the first line.
>
>> +(defcustom shell-highlight-undef-aliases nil
>> +  "List of commands to highlight as a command alias."
>
> Here, the doc string should mention shell command aliases, otherwise
> it's too general ("command alias" doesn't necessarily imply a shell).
>
> Please go over the doc strings with this criterion in mind, and see
> what else needs to be fixed in the same way.

Thanks for feedback, revised patches attached. I hope I've clarified the
doc strings this time. I've also made a minor code simplification in the
fourth patch.

Best regards.


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0001-Implement-a-general-input-fontification-mechanism-fo.patch
Content-Transfer-Encoding: quoted-printable

From=20ec5c05b0b32ce3ed52d95307f5902b7589e7aefd Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 22:51:51 +0100
Subject: [PATCH 1/5] Implement a general input fontification mechanism for
 comint modes

* lisp/comint.el
(comint-indirect-setup-function): New variable for comint derived
major modes to customize.
(comint-fl-mode): New minor mode that fontifies input text through an
indirect buffer.
(comint-indirect-setup-hook):
(comint--indirect-buffer):
(comint--fl-saved-jit-lock-contextually):
(comint--fl-on):
(comint--fl-off):
(comint--fl-ppss-flush-indirect):
(comint--fl-fontify-region):
(comint--intersect-regions):
(comint-indirect-buffer):
(comint--indirect-cleanup): New functions and buffer-local variables.
=2D--
 lisp/comint.el | 229 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 229 insertions(+)

diff --git a/lisp/comint.el b/lisp/comint.el
index 544f0b8b82..dbbd687e18 100644
=2D-- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -1931,6 +1931,7 @@ comint-send-input
               (when comint-highlight-input
                 (add-text-properties beg end
                                      '( font-lock-face comint-highlight-in=
put
+                                        comint--fl-inhibit-fontification t
                                         front-sticky t )))
               (unless comint-use-prompt-regexp
                 ;; Give old user input a field property of `input', to
@@ -4042,6 +4043,234 @@ comint-osc-hyperlink-handler
              (cons (point-marker) (match-string-no-properties 1 text)))))
=20
 
+;;; Input fontification through an indirect buffer
+;;=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
+;;
+;; 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.
+
+(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
+`after-change-major-mode-hook' are bound to nil.")
+
+(defcustom comint-indirect-setup-hook nil
+  "Hook run after setting up an indirect comint fontification buffer.
+It is run after the indirect buffer is set up for fontification
+of input regions."
+  :group 'comint
+  :type 'hook
+  :version "29.1")
+
+(defvar-local comint--indirect-buffer nil
+  "Indirect buffer used for input fontification.")
+
+(defvar-local comint--fl-saved-jit-lock-contextually nil)
+
+(define-minor-mode comint-fl-mode
+  "Enable input fontification in the current comint buffer.
+This minor mode is useful if the current major mode derives from
+`comint-mode' and if `comint-indirect-setup-function' is set.
+Comint modes that support input fontification usually set this
+variable buffer-locally to a major-mode command whose
+font-locking is desired for input text.
+
+Input text is fontified through an indirect buffer created with
+`comint-indirect-buffer', which see.
+
+This function signals an error if `comint-use-prompt-regexp' is
+non-nil.  Input fontification isn't compatible with this
+setting."
+  :lighter nil
+  (if comint-fl-mode
+      (let ((success nil))
+        (unwind-protect
+            (progn
+              (comint--fl-on)
+              (setq success t))
+          (unless success
+            (setq comint-fl-mode nil)
+            (comint--fl-off))))
+    (comint--fl-off)))
+
+(defun comint--fl-on ()
+  "Enable input fontification in the current comint buffer."
+  (comint--fl-off)
+
+  (when comint-use-prompt-regexp
+    (error
+     "Input fontification is incompatible with `comint-use-prompt-regexp'"=
))
+
+  (add-function :around (local 'font-lock-fontify-region-function)
+                #'comint--fl-fontify-region)
+  ;; `before-change-functions' are only run in the current buffer and
+  ;; not in its indirect buffers, which means that we must manually
+  ;; flush ppss cache
+  (add-hook 'before-change-functions
+            #'comint--fl-ppss-flush-indirect 99 t)
+
+  ;; Set up contextual fontification
+  (unless (booleanp jit-lock-contextually)
+    (setq comint--fl-saved-jit-lock-contextually
+          jit-lock-contextually)
+    (setq-local jit-lock-contextually t)
+    (when jit-lock-mode
+      (jit-lock-mode t))))
+
+(defun comint--fl-off ()
+  "Disable input fontification in the current comint buffer."
+  (remove-function (local 'font-lock-fontify-region-function)
+                   #'comint--fl-fontify-region)
+  (remove-hook 'before-change-functions
+               #'comint--fl-ppss-flush-indirect t)
+
+  ;; Reset contextual fontification
+  (when comint--fl-saved-jit-lock-contextually
+    (setq-local jit-lock-contextually
+                comint--fl-saved-jit-lock-contextually)
+    (setq comint--fl-saved-jit-lock-contextually nil)
+    (when jit-lock-mode
+      (jit-lock-mode t)))
+
+  (font-lock-flush))
+
+(defun comint--fl-ppss-flush-indirect (beg &rest rest)
+  (when-let ((buf (comint-indirect-buffer t)))
+    (with-current-buffer buf
+      (when (memq #'syntax-ppss-flush-cache before-change-functions)
+        (apply #'syntax-ppss-flush-cache beg rest)))))
+
+(defun comint--fl-fontify-region (fun beg end verbose)
+  "Fontify process output and user input in the current comint buffer.
+First, highlight the region between BEG and END using FUN.  Then
+highlight only the input text in the region with the help of an
+indirect buffer.  VERBOSE is passed to the fontify-region
+functions.  Skip fontification of input regions with non-nil
+`comint--fl-inhibit-fontification' text property."
+  (pcase (funcall fun beg end verbose)
+    (`(jit-lock-bounds ,beg1 . ,end1)
+     (setq beg beg1 end end1)))
+  (pcase
+      (let ((min (point-min))
+            (max (point-max)))
+        (with-current-buffer (comint-indirect-buffer)
+          (narrow-to-region min max)
+          (comint--intersect-regions
+           nil (lambda (beg end)
+                 (unless (get-text-property
+                          beg 'comint--fl-inhibit-fontification)
+                   (font-lock-fontify-region beg end verbose)))
+           beg end)))
+    (`((jit-lock-bounds ,beg1 . ,_) . (jit-lock-bounds ,_ . ,end1))
+     (setq beg (min beg beg1))
+     (setq end (max end end1))))
+
+  `(jit-lock-bounds ,beg . ,end))
+
+(defun comint--intersect-regions (fun-output fun-input beg end)
+  "Iterate over comint output and input regions between BEG and END.
+Divide the region specified by BEG and END into smaller regions
+that cover either process output (its 'field property is 'output)
+or input (all remaining text).  Interchangeably call FUN-OUTPUT
+on each output region, and FUN-INPUT on each input region.
+
+FUN-OUTPUT and FUN-INPUT are passed two arguments, the beginning
+and end of the smaller region.  Before calling each function,
+narrow the buffer to the surrounding process output or input.  You
+can also pass nil as either function to skip its corresponding
+regions.
+
+Return a cons cell of return values of the first and last
+function called, or nil, if no function was called (if BEG =3D END)."
+  (let ((beg1 beg)
+        (end1 (copy-marker nil t))
+        (return-beg nil) (return-end nil)
+        (is-output (eq (get-text-property beg 'field) 'output)))
+    (setq end (copy-marker end t))
+
+    (while (< beg1 end)
+      (set-marker
+       end1 (or (if is-output
+                    (text-property-not-all beg1 end 'field 'output)
+                  (text-property-any beg1 end 'field 'output))
+                end))
+      (when-let ((fun (if is-output fun-output fun-input)))
+        (save-restriction
+          (let ((beg2 beg1)
+                (end2 end1))
+            (when (=3D beg2 beg)
+              (setq beg2 (field-beginning beg2)))
+            (when (=3D end2 end)
+              (setq end2 (field-end end2)))
+            ;; Narrow to the whole field surrounding the region
+            (narrow-to-region beg2 end2))
+          (setq return-end (list (funcall fun beg1
+                                          (marker-position end1)))))
+        (unless return-beg
+          (setq return-beg return-end)))
+      (setq beg1 (marker-position end1))
+      (setq is-output (not is-output)))
+
+    (set-marker end nil)
+    (set-marker end1 nil)
+    (when return-beg
+      (cons (car return-beg) (car return-end)))))
+
+(defun comint-indirect-buffer (&optional no-create)
+  "Return an indirect comint fontification buffer.
+If an indirect buffer for the current buffer already exists,
+return it, otherwise create it first and set it up by calling
+`comint-indirect-setup-function' with zero arguments, turning on
+font-lock, and running `comint-indirect-setup-hook'.  This setup
+happens with `delay-mode-hooks' set to t in order to prevent
+possible SETUP-FUN's mode hooks from running.
+
+If an indirect buffer doesn't exist and NO-CREATE is non-nil,
+return nil."
+  (or
+   comint--indirect-buffer
+   (unless no-create
+     (let ((setup-hook
+            (if (local-variable-p 'comint-indirect-setup-hook)
+                (list comint-indirect-setup-hook)))
+           (setup-fun comint-indirect-setup-function))
+
+       (add-hook 'change-major-mode-hook #'comint--indirect-cleanup
+                 nil t)
+
+       (with-current-buffer
+           (setq comint--indirect-buffer
+                 (make-indirect-buffer
+                  (current-buffer)
+                  (generate-new-buffer-name
+                   (concat " " (buffer-name) "-comint-indirect"))))
+         (setq-local delay-mode-hooks t)
+         (when setup-fun
+           (let ((change-major-mode-hook nil)
+                 (after-change-major-mode-hook nil))
+             (funcall setup-fun)))
+         (setq-local font-lock-dont-widen t)
+         (setq-local font-lock-support-mode nil)
+         (font-lock-mode)
+         (when setup-hook
+           (setq-local comint-indirect-setup-hook
+                       (car setup-hook)))
+         (run-hooks 'comint-indirect-setup-hook))
+       comint--indirect-buffer))))
+
+(defun comint--indirect-cleanup ()
+  (when comint--indirect-buffer
+    (kill-buffer comint--indirect-buffer)
+    (setq comint--indirect-buffer nil)))
+
+
+
 ;;; Converting process modes to use comint mode
 ;;=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
 ;; The code in the Emacs 19 distribution has all been modified to use comi=
nt
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0002-Input-fontification-for-M-x-shell.patch
Content-Transfer-Encoding: quoted-printable

From=20c4eac600dffc7cfd04e260059cb40757f46fa8a0 Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:13:03 +0100
Subject: [PATCH 2/5] Input fontification for M-x shell

* lisp/shell.el (shell-comint-fl-enable): New user option to control
input fontification.
(shell-indirect-setup-hook): New hook.
(shell-mode): Set up and enable input fontification.
=2D--
 lisp/shell.el | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/lisp/shell.el b/lisp/shell.el
index 370532ea46..a91a59f070 100644
=2D-- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -306,6 +306,22 @@ shell-input-autoexpand
 		 (const :tag "on" t))
   :group 'shell)
=20
+(defcustom shell-comint-fl-enable t
+  "Enable highlighting of input in shell buffers.
+This variable only has effect when the shell is started.  Use the
+command `comint-fl-mode' to toggle highlighting of input."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom shell-indirect-setup-hook nil
+  "Hook run after setting up an indirect shell fontification buffer."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
 (defvar shell-dirstack nil
   "List of directories saved by pushd in this buffer's shell.
 Thus, this does not include the shell's current directory.")
@@ -516,6 +532,8 @@ shell-completion-vars
=20
 (put 'shell-mode 'mode-class 'special)
=20
+(defvar sh-shell-file)
+
 (define-derived-mode shell-mode comint-mode "Shell"
   "Major mode for interacting with an inferior shell.
 \\<shell-mode-map>
@@ -572,6 +590,11 @@ shell-mode
 control whether input and output cause the window to scroll to the end of =
the
 buffer."
   :interactive nil
+  :after-hook
+  (and (null comint-use-prompt-regexp)
+       shell-comint-fl-enable
+       (comint-fl-mode))
+
   (setq comint-prompt-regexp shell-prompt-pattern)
   (shell-completion-vars)
   (setq-local paragraph-separate "\\'")
@@ -591,6 +614,19 @@ shell-mode
   (setq-local ansi-color-apply-face-function #'shell-apply-ansi-color)
   (shell-reapply-ansi-color)
=20
+  (add-hook 'comint-indirect-setup-hook
+            #'shell-indirect-setup-hook 'append t)
+  (setq comint-indirect-setup-function
+        (let ((shell shell--start-prog))
+          (lambda ()
+            (require 'sh-script)
+            (cl-letf
+                (((default-value 'sh-shell-file)
+                  (or shell sh-shell-file))
+                 (inhibit-message t)
+                 (message-log-max nil))
+              (sh-mode)))))
+
   ;; 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))
@@ -645,6 +681,10 @@ shell-mode
                     ": [[:digit:]]+:[[:digit:]]+;")))
     (comint-read-input-ring t)))
=20
+(defun shell-indirect-setup-hook ()
+  "Run `shell-indirect-setup-hook'."
+  (run-hooks 'shell-indirect-setup-hook))
+
 (defun shell-apply-ansi-color (beg end face)
   "Apply FACE as the ansi-color face for the text between BEG and END."
   (when face
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0003-Input-fontification-for-M-x-ielm.patch
Content-Transfer-Encoding: quoted-printable

From=20948c02d0a03c18bbfbd098f9cbe668e5dd30b6f7 Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:16:23 +0100
Subject: [PATCH 3/5] Input fontification for M-x ielm

* lisp/ielm.el (ielm-comint-fl-enable): New user option to control
input fontification.
(ielm-indirect-setup-hook): New hook.
(inferior-emacs-lisp-mode): Set up and enable input fontification.
=2D--
 lisp/ielm.el | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/lisp/ielm.el b/lisp/ielm.el
index 39820a893a..853868fa70 100644
=2D-- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -472,6 +472,27 @@ ielm-set-pm
   ;; Set the process mark in the current buffer to POS.
   (set-marker (process-mark (get-buffer-process (current-buffer))) pos))
=20
+;;; Input fontification
+
+(defcustom ielm-comint-fl-enable t
+  "Enable highlighting of input in ielm buffers.
+This variable only has effect when creating an ielm buffer.  Use
+the command `comint-fl-mode' to toggle highlighting of input in
+an already existing ielm buffer."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom ielm-indirect-setup-hook nil
+  "Hook run after setting up an indirect ielm fontification buffer."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
+(defun ielm-indirect-setup-hook ()
+  "Run `ielm-indirect-setup-hook'."
+  (run-hooks 'ielm-indirect-setup-hook))
+
 ;;; Major mode
=20
 (define-derived-mode inferior-emacs-lisp-mode comint-mode "IELM"
@@ -526,6 +547,10 @@ inferior-emacs-lisp-mode
 Customized bindings may be defined in `ielm-map', which currently contains:
 \\{ielm-map}"
   :syntax-table emacs-lisp-mode-syntax-table
+  :after-hook
+  (and (null comint-use-prompt-regexp)
+       ielm-comint-fl-enable
+       (comint-fl-mode))
=20
   (setq comint-prompt-regexp (concat "^" (regexp-quote ielm-prompt)))
   (setq-local paragraph-separate "\\'")
@@ -564,6 +589,10 @@ inferior-emacs-lisp-mode
   (setq-local font-lock-defaults
        '(ielm-font-lock-keywords nil nil ((?: . "w") (?- . "w") (?* . "w")=
)))
=20
+  (add-hook 'comint-indirect-setup-hook
+            #'ielm-indirect-setup-hook 'append t)
+  (setq comint-indirect-setup-function #'emacs-lisp-mode)
+
   ;; A dummy process to keep comint happy. It will never get any input
   (unless (comint-check-proc (current-buffer))
     ;; Was cat, but on non-Unix platforms that might not exist, so
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0004-Highlight-non-existent-commands-in-M-x-shell.patch
Content-Transfer-Encoding: quoted-printable

From=200f3ec0f20a407f68aff6cafd6694f492ca3f8d44 Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:21:03 +0100
Subject: [PATCH 4/5] Highlight non-existent commands in M-x shell

* lisp/shell.el (shell-mode): Enable highlighting of non-existent
commands if requested.
(shell-highlight-undef-aliases):
(shell-highlight-undef-remote-file-name-inhibit-cache): New user
options.
(shell-highlight-undef-mode): New minor mode.
(shell-highlight-undef-defined-face):
(shell-highlight-undef-undefined-face):
(shell-highlight-undef-alias-face): New faces.
(shell-highlight-undef--exec-cache):
(shell-highlight-undef--face):
(shell-highlight-undef-keywords):
(shell-highlight-undef-regexp):
(shell-highlight-undef--executable-find):
(shell-highlight-undef-matcher):
(shell-highlight-undef--indirect):
(shell-highlight--setup):
(shell-highlight-undef-reset-mode): New functions and buffer local
variables.
=2D--
 lisp/shell.el | 230 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 227 insertions(+), 3 deletions(-)

diff --git a/lisp/shell.el b/lisp/shell.el
index a91a59f070..71a37c37ca 100644
=2D-- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -322,6 +322,16 @@ shell-indirect-setup-hook
   :safe 'booleanp
   :version "29.1")
=20
+(defcustom shell-highlight-undef-enable nil
+  "Enable highlighting of undefined commands in shell buffers.
+This variable only has effect when the shell is started.  Use the
+command `shell-highlight-undef-mode' to toggle highlighting of
+undefined commands."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
 (defvar shell-dirstack nil
   "List of directories saved by pushd in this buffer's shell.
 Thus, this does not include the shell's current directory.")
@@ -591,9 +601,11 @@ shell-mode
 buffer."
   :interactive nil
   :after-hook
=2D  (and (null comint-use-prompt-regexp)
=2D       shell-comint-fl-enable
=2D       (comint-fl-mode))
+  (unless comint-use-prompt-regexp
+    (if shell-comint-fl-enable
+        (comint-fl-mode))
+    (if shell-highlight-undef-enable
+        (shell-highlight-undef-mode)))
=20
   (setq comint-prompt-regexp shell-prompt-pattern)
   (shell-completion-vars)
@@ -1471,6 +1483,218 @@ shell-narrow-to-prompt
            (point-max)
          (shell--prompt-begin-position))))))
=20
+;;; Highlight undefined commands
+;;
+;; To highlight non-existent shell commands, customize
+;; `shell-highlight-undef-enable' to t.  To highlight some shell
+;; commands as aliases, add them to `shell-highlight-undef-aliases'.
+
+(defcustom shell-highlight-undef-aliases nil
+  "List of shell commands to highlight as a command alias."
+  :group 'shell
+  :type '(repeat string)
+  :version "29.1")
+
+(defface shell-highlight-undef-defined-face
+  '((t :inherit 'font-lock-function-name-face))
+  "Face used for existent shell commands."
+  :group 'shell
+  :version "29.1")
+
+(defface shell-highlight-undef-undefined-face
+  '((t :inherit 'font-lock-warning-face))
+  "Face used for non-existent shell commands."
+  :group 'shell
+  :version "29.1")
+
+(defface shell-highlight-undef-alias-face
+  '((t :inherit 'font-lock-variable-name-face))
+  "Face used for shell command aliases."
+  :group 'shell
+  :version "29.1")
+
+(defcustom shell-highlight-undef-remote-file-name-inhibit-cache nil
+  "Whether to use cache to determine fontification a shell command.
+When fontification of non-existent commands is enabled on a
+remote shell buffer, use cache to speed up searching for
+executable files on the remote machine.  This options is used to
+control expiry of this cache.  See
+`remote-file-name-inhibit-cache' for description."
+  :group 'faces
+  :type '(choice
+          (const :tag "Do not inhibit file name cache" nil)
+          (const :tag "Do not use file name cache" t)
+          (integer :tag "Do not use file name cache"
+                   :format "Do not use file name cache older than %v secon=
ds"
+                   :value 10))
+  :version "29.1")
+
+(defvar shell--highlight-undef-exec-cache nil
+  "Cache of executable files found in `exec-path'.
+An alist, whose elements are of the form
+\(REMOTE TIME EXECUTABLES), where REMOTE is a string, returned by
+`file-remote-p', TIME is the return value of `float-time' end
+EXECUTABLES is a hash table with keys being the base-names of
+executable files.
+
+Cache expiry is controlled by the user option
+`remote-file-name-inhibit-cache'.")
+
+(defvar shell--highlight-undef-face 'shell-highlight-undef-defined-face)
+
+(defvar shell-highlight-undef-keywords
+  `((,#'shell-highlight-undef-matcher 6 shell--highlight-undef-face)))
+
+(defvar-local shell-highlight-undef-regexp regexp-unmatchable)
+
+(defun shell--highlight-undef-executable-find (command)
+  "Return non-nil if COMMAND is found in `exec-path'.
+Similar to `executable-find', but use cache stored in
+`shell--highlight-undef-exec-cache'."
+  (let ((remote (file-remote-p default-directory))
+        as ret found-in-cache delta-time)
+    (if (null remote)
+        (executable-find command)
+
+      (setq delta-time
+            shell-highlight-undef-remote-file-name-inhibit-cache)
+
+      (pcase (setq as (assoc remote shell--highlight-undef-exec-cache))
+        (`(,_ ,time ,hash)
+         (when (pcase delta-time
+                 ((pred numberp) (<=3D (float-time) (+ time delta-time)))
+                 ('t nil)
+                 ('nil t))
+           (setq ret (gethash command hash))
+           (setq found-in-cache t)))
+        (_ (setq as (list remote 0 (make-hash-table :test #'equal)))
+           (push as shell--highlight-undef-exec-cache)))
+
+      (if found-in-cache
+          ret
+        ;; Build cache
+        (setcar (cdr as) (float-time))
+        (let ((hash (clrhash (caddr as))))
+          (dolist (dir (exec-path))
+            (pcase-dolist (`(,f . ,attr)
+                           (condition-case nil
+                               (directory-files-and-attributes
+                                (concat remote dir) nil nil 'nosort 'integ=
er)
+                             (file-error nil)))
+              ;; Approximation.  Assume every non-directory file in $PATH =
is an
+              ;; executable.  Alternatively, we could check
+              ;; `file-executable-p', but doing so for every file in $PATH=
 is
+              ;; slow on remote machines.
+              (unless (eq t (file-attribute-type attr))
+                (puthash f t hash))))
+          (gethash command hash))))))
+
+(defun shell-highlight-undef-matcher (end)
+  "Matcher used to highlight shell commands up to END."
+  (when (re-search-forward shell-highlight-undef-regexp end t)
+    (save-match-data
+      (let ((cmd (match-string 6))
+            (beg (match-beginning 6)))
+        (setq shell--highlight-undef-face
+              (let* ((buf (buffer-base-buffer))
+                     (default-directory
+                      (if buf (buffer-local-value 'default-directory buf)
+                        default-directory)))
+                (cond
+                 ;; Don't highlight command output.  Mostly useful if
+                 ;; `comint-fl-mode' is disabled.
+                 ((text-property-any beg (point) 'field 'output)
+                  nil)
+                 ((member cmd shell-highlight-undef-aliases)
+                  'shell-highlight-undef-alias-face)
+                 ;; Check if it contains a directory separator
+                 ((file-name-directory cmd)
+                  (when (file-name-absolute-p cmd)
+                    (setq cmd (concat
+                               (or (bound-and-true-p comint-file-name-pref=
ix)
+                                   (file-remote-p default-directory))
+                               cmd)))
+                  (if (or (file-executable-p cmd)
+                          (file-directory-p cmd))
+                      'shell-highlight-undef-defined-face
+                    'shell-highlight-undef-undefined-face))
+                 ((shell--highlight-undef-executable-find cmd)
+                  'shell-highlight-undef-defined-face)
+                 (t 'shell-highlight-undef-undefined-face))))))
+    t))
+
+(defvar-local shell--highlight-undef-indirect nil
+  "t if shell commands are fontified in `comint-indirect-buffer'.")
+
+(declare-function sh-feature "sh-script" (alist &optional function))
+(defvar sh-leading-keywords)
+(defvar sh-other-keywords)
+
+(define-minor-mode shell-highlight-undef-mode
+  "Highlight undefined shell commands and aliases.
+This minor mode is mostly useful in `shell-mode' buffers and
+works better if `comint-fl-mode' is enabled."
+  :init-value nil
+  (if shell--highlight-undef-indirect
+      (progn
+        (remove-hook 'comint-indirect-setup-hook shell--highlight-undef-in=
direct t)
+        (setq shell--highlight-undef-indirect nil)
+        (when-let ((buf (comint-indirect-buffer t)))
+          (with-current-buffer buf
+            (font-lock-remove-keywords nil shell-highlight-undef-keywords)=
)))
+    (font-lock-remove-keywords nil shell-highlight-undef-keywords))
+  (remove-hook 'comint-fl-mode-hook
+               #'shell-highlight-undef-reset-mode t)
+
+  (when shell-highlight-undef-mode
+    (when comint-use-prompt-regexp
+      (setq shell-highlight-undef-mode nil)
+      (error
+       "`shell-highlight-undef-mode' is incompatible with `comint-use-prom=
pt-regexp'"))
+
+    (require 'sh-script)
+
+    (let* ((regexp
+            ;; Adapted from `sh-font-lock-keywords-1'
+            (concat
+             "\\("
+             "[;(){}`|&]"
+             (if comint-fl-mode
+                 ;; `comint-fl-mode' already puts point-min on end of
+                 ;; prompt
+                 ""
+               (concat "\\|" comint-prompt-regexp))
+             "\\|^"
+             "\\)"
+             "[ \t]*\\(\\("
+             (regexp-opt (sh-feature sh-leading-keywords) t)
+             "[ \t]+\\)?"
+             (regexp-opt (append (sh-feature sh-leading-keywords)
+                                 (sh-feature sh-other-keywords))
+                         t)
+             "[ \t]+\\)?\\_<\\(\\(?:\\s_\\|\\sw\\|/\\)+\\)\\_>"))
+           (setup
+            (lambda ()
+              (setq shell-highlight-undef-regexp regexp)
+              (font-lock-add-keywords nil shell-highlight-undef-keywords t=
))))
+      (cond (comint-fl-mode
+             (setq shell--highlight-undef-indirect setup)
+             (if-let ((buf (comint-indirect-buffer t)))
+                 (with-current-buffer buf
+                   (funcall setup))
+               (add-hook 'comint-indirect-setup-hook setup nil t)))
+            (t (funcall setup))))
+
+    (add-hook 'comint-fl-mode-hook
+              #'shell-highlight-undef-reset-mode nil t))
+
+  (font-lock-flush))
+
+(defun shell-highlight-undef-reset-mode ()
+  "If `shell-highlight-undef-mode' is on, turn it off and on."
+  (when shell-highlight-undef-mode
+    (shell-highlight-undef-mode 1)))
+
 (provide 'shell)
=20
 ;;; shell.el ends here
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0005-Input-indentation-for-M-x-shell.patch
Content-Transfer-Encoding: quoted-printable

From=2068c23c6e154e9386cb95e1e4737dc4ea980663c4 Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:37:36 +0100
Subject: [PATCH 5/5] 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.
=2D--
 lisp/comint.el | 93 +++++++++++++++++++++++++++++++++++++++++++++-----
 lisp/shell.el  |  4 +++
 2 files changed, 89 insertions(+), 8 deletions(-)

diff --git a/lisp/comint.el b/lisp/comint.el
index dbbd687e18..23ec9cbabb 100644
=2D-- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -4043,21 +4043,21 @@ comint-osc-hyperlink-handler
              (cons (point-marker) (match-string-no-properties 1 text)))))
=20
 
=2D;;; Input fontification through an indirect buffer
+;;; Input fontification and indentation through an indirect buffer
 ;;=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
 ;;
=2D;; Modes derived from `comint-mode' can set up fontification input
=2D;; text with the help of an indirect buffer whose major mode and
=2D;; 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.
=20
 (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
=2Da major-mode command whose font-locking is desired for input
=2Dtext.  In order to prevent possible mode hooks from running, the
=2Dvariable `delay-mode-hooks' is set to t prior to calling this
=2Dfunction 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.")
=20
 (defcustom comint-indirect-setup-hook nil
@@ -4222,6 +4222,83 @@ comint--intersect-regions
     (when return-beg
       (cons (car return-beg) (car return-end)))))
=20
+(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 (=3D 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 (=3D 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,
diff --git a/lisp/shell.el b/lisp/shell.el
index 71a37c37ca..3b1244fa8d 100644
=2D-- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -639,6 +639,10 @@ shell-mode
                  (message-log-max nil))
               (sh-mode)))))
=20
+  (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))
=2D-=20
2.34.0


--=-=-=--

--==-=-=
Content-Type: application/pgp-signature; name="signature.asc"

-----BEGIN PGP SIGNATURE-----

iQJHBAEBCAAxFiEEmxVnesoT5rQXvVXnswkaGpIVmT8FAmGjdAsTHG1paGFAa2Ft
bml0bmlrLnRvcAAKCRCzCRoakhWZP3YoD/4tNFN/7IrtjRxEWs8EMi0Blj2g57Dl
lnHV24ev3guk0rA1oJCJpUVpnlyY3pscQ4kwlfPSkqy9IfdbE6jWjlmURxAkxXUr
SqpKFYVox+JZjVd71T/3NGYbNlsbrRM75g3vXE+q6MFXVcVVsduAHQ1BgW4EEbQg
47/JDeBUU8OlLt170F0TcsAnjm+uq35Yp5zYOyki5ykHaqNFzAK+A2KO99758u5V
6MjmLRFZ9IiFAJpqicGZ5j2F6PCthg4YXfZXYlWcw0JBcAzfzBrQgbDUa7u+2Khg
AEDnTSd9MesGeIre+IAuRzemc1DEY8/QlftJeWgkLEA9w0rUhB6tPo+fTfnxgRaq
QXpv9/TIhnRys9YiETS0Do0Nqc0XACgVcmKebBV9zfip1UsxsOBjCb2UxfNKkJLw
rAISqcLF+Kmlvy1eos3Hib3ENpJreAGew/3573kmfo1h85Z5Pf4qo/vBNUK8lYaI
Bpe1vXQRLYk86x1X+dKw5UcAaMDKKBldtOTfxaQLGqa/uUwHFWdtbrX5hGSCLYJj
Gk6tR+h4jfGIPM67sQ1PKl+jUXvwpbKqtnxvK4XdH1raQZygY6ki6A/JoFKw8V1n
iNBmmWf3EIBy2wMB2fymsFBPnXlzlrYgLccB4saO1opA5NNDLgYO76hT2S7BxluH
26VYBeC6GqiV/w==
=exM+
-----END PGP SIGNATURE-----
--==-=-=--




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#51940; Package emacs. Full text available.

Message received at 51940 <at> debbugs.gnu.org:


Received: (at 51940) by debbugs.gnu.org; 25 Nov 2021 10:42:49 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Thu Nov 25 05:42:49 2021
Received: from localhost ([127.0.0.1]:55883 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1mqCDJ-0000tZ-Eg
	for submit <at> debbugs.gnu.org; Thu, 25 Nov 2021 05:42:49 -0500
Received: from eggs.gnu.org ([209.51.188.92]:44552)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <eliz@HIDDEN>) id 1mqCDH-0000tN-Q0
 for 51940 <at> debbugs.gnu.org; Thu, 25 Nov 2021 05:42:48 -0500
Received: from [2001:470:142:3::e] (port=36058 helo=fencepost.gnu.org)
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <eliz@HIDDEN>)
 id 1mqCDC-0000zA-5R; Thu, 25 Nov 2021 05:42:42 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date:
 mime-version; bh=9jhR0niW83PxNtOZeTDXIYWWa4O31T9SXWJZYfoLf9U=; b=TbM1kv1vP5DY
 cV9iPbPfeDxWXfTssrjNnNUW32RwcbEqr0EZAViv3/TF+ct/U8TwDvOM92grxNk1KOYfRnOYb+beA
 +NiUveR0FRbScFBKThxDfnCo4T1viJK3FD0qqY5LOiMmC3GsNvgMuJsBo6mQPfE9zb2JXLe6ROi/M
 hdwmOJuBt+jN9xFbXja1GvCn6SF2orDTMHaiCrDYZowQiOZjULoSTACalbvYxH8qGhRME7lCnUW4E
 Y2F323Y1fTsrGmcsLIn+3t21CyKa9YHqO1TnWnPc5q+ZijpSRFQJm1EHpcApPorfR+3fkmMquzSzJ
 OhtfhEAcN3A3sUPYVAoNVg==;
Received: from [87.69.77.57] (port=4738 helo=home-c4e4a596f7)
 by fencepost.gnu.org with esmtpsa (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <eliz@HIDDEN>)
 id 1mqCDB-0002zg-EK; Thu, 25 Nov 2021 05:42:41 -0500
Date: Thu, 25 Nov 2021 12:42:42 +0200
Message-Id: <8335nk7bz1.fsf@HIDDEN>
From: Eli Zaretskii <eliz@HIDDEN>
To: <miha@HIDDEN>
In-Reply-To: <87pmqxeghn.fsf@miha-pc>
Subject: Re: bug#51940: 29.0.50; [PATCH] Fontification and indentation in
 M-x shell and ielm
References: <87sfvteqx2.fsf@miha-pc> <83ee7dyar2.fsf@HIDDEN>
 <87pmqxeghn.fsf@miha-pc>
X-Spam-Score: -0.3 (/)
X-Debbugs-Envelope-To: 51940
Cc: 51940 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.3 (-)

> From: <miha@HIDDEN>
> Cc: 51940 <at> debbugs.gnu.org
> Date: Thu, 18 Nov 2021 14:32:52 +0100
> 
> Eli Zaretskii <eliz@HIDDEN> writes:
> 
> >> Date: Thu, 18 Nov 2021 10:47:37 +0100
> >> From: miha--- via "Bug reports for GNU Emacs,
> >>  the Swiss army knife of text editors" <bug-gnu-emacs@HIDDEN>
> >> 
> >> Please find the attached patches which enable fontification and
> >> indentation in M-x shell and ielm. The main fontification and
> >> indentation machinery is implemented in comint.el and should be general
> >> enough for use by other comint derived modes. It works by making an
> >> indirect buffer, putting it in a suitable major mode (sh-mode in
> >> M-x shell buffers, for example) and fontifying and indenting user input
> >> that buffer.
> >
> > Thanks.
> >
> > One general comment about the doc strings:
> >
> >> +(defvar-local comint-indirect-setup-function nil
> >> +  "Function to set up an indirect buffer.
> >
> > here and elsewhere in the changes, please modify the first sentence of
> > the doc strings so that they don't sound too general.  "Set up an
> > indirect buffer" doesn't hint on the fact that this is for comint
> > buffers, not in general about indirect buffers.  The same problem
> > exists with many doc strings in these patches.
> 
> Okay, attached are revised patches with improved doc strings and a fixed
> commit message.

The doc strings are still not specific enough, sorry for being unclear
in what I meant.  Let's take an example:

> +(define-minor-mode comint-fl-mode
> +  "Enable input fontification.

The first line should say something like

  Enable input fontification in comint buffers.

> +(defun comint--fl-off ()
> +  "Disable input fontification for the current buffer."

Similarly here:

  Disable input fontification in the current comint buffer.

> +(defun comint--fl-fontify-region (fun beg end verbose)
> +  "Around advice for `font-lock-fontify-region-function'.

Likewise here: mention comint in the first line.

> +(defcustom shell-highlight-undef-aliases nil
> +  "List of commands to highlight as a command alias."

Here, the doc string should mention shell command aliases, otherwise
it's too general ("command alias" doesn't necessarily imply a shell).

Please go over the doc strings with this criterion in mind, and see
what else needs to be fixed in the same way.

Also, would people who use these modes a lot please try the feature
and provide feedback?

Thanks.




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#51940; Package emacs. Full text available.

Message received at 51940 <at> debbugs.gnu.org:


Received: (at 51940) by debbugs.gnu.org; 18 Nov 2021 13:28:26 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Thu Nov 18 08:28:26 2021
Received: from localhost ([127.0.0.1]:35133 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1mnhSf-0003Jv-Em
	for submit <at> debbugs.gnu.org; Thu, 18 Nov 2021 08:28:25 -0500
Received: from kamnitnik.top ([209.250.245.214]:34990)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <miha@HIDDEN>) id 1mnhSb-0003Jl-Sb
 for 51940 <at> debbugs.gnu.org; Thu, 18 Nov 2021 08:28:20 -0500
Received: from localhost (BSN-77-156-43.static.siol.net [193.77.156.43])
 by kamnitnik.top (Postfix) with ESMTPSA id A5FE39CFA8;
 Thu, 18 Nov 2021 13:28:16 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kamnitnik.top;
 s=mail; t=1637242096;
 bh=jrT5foErKA4+fNEHRxDOxJxlsi7byTgCvoy5CT4Y/I8=;
 h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
 b=FDoo1mPg1u+Zof17D3CxnbwS7C2cFOHpo+2ZHpFy5TYgd0xiYKGtfjUctj8LAy6xv
 NbOKQb4EPYRHwTI6b8JXtCP0VboYTdNxBZBj24fJJJgNvfm8iLU5TuVa5zjGYOtL3k
 PWmBPeeFOVkmM2TeQodg/EZlI3fgKpoKxhlI+sIDPUusSd2ZeigZ6Sy9QptXuiDIRh
 BmTikaFK8RdNvlBg4GAPCOYTY14pmHb/xtsQVnl+8ePt+nifyq0ginF4EvUSw9fGKl
 J16EGIaBOA7Lk3ZE28FuhAg8A22mHJxEl9tfamXGcOiTkp8Ke2f0NQqXXwUwic+aLi
 8jZyP/uk2QGgA==
From: <miha@HIDDEN>
To: Eli Zaretskii <eliz@HIDDEN>
Subject: Re: bug#51940: 29.0.50; [PATCH] Fontification and indentation in
 M-x shell and ielm
In-Reply-To: <83ee7dyar2.fsf@HIDDEN>
References: <87sfvteqx2.fsf@miha-pc> <83ee7dyar2.fsf@HIDDEN>
Date: Thu, 18 Nov 2021 14:32:52 +0100
Message-ID: <87pmqxeghn.fsf@miha-pc>
MIME-Version: 1.0
Content-Type: multipart/signed; boundary="==-=-=";
 micalg=pgp-sha256; protocol="application/pgp-signature"
X-Spam-Score: 2.5 (++)
X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 Content preview:  Eli Zaretskii <eliz@HIDDEN> writes: >> Date: Thu, 18 Nov
 2021 10:47:37 +0100 >> From: miha--- via "Bug reports for GNU Emacs, >> the
 Swiss army knife of text editors" <bug-gnu-emacs@HIDDEN> >> >> Please find
 the attached patches which [...] 
 Content analysis details:   (2.5 points, 10.0 required)
 pts rule name              description
 ---- ---------------------- --------------------------------------------------
 -0.0 SPF_PASS               SPF: sender matches SPF record
 2.0 PDS_OTHER_BAD_TLD      Untrustworthy TLDs
 [URI: kamnitnik.top (top)]
 -0.0 SPF_HELO_PASS          SPF: HELO matches SPF record
 0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
X-Debbugs-Envelope-To: 51940
Cc: 51940 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: 2.5 (++)
X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 
 Content preview:  Eli Zaretskii <eliz@HIDDEN> writes: >> Date: Thu, 18 Nov
   2021 10:47:37 +0100 >> From: miha--- via "Bug reports for GNU Emacs, >> the
    Swiss army knife of text editors" <bug-gnu-emacs@HIDDEN> >> >> Please find
    the attached patches which [...] 
 
 Content analysis details:   (2.5 points, 10.0 required)
 
  pts rule name              description
 ---- ---------------------- --------------------------------------------------
  2.0 PDS_OTHER_BAD_TLD      Untrustworthy TLDs
                             [URI: kamnitnik.top (top)]
  0.0 T_SPF_TEMPERROR        SPF: test of record failed (temperror)
 -0.0 SPF_HELO_PASS          SPF: HELO matches SPF record
  0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
  1.0 BULK_RE_SUSP_NTLD      Precedence bulk and RE: from a suspicious TLD
 -1.0 MAILING_LIST_MULTI     Multiple indicators imply a widely-seen list
                             manager

--==-=-=
Content-Type: multipart/mixed; boundary="=-=-="

--=-=-=
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

Eli Zaretskii <eliz@HIDDEN> writes:

>> Date: Thu, 18 Nov 2021 10:47:37 +0100
>> From: miha--- via "Bug reports for GNU Emacs,
>>  the Swiss army knife of text editors" <bug-gnu-emacs@HIDDEN>
>>=20
>> Please find the attached patches which enable fontification and
>> indentation in M-x shell and ielm. The main fontification and
>> indentation machinery is implemented in comint.el and should be general
>> enough for use by other comint derived modes. It works by making an
>> indirect buffer, putting it in a suitable major mode (sh-mode in
>> M-x shell buffers, for example) and fontifying and indenting user input
>> that buffer.
>
> Thanks.
>
> One general comment about the doc strings:
>
>> +(defvar-local comint-indirect-setup-function nil
>> +  "Function to set up an indirect buffer.
>
> here and elsewhere in the changes, please modify the first sentence of
> the doc strings so that they don't sound too general.  "Set up an
> indirect buffer" doesn't hint on the fact that this is for comint
> buffers, not in general about indirect buffers.  The same problem
> exists with many doc strings in these patches.

Okay, attached are revised patches with improved doc strings and a fixed
commit message.


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0001-Implement-a-general-input-fontification-mechanism-fo.patch
Content-Transfer-Encoding: quoted-printable

From=206b5a52475642b707c7f0cc0f871f6540d8a0aa73 Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 22:51:51 +0100
Subject: [PATCH 1/5] Implement a general input fontification mechanism for
 comint modes

* lisp/comint.el
(comint-indirect-setup-function): New variable for comint derived
major modes to customize.
(comint-fl-mode): New minor mode that fontifies input text through an
indirect buffer.
(comint-indirect-setup-hook):
(comint--indirect-buffer):
(comint--fl-saved-jit-lock-contextually):
(comint--fl-on):
(comint--fl-off):
(comint--fl-ppss-flush-indirect):
(comint--fl-fontify-region):
(comint--intersect-regions):
(comint-indirect-buffer):
(comint--indirect-cleanup): New functions and buffer-local variables.
=2D--
 lisp/comint.el | 223 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 223 insertions(+)

diff --git a/lisp/comint.el b/lisp/comint.el
index 544f0b8b82..7709178afd 100644
=2D-- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -1931,6 +1931,7 @@ comint-send-input
               (when comint-highlight-input
                 (add-text-properties beg end
                                      '( font-lock-face comint-highlight-in=
put
+                                        comint--fl-inhibit-fontification t
                                         front-sticky t )))
               (unless comint-use-prompt-regexp
                 ;; Give old user input a field property of `input', to
@@ -4042,6 +4043,228 @@ comint-osc-hyperlink-handler
              (cons (point-marker) (match-string-no-properties 1 text)))))
=20
 
+;;; Input fontification through an indirect buffer
+;;=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
+;;
+;; 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.
+
+(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
+`after-change-major-mode-hook' are bound to nil.")
+
+(defcustom comint-indirect-setup-hook nil
+  "Hook run after setting up an indirect comint fontification buffer.
+It is run after the indirect buffer is set up for fontification
+of input regions."
+  :group 'comint
+  :type 'hook
+  :version "29.1")
+
+(defvar-local comint--indirect-buffer nil
+  "Indirect buffer used for input fontification.")
+
+(defvar-local comint--fl-saved-jit-lock-contextually nil)
+
+(define-minor-mode comint-fl-mode
+  "Enable input fontification.
+Input fontification happens through an indirect buffer created
+with `comint-indirect-buffer', which see.
+
+This function signals an error if `comint-use-prompt-regexp' is
+non-nil because input fontification isn't compatible with this
+setting."
+  :lighter nil
+  (if comint-fl-mode
+      (let ((success nil))
+        (unwind-protect
+            (progn
+              (comint--fl-on)
+              (setq success t))
+          (unless success
+            (setq comint-fl-mode nil)
+            (comint--fl-off))))
+    (comint--fl-off)))
+
+(defun comint--fl-on ()
+  "Enable input fontification for the current buffer."
+  (comint--fl-off)
+
+  (when comint-use-prompt-regexp
+    (error
+     "Input fontification is incompatible with `comint-use-prompt-regexp'"=
))
+
+  (add-function :around (local 'font-lock-fontify-region-function)
+                #'comint--fl-fontify-region)
+  ;; `before-change-functions' are only run in the current buffer and
+  ;; not in its indirect buffers, which means that we must manually
+  ;; flush ppss cache
+  (add-hook 'before-change-functions
+            #'comint--fl-ppss-flush-indirect 99 t)
+
+  ;; Set up contextual fontification
+  (unless (booleanp jit-lock-contextually)
+    (setq comint--fl-saved-jit-lock-contextually
+          jit-lock-contextually)
+    (setq-local jit-lock-contextually t)
+    (when jit-lock-mode
+      (jit-lock-mode t))))
+
+(defun comint--fl-off ()
+  "Disable input fontification for the current buffer."
+  (remove-function (local 'font-lock-fontify-region-function)
+                   #'comint--fl-fontify-region)
+  (remove-hook 'before-change-functions
+               #'comint--fl-ppss-flush-indirect t)
+
+  ;; Reset contextual fontification
+  (when comint--fl-saved-jit-lock-contextually
+    (setq-local jit-lock-contextually
+                comint--fl-saved-jit-lock-contextually)
+    (setq comint--fl-saved-jit-lock-contextually nil)
+    (when jit-lock-mode
+      (jit-lock-mode t)))
+
+  (font-lock-flush))
+
+(defun comint--fl-ppss-flush-indirect (beg &rest rest)
+  (when-let ((buf (comint-indirect-buffer t)))
+    (with-current-buffer buf
+      (when (memq #'syntax-ppss-flush-cache before-change-functions)
+        (apply #'syntax-ppss-flush-cache beg rest)))))
+
+(defun comint--fl-fontify-region (fun beg end verbose)
+  "Around advice for `font-lock-fontify-region-function'.
+Fontify region between BEG and END.  First, highlight it using
+FUN.  Then highlight only the input text in the region with the
+help of an indirect buffer.  VERBOSE is passed to the
+fontify-region functions.  Skip fontification of input regions
+with non-nil `comint--fl-inhibit-fontification' text property."
+  (pcase (funcall fun beg end verbose)
+    (`(jit-lock-bounds ,beg1 . ,end1)
+     (setq beg beg1 end end1)))
+  (pcase
+      (let ((min (point-min))
+            (max (point-max)))
+        (with-current-buffer (comint-indirect-buffer)
+          (narrow-to-region min max)
+          (comint--intersect-regions
+           nil (lambda (beg end)
+                 (unless (get-text-property
+                          beg 'comint--fl-inhibit-fontification)
+                   (font-lock-fontify-region beg end verbose)))
+           beg end)))
+    (`((jit-lock-bounds ,beg1 . ,_) . (jit-lock-bounds ,_ . ,end1))
+     (setq beg (min beg beg1))
+     (setq end (max end end1))))
+
+  `(jit-lock-bounds ,beg . ,end))
+
+(defun comint--intersect-regions (fun-output fun-input beg end)
+  "Iterate over output and input regions between BEG and END.
+Divide the region specified by BEG and END into smaller regions
+that cover either process output (its 'field property is 'output)
+or input (all remaining text).  Interchangeably call FUN-OUTPUT on
+each output region, and FUN-INPUT on each input region.
+
+FUN-OUTPUT and FUN-INPUT are passed two arguments, the beginning
+and end of the smaller region.  Before calling each function,
+narrow the buffer to the surrounding process output or input.  You
+can also pass nil as either function to skip its corresponding
+regions.
+
+Return a cons cell of return values of the first and last
+function called, or nil, if no function was called (if BEG =3D END)."
+  (let ((beg1 beg)
+        (end1 (copy-marker nil t))
+        (return-beg nil) (return-end nil)
+        (is-output (eq (get-text-property beg 'field) 'output)))
+    (setq end (copy-marker end t))
+
+    (while (< beg1 end)
+      (set-marker
+       end1 (or (if is-output
+                    (text-property-not-all beg1 end 'field 'output)
+                  (text-property-any beg1 end 'field 'output))
+                end))
+      (when-let ((fun (if is-output fun-output fun-input)))
+        (save-restriction
+          (let ((beg2 beg1)
+                (end2 end1))
+            (when (=3D beg2 beg)
+              (setq beg2 (field-beginning beg2)))
+            (when (=3D end2 end)
+              (setq end2 (field-end end2)))
+            ;; Narrow to the whole field surrounding the region
+            (narrow-to-region beg2 end2))
+          (setq return-end (list (funcall fun beg1
+                                          (marker-position end1)))))
+        (unless return-beg
+          (setq return-beg return-end)))
+      (setq beg1 (marker-position end1))
+      (setq is-output (not is-output)))
+
+    (set-marker end nil)
+    (set-marker end1 nil)
+    (when return-beg
+      (cons (car return-beg) (car return-end)))))
+
+(defun comint-indirect-buffer (&optional no-create)
+  "Return an indirect comint fontification buffer.
+If an indirect buffer for the current buffer already exists,
+return it, otherwise create it first and set it up by calling
+`comint-indirect-setup-function' with zero arguments, turning on
+font-lock, and running `comint-indirect-setup-hook'.  This setup
+happens with `delay-mode-hooks' set to t in order to prevent
+possible SETUP-FUN's mode hooks from running.
+
+If an indirect buffer doesn't exist and NO-CREATE is non-nil,
+return nil."
+  (or
+   comint--indirect-buffer
+   (unless no-create
+     (let ((setup-hook
+            (if (local-variable-p 'comint-indirect-setup-hook)
+                (list comint-indirect-setup-hook)))
+           (setup-fun comint-indirect-setup-function))
+
+       (add-hook 'change-major-mode-hook #'comint--indirect-cleanup
+                 nil t)
+
+       (with-current-buffer
+           (setq comint--indirect-buffer
+                 (make-indirect-buffer
+                  (current-buffer)
+                  (generate-new-buffer-name
+                   (concat " " (buffer-name) "-comint-indirect"))))
+         (setq-local delay-mode-hooks t)
+         (when setup-fun
+           (let ((change-major-mode-hook nil)
+                 (after-change-major-mode-hook nil))
+             (funcall setup-fun)))
+         (setq-local font-lock-dont-widen t)
+         (setq-local font-lock-support-mode nil)
+         (font-lock-mode)
+         (when setup-hook
+           (setq-local comint-indirect-setup-hook
+                       (car setup-hook)))
+         (run-hooks 'comint-indirect-setup-hook))
+       comint--indirect-buffer))))
+
+(defun comint--indirect-cleanup ()
+  (when comint--indirect-buffer
+    (kill-buffer comint--indirect-buffer)
+    (setq comint--indirect-buffer nil)))
+
+
+
 ;;; Converting process modes to use comint mode
 ;;=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
 ;; The code in the Emacs 19 distribution has all been modified to use comi=
nt
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0002-Input-fontification-for-M-x-shell.patch
Content-Transfer-Encoding: quoted-printable

From=20960dffce408027fd2e20f3b9f9c7e4a35c860833 Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:13:03 +0100
Subject: [PATCH 2/5] Input fontification for M-x shell

* lisp/shell.el (shell-comint-fl-enable): New user option to control
input fontification.
(shell-indirect-setup-hook): New hook.
(shell-mode): Set up and enable input fontification.
=2D--
 lisp/shell.el | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/lisp/shell.el b/lisp/shell.el
index 370532ea46..a91a59f070 100644
=2D-- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -306,6 +306,22 @@ shell-input-autoexpand
 		 (const :tag "on" t))
   :group 'shell)
=20
+(defcustom shell-comint-fl-enable t
+  "Enable highlighting of input in shell buffers.
+This variable only has effect when the shell is started.  Use the
+command `comint-fl-mode' to toggle highlighting of input."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom shell-indirect-setup-hook nil
+  "Hook run after setting up an indirect shell fontification buffer."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
 (defvar shell-dirstack nil
   "List of directories saved by pushd in this buffer's shell.
 Thus, this does not include the shell's current directory.")
@@ -516,6 +532,8 @@ shell-completion-vars
=20
 (put 'shell-mode 'mode-class 'special)
=20
+(defvar sh-shell-file)
+
 (define-derived-mode shell-mode comint-mode "Shell"
   "Major mode for interacting with an inferior shell.
 \\<shell-mode-map>
@@ -572,6 +590,11 @@ shell-mode
 control whether input and output cause the window to scroll to the end of =
the
 buffer."
   :interactive nil
+  :after-hook
+  (and (null comint-use-prompt-regexp)
+       shell-comint-fl-enable
+       (comint-fl-mode))
+
   (setq comint-prompt-regexp shell-prompt-pattern)
   (shell-completion-vars)
   (setq-local paragraph-separate "\\'")
@@ -591,6 +614,19 @@ shell-mode
   (setq-local ansi-color-apply-face-function #'shell-apply-ansi-color)
   (shell-reapply-ansi-color)
=20
+  (add-hook 'comint-indirect-setup-hook
+            #'shell-indirect-setup-hook 'append t)
+  (setq comint-indirect-setup-function
+        (let ((shell shell--start-prog))
+          (lambda ()
+            (require 'sh-script)
+            (cl-letf
+                (((default-value 'sh-shell-file)
+                  (or shell sh-shell-file))
+                 (inhibit-message t)
+                 (message-log-max nil))
+              (sh-mode)))))
+
   ;; 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))
@@ -645,6 +681,10 @@ shell-mode
                     ": [[:digit:]]+:[[:digit:]]+;")))
     (comint-read-input-ring t)))
=20
+(defun shell-indirect-setup-hook ()
+  "Run `shell-indirect-setup-hook'."
+  (run-hooks 'shell-indirect-setup-hook))
+
 (defun shell-apply-ansi-color (beg end face)
   "Apply FACE as the ansi-color face for the text between BEG and END."
   (when face
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0003-Input-fontification-for-M-x-ielm.patch
Content-Transfer-Encoding: quoted-printable

From=202ec0286b5697bfc469afcc8e915e2c090b06f5fb Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:16:23 +0100
Subject: [PATCH 3/5] Input fontification for M-x ielm

* lisp/ielm.el (ielm-comint-fl-enable): New user option to control
input fontification.
(ielm-indirect-setup-hook): New hook.
(inferior-emacs-lisp-mode): Set up and enable input fontification.
=2D--
 lisp/ielm.el | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/lisp/ielm.el b/lisp/ielm.el
index 39820a893a..d792bd871d 100644
=2D-- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -472,6 +472,26 @@ ielm-set-pm
   ;; Set the process mark in the current buffer to POS.
   (set-marker (process-mark (get-buffer-process (current-buffer))) pos))
=20
+;;; Input fontification
+
+(defcustom ielm-comint-fl-enable t
+  "Enable highlighting of input in ielm buffers.
+This variable only has effect when the shell is started.  Use the
+command `comint-fl-mode' to toggle highlighting of input."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom ielm-indirect-setup-hook nil
+  "Hook run after setting up an indirect ielm fontification buffer."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
+(defun ielm-indirect-setup-hook ()
+  "Run `ielm-indirect-setup-hook'."
+  (run-hooks 'ielm-indirect-setup-hook))
+
 ;;; Major mode
=20
 (define-derived-mode inferior-emacs-lisp-mode comint-mode "IELM"
@@ -526,6 +546,10 @@ inferior-emacs-lisp-mode
 Customized bindings may be defined in `ielm-map', which currently contains:
 \\{ielm-map}"
   :syntax-table emacs-lisp-mode-syntax-table
+  :after-hook
+  (and (null comint-use-prompt-regexp)
+       ielm-comint-fl-enable
+       (comint-fl-mode))
=20
   (setq comint-prompt-regexp (concat "^" (regexp-quote ielm-prompt)))
   (setq-local paragraph-separate "\\'")
@@ -564,6 +588,10 @@ inferior-emacs-lisp-mode
   (setq-local font-lock-defaults
        '(ielm-font-lock-keywords nil nil ((?: . "w") (?- . "w") (?* . "w")=
)))
=20
+  (add-hook 'comint-indirect-setup-hook
+            #'ielm-indirect-setup-hook 'append t)
+  (setq comint-indirect-setup-function #'emacs-lisp-mode)
+
   ;; A dummy process to keep comint happy. It will never get any input
   (unless (comint-check-proc (current-buffer))
     ;; Was cat, but on non-Unix platforms that might not exist, so
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0004-Highlight-non-existent-commands-in-M-x-shell.patch
Content-Transfer-Encoding: quoted-printable

From=20a30796d8bafd3cea96f58339a4258ad25890f683 Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:21:03 +0100
Subject: [PATCH 4/5] Highlight non-existent commands in M-x shell

* lisp/shell.el (shell-mode): Enable highlighting of non-existent
commands if requested.
(shell-highlight-undef-aliases):
(shell-highlight-undef-remote-file-name-inhibit-cache): New user
options.
(shell-highlight-undef-mode): New minor mode.
(shell-highlight-undef-defined-face):
(shell-highlight-undef-undefined-face):
(shell-highlight-undef-alias-face): New faces.
(shell-highlight-undef--exec-cache):
(shell-highlight-undef--face):
(shell-highlight-undef-keywords):
(shell-highlight-undef-regexp):
(shell-highlight-undef--executable-find):
(shell-highlight-undef-matcher):
(shell-highlight-undef--indirect):
(shell-highlight--setup):
(shell-highlight-undef-reset-mode): New functions and buffer local
variables.
=2D--
 lisp/shell.el | 227 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 224 insertions(+), 3 deletions(-)

diff --git a/lisp/shell.el b/lisp/shell.el
index a91a59f070..274716f68a 100644
=2D-- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -322,6 +322,16 @@ shell-indirect-setup-hook
   :safe 'booleanp
   :version "29.1")
=20
+(defcustom shell-highlight-undef-enable nil
+  "Enable highlighting of undefined commands in shell buffers.
+This variable only has effect when the shell is started.  Use the
+command `shell-highlight-undef-mode' to toggle highlighting of
+undefined commands."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
 (defvar shell-dirstack nil
   "List of directories saved by pushd in this buffer's shell.
 Thus, this does not include the shell's current directory.")
@@ -591,9 +601,11 @@ shell-mode
 buffer."
   :interactive nil
   :after-hook
=2D  (and (null comint-use-prompt-regexp)
=2D       shell-comint-fl-enable
=2D       (comint-fl-mode))
+  (unless comint-use-prompt-regexp
+    (if shell-comint-fl-enable
+        (comint-fl-mode))
+    (if shell-highlight-undef-enable
+        (shell-highlight-undef-mode)))
=20
   (setq comint-prompt-regexp shell-prompt-pattern)
   (shell-completion-vars)
@@ -1471,6 +1483,215 @@ shell-narrow-to-prompt
            (point-max)
          (shell--prompt-begin-position))))))
=20
+;;; Highlight undefined commands
+;;
+;; To highlight non-existent commands, customize
+;; `shell-highlight-undef-enable' to t.  To highlight some commands as
+;; aliases, add them to `shell-highlight-undef-aliases'.
+
+(defcustom shell-highlight-undef-aliases nil
+  "List of commands to highlight as a command alias."
+  :group 'shell
+  :type '(repeat string)
+  :version "29.1")
+
+(defface shell-highlight-undef-defined-face
+  '((t :inherit 'font-lock-function-name-face))
+  "Face used for existent commands."
+  :group 'shell
+  :version "29.1")
+
+(defface shell-highlight-undef-undefined-face
+  '((t :inherit 'font-lock-warning-face))
+  "Face used for non-existent commands."
+  :group 'shell
+  :version "29.1")
+
+(defface shell-highlight-undef-alias-face
+  '((t :inherit 'font-lock-variable-name-face))
+  "Face used for command aliases."
+  :group 'shell
+  :version "29.1")
+
+(defcustom shell-highlight-undef-remote-file-name-inhibit-cache nil
+  "Whether to use cache for finding remote executables.
+See `remote-file-name-inhibit-cache' for description."
+  :group 'faces
+  :type '(choice
+          (const :tag "Do not inhibit file name cache" nil)
+          (const :tag "Do not use file name cache" t)
+          (integer :tag "Do not use file name cache"
+                   :format "Do not use file name cache older than %v secon=
ds"
+                   :value 10))
+  :version "29.1")
+
+(defvar shell--highlight-undef-exec-cache nil
+  "Cache of executable files found in `exec-path'.
+An alist, whose elements are of the form
+\(REMOTE TIME EXECUTABLES), where REMOTE is a string, returned by
+`file-remote-p', TIME is the return value of `float-time' end
+EXECUTABLES is a hash table with keys being the base-names of
+executable files.
+
+Cache expiry is controlled by the user option
+`remote-file-name-inhibit-cache'.")
+
+(defvar shell--highlight-undef-face 'shell-highlight-undef-defined-face)
+
+(defvar shell-highlight-undef-keywords
+  `((,#'shell-highlight-undef-matcher 6 shell--highlight-undef-face)))
+
+(defvar-local shell-highlight-undef-regexp regexp-unmatchable)
+
+(defun shell--highlight-undef-executable-find (command)
+  "Return non-nil if COMMAND is found in `exec-path'.
+Similar to `executable-find', but use cache stored in
+`shell--highlight-undef-exec-cache'."
+  (let ((remote (file-remote-p default-directory))
+        as ret found-in-cache delta-time)
+    (if (null remote)
+        (executable-find command)
+
+      (setq delta-time
+            shell-highlight-undef-remote-file-name-inhibit-cache)
+
+      (pcase (setq as (assoc remote shell--highlight-undef-exec-cache))
+        (`(,_ ,time ,hash)
+         (when (pcase delta-time
+                 ((pred numberp) (<=3D (float-time) (+ time delta-time)))
+                 ('t nil)
+                 ('nil t))
+           (setq ret (gethash command hash))
+           (setq found-in-cache t)))
+        (_ (setq as (list remote 0 (make-hash-table :test #'equal)))
+           (push as shell--highlight-undef-exec-cache)))
+
+      (if found-in-cache
+          ret
+        ;; Build cache
+        (setcar (cdr as) (float-time))
+        (let ((hash (clrhash (caddr as))))
+          (dolist (dir (exec-path))
+            (pcase-dolist (`(,f . ,attr)
+                           (condition-case nil
+                               (directory-files-and-attributes
+                                (concat remote dir) nil nil 'nosort 'integ=
er)
+                             (file-error nil)))
+              ;; Approximation.  Assume every non-directory file in $PATH =
is an
+              ;; executable.  Alternatively, we could check
+              ;; `file-executable-p', but doing so for every file in $PATH=
 is
+              ;; slow on remote machines.
+              (unless (eq t (file-attribute-type attr))
+                (puthash f t hash))))
+          (gethash command hash))))))
+
+(defun shell-highlight-undef-matcher (end)
+  "Matcher used to highlight commands up to END."
+  (when (re-search-forward shell-highlight-undef-regexp end t)
+    (save-match-data
+      (let ((cmd (match-string 6))
+            (beg (match-beginning 6)))
+        (setq shell--highlight-undef-face
+              (let* ((buf (buffer-base-buffer))
+                     (default-directory
+                      (if buf (buffer-local-value 'default-directory buf)
+                        default-directory)))
+                (cond
+                 ;; Don't highlight command output.  Mostly useful if
+                 ;; `comint-fl-mode' is disabled.
+                 ((text-property-any beg (point) 'field 'output)
+                  nil)
+                 ((member cmd shell-highlight-undef-aliases)
+                  'shell-highlight-undef-alias-face)
+                 ;; Check if it contains a directory separator
+                 ((file-name-directory cmd)
+                  (when (file-name-absolute-p cmd)
+                    (setq cmd (concat
+                               (or (bound-and-true-p comint-file-name-pref=
ix)
+                                   (file-remote-p default-directory))
+                               cmd)))
+                  (if (or (file-executable-p cmd)
+                          (file-directory-p cmd))
+                      'shell-highlight-undef-defined-face
+                    'shell-highlight-undef-undefined-face))
+                 ((shell--highlight-undef-executable-find cmd)
+                  'shell-highlight-undef-defined-face)
+                 (t 'shell-highlight-undef-undefined-face))))))
+    t))
+
+(defvar-local shell--highlight-undef-indirect nil
+  "Whether we are using a `comint-fl-mode' indirect buffer.")
+
+(declare-function sh-feature "sh-script" (alist &optional function))
+(defvar sh-leading-keywords)
+(defvar sh-other-keywords)
+
+(define-minor-mode shell-highlight-undef-mode
+  "Highlight undefined shell commands and aliases."
+  :init-value nil
+  (if shell--highlight-undef-indirect
+      (progn
+        (setq shell--highlight-undef-indirect nil)
+        (when-let ((buf (comint-indirect-buffer t)))
+          (with-current-buffer buf
+            (font-lock-remove-keywords nil shell-highlight-undef-keywords)=
)))
+    (font-lock-remove-keywords nil shell-highlight-undef-keywords))
+  (remove-hook 'comint-fl-mode-hook
+               #'shell-highlight-undef-reset-mode t)
+  (remove-hook 'comint-indirect-setup-hook #'shell--highlight-undef-setup =
t)
+
+  (when shell-highlight-undef-mode
+    (when comint-use-prompt-regexp
+      (setq shell-highlight-undef-mode nil)
+      (error
+       "`shell-highlight-undef-mode' is incompatible with `comint-use-prom=
pt-regexp'"))
+
+    (require 'sh-script)
+
+    (let ((regexp
+           ;; Adapted from `sh-font-lock-keywords-1'
+           (concat
+            "\\("
+            "[;(){}`|&]"
+            (if comint-fl-mode
+                ;; `comint-fl-mode' already puts point-min on end of
+                ;; prompt
+                ""
+              (concat "\\|" comint-prompt-regexp))
+            "\\|^"
+            "\\)"
+            "[ \t]*\\(\\("
+            (regexp-opt (sh-feature sh-leading-keywords) t)
+            "[ \t]+\\)?"
+            (regexp-opt (append (sh-feature sh-leading-keywords)
+                                (sh-feature sh-other-keywords))
+                        t)
+            "[ \t]+\\)?\\_<\\(\\(?:\\s_\\|\\sw\\|/\\)+\\)\\_>")))
+      (cond (comint-fl-mode
+             (setq shell--highlight-undef-indirect t)
+             (if-let ((buf (comint-indirect-buffer t)))
+                 (with-current-buffer buf
+                   (shell--highlight-undef-setup regexp))
+               (add-hook 'comint-indirect-setup-hook
+                         (lambda ()
+                           (shell--highlight-undef-setup regexp))
+                         nil t)))
+            (t (shell--highlight-undef-setup regexp))))
+
+    (add-hook 'comint-fl-mode-hook
+              #'shell-highlight-undef-reset-mode nil t))
+
+  (font-lock-flush))
+
+(defun shell--highlight-undef-setup (regexp)
+  (font-lock-add-keywords nil shell-highlight-undef-keywords t)
+  (setq shell-highlight-undef-regexp regexp))
+
+(defun shell-highlight-undef-reset-mode ()
+  "If `shell-highlight-undef-mode' is on, turn it off and on."
+  (when shell-highlight-undef-mode
+    (shell-highlight-undef-mode 1)))
+
 (provide 'shell)
=20
 ;;; shell.el ends here
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0005-Input-indentation-for-M-x-shell.patch
Content-Transfer-Encoding: quoted-printable

From=207fb17ea46239f514b43ac179b17f76a31bee6650 Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:37:36 +0100
Subject: [PATCH 5/5] 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.
=2D--
 lisp/comint.el | 93 +++++++++++++++++++++++++++++++++++++++++++++-----
 lisp/shell.el  |  4 +++
 2 files changed, 89 insertions(+), 8 deletions(-)

diff --git a/lisp/comint.el b/lisp/comint.el
index 7709178afd..0d115e7c70 100644
=2D-- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -4043,21 +4043,21 @@ comint-osc-hyperlink-handler
              (cons (point-marker) (match-string-no-properties 1 text)))))
=20
 
=2D;;; Input fontification through an indirect buffer
+;;; Input fontification and indentation through an indirect buffer
 ;;=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
 ;;
=2D;; Modes derived from `comint-mode' can set up fontification input
=2D;; text with the help of an indirect buffer whose major mode and
=2D;; 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.
=20
 (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
=2Da major-mode command whose font-locking is desired for input
=2Dtext.  In order to prevent possible mode hooks from running, the
=2Dvariable `delay-mode-hooks' is set to t prior to calling this
=2Dfunction 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.")
=20
 (defcustom comint-indirect-setup-hook nil
@@ -4216,6 +4216,83 @@ comint--intersect-regions
     (when return-beg
       (cons (car return-beg) (car return-end)))))
=20
+(defun comint-indent-input-line (fun)
+  "Indent current process output or input line.
+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 process output and input regions 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 (=3D 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 (=3D 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 process output or input line.
+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 process output and input regions 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,
diff --git a/lisp/shell.el b/lisp/shell.el
index 274716f68a..0662b77c55 100644
=2D-- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -639,6 +639,10 @@ shell-mode
                  (message-log-max nil))
               (sh-mode)))))
=20
+  (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))
=2D-=20
2.34.0


--=-=-=--

--==-=-=
Content-Type: application/pgp-signature; name="signature.asc"

-----BEGIN PGP SIGNATURE-----

iQJHBAEBCAAxFiEEmxVnesoT5rQXvVXnswkaGpIVmT8FAmGWVgQTHG1paGFAa2Ft
bml0bmlrLnRvcAAKCRCzCRoakhWZP4dHD/0bMHNv92LBciGx4jtGl5Awv575eKy0
RD38UsV48bzI9KK32krUlw0Otl2tpxbXvdY2E1ce/HtL4m/MNjYMLtoLYlatBW5H
2oGhrFdXvJNHjgZu8yvRc5fNAcp8PWHnHPAf3GQyccAmJgs84W62fRikX405OOlM
vCT2WWadwPErIItf94fpxKp3TTvClJ2gdW5PTcUZvBboDMO0PMibBkDHDrKyr6TU
1yO4yP+zHjU/3E5oou0BrHPGY92xALlIH/3fzH8KsVVbU+8yGuOdhHFslKPhbcgL
zI8uW0lcqGqCVyqi080OLCILrAEUJpiZypBuuWSRnxlykAjoppvUsIr4oxLlOGKz
l0xg0V2CZfEqRcbWSp7eprcvbjefR1Oib0zR7/rECySiVztIwlCwJ/l6Ry96/lEd
Jmxs3U5UGAFNixc7F3QQ7l/SNtLH8wkCNhIQUZ4kJEK0KMLVh8XHpRIYNDaMlv/H
OuD4RaXeVI678CikZJ/rhE+HHzao0g1AXBzMOXxb5zFCP58R9dZuB0oudZ+aIAV8
Rk9c+NSAtvycQlh2QsAhgzSSOeC/+7flFqpF3+a3QOmnO09PI1lYl3xvahJBr4XF
kaQ25jKJqnlEOm1CUW5Hm3U4TNSGJoWK8u09MBBMimi2ghk+vzdv3T6SyoP0AZ5S
ha6D3KG2McPUCA==
=AXCI
-----END PGP SIGNATURE-----
--==-=-=--




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#51940; Package emacs. Full text available.

Message received at 51940 <at> debbugs.gnu.org:


Received: (at 51940) by debbugs.gnu.org; 18 Nov 2021 11:16:43 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Thu Nov 18 06:16:43 2021
Received: from localhost ([127.0.0.1]:34991 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1mnfPH-0002rU-41
	for submit <at> debbugs.gnu.org; Thu, 18 Nov 2021 06:16:43 -0500
Received: from eggs.gnu.org ([209.51.188.92]:47482)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <eliz@HIDDEN>) id 1mnfPF-0002rA-Vv
 for 51940 <at> debbugs.gnu.org; Thu, 18 Nov 2021 06:16:42 -0500
Received: from [2001:470:142:3::e] (port=45068 helo=fencepost.gnu.org)
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <eliz@HIDDEN>)
 id 1mnfPA-0004I9-Fo; Thu, 18 Nov 2021 06:16:36 -0500
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date:
 mime-version; bh=Gvlr9N//trwkyOA69keHW9zoKLBzyj+K7o7vIvJk/j0=; b=dkm9/0Lmgp0B
 7v+VE+t+U+7LLc2op2EucXali8s84EJVg2xgLdIgiL5SokOYnvqvPwcQyxbCjrXtysOuZb0GEAwE7
 c6djWoMiVqqIozq7CGX9AuiDeoJeG9qQRY6IeASkdSAmB8Q0hcGdfniss4ASuLoIDrW3TFOu3mxkJ
 o+FTCffGiQMmDFHdoWOl9YB4QCC9rjEmQ4qZpOxMI/r2zJTODidXt4OfWnaN8qOy60S3S4lJ8aBLr
 eUwcTT9AAoPyFAbDCL1PqH7ArVNKtTcid1AGBE6gSX3Uyk6yVepg6JZsC7l5z5AMNCWlLl9BR+XOt
 tb/QgTA06LPgOsQWRQqsYA==;
Received: from [87.69.77.57] (port=3215 helo=home-c4e4a596f7)
 by fencepost.gnu.org with esmtpsa (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <eliz@HIDDEN>)
 id 1mnfPA-0008Ai-4F; Thu, 18 Nov 2021 06:16:36 -0500
Date: Thu, 18 Nov 2021 13:16:33 +0200
Message-Id: <83ee7dyar2.fsf@HIDDEN>
From: Eli Zaretskii <eliz@HIDDEN>
To: miha@HIDDEN
In-Reply-To: <87sfvteqx2.fsf@miha-pc> (bug-gnu-emacs@HIDDEN)
Subject: Re: bug#51940: 29.0.50;
 [PATCH] Fontification and indentation in M-x shell and ielm
References: <87sfvteqx2.fsf@miha-pc>
X-Spam-Score: -2.3 (--)
X-Debbugs-Envelope-To: 51940
Cc: 51940 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

> Date: Thu, 18 Nov 2021 10:47:37 +0100
> From: miha--- via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs@HIDDEN>
> 
> Please find the attached patches which enable fontification and
> indentation in M-x shell and ielm. The main fontification and
> indentation machinery is implemented in comint.el and should be general
> enough for use by other comint derived modes. It works by making an
> indirect buffer, putting it in a suitable major mode (sh-mode in
> M-x shell buffers, for example) and fontifying and indenting user input
> that buffer.

Thanks.

One general comment about the doc strings:

> +(defvar-local comint-indirect-setup-function nil
> +  "Function to set up an indirect buffer.

here and elsewhere in the changes, please modify the first sentence of
the doc strings so that they don't sound too general.  "Set up an
indirect buffer" doesn't hint on the fact that this is for comint
buffers, not in general about indirect buffers.  The same problem
exists with many doc strings in these patches.




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#51940; Package emacs. Full text available.

Message received at submit <at> debbugs.gnu.org:


Received: (at submit) by debbugs.gnu.org; 18 Nov 2021 09:43:14 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Thu Nov 18 04:43:14 2021
Received: from localhost ([127.0.0.1]:34790 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1mndwm-0007oA-W8
	for submit <at> debbugs.gnu.org; Thu, 18 Nov 2021 04:43:14 -0500
Received: from lists.gnu.org ([209.51.188.17]:41036)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <miha@HIDDEN>) id 1mndwk-0007nz-7U
 for submit <at> debbugs.gnu.org; Thu, 18 Nov 2021 04:43:12 -0500
Received: from eggs.gnu.org ([209.51.188.92]:51064)
 by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <miha@HIDDEN>)
 id 1mndwk-0002DJ-13
 for bug-gnu-emacs@HIDDEN; Thu, 18 Nov 2021 04:43:10 -0500
Received: from kamnitnik.top ([209.250.245.214]:60910)
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <miha@HIDDEN>)
 id 1mndwg-0006V8-Bm
 for bug-gnu-emacs@HIDDEN; Thu, 18 Nov 2021 04:43:09 -0500
Received: from localhost (BSN-77-156-43.static.siol.net [193.77.156.43])
 by kamnitnik.top (Postfix) with ESMTPSA id 2E0F79CFA8
 for <bug-gnu-emacs@HIDDEN>; Thu, 18 Nov 2021 09:43:01 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kamnitnik.top;
 s=mail; t=1637228581;
 bh=v4Urj/QOtujOfVe8dhXcgADz+Z0LTvsMKELb7fFIIys=;
 h=From:To:Subject:Date:From;
 b=vJpIpww9ppgF1G1BYxBcAFI+6MWzzuXSrFbhtywv2FNcuSdcqLJwRxbpgCkWBkvEC
 pcOxQ4ChN2vRCeJj6IyLhFlhaJSdQANRH9vgdyLC/Tya+Y2y3gN1hboKeI48eTu0T2
 wjvKisIVPJGngIYBCQHv7kXYMiO132n+NmKLJn8Bft7tWP2A0C8zecxeu+UuNHmxVE
 Jljsli5I/nyEz+fjlR/a/lACnFwnI+uONf9oKa0zSWSzWJeYsDV6Y58R61Q6cVbo17
 hd3tu4fSThNq8LsEUctpQlwGoir2AH/CychtGCSHSvUDtg1j3U3H24Ji4XHZSLWfG1
 CB3KiKZTGWpHQ==
From: miha@HIDDEN
To: bug-gnu-emacs@HIDDEN
Subject: 29.0.50; [PATCH] Fontification and indentation in M-x shell and ielm
Date: Thu, 18 Nov 2021 10:47:37 +0100
Message-ID: <87sfvteqx2.fsf@miha-pc>
MIME-Version: 1.0
Content-Type: multipart/signed; boundary="==-=-=";
 micalg=pgp-sha256; protocol="application/pgp-signature"
Received-SPF: pass client-ip=209.250.245.214; envelope-from=miha@HIDDEN;
 helo=kamnitnik.top
X-Spam_score_int: 0
X-Spam_score: -0.1
X-Spam_bar: /
X-Spam_report: (-0.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,
 DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,
 FROM_SUSPICIOUS_NTLD=0.001, FROM_SUSPICIOUS_NTLD_FP=1.997,
 PDS_OTHER_BAD_TLD=0.001, SPF_HELO_PASS=-0.001,
 SPF_PASS=-0.001 autolearn=no autolearn_force=no
X-Spam_action: no action
X-Spam-Score: 1.7 (+)
X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org",
 has NOT identified this incoming email as spam.  The original
 message has been attached to this so you can view it or label
 similar future email.  If you have any questions, see
 the administrator of that system for details.
 Content preview: Please find the attached patches which enable fontification
 and indentation in M-x shell and ielm. The main fontification and indentation
 machinery is implemented in comint.el and should be general en [...] 
 Content analysis details:   (1.7 points, 10.0 required)
 pts rule name              description
 ---- ---------------------- --------------------------------------------------
 -2.3 RCVD_IN_DNSWL_MED      RBL: Sender listed at https://www.dnswl.org/,
 medium trust [209.51.188.17 listed in list.dnswl.org]
 2.0 PDS_OTHER_BAD_TLD      Untrustworthy TLDs
 [URI: kamnitnik.top (top)]
 0.9 SPF_FAIL               SPF: sender does not match SPF record (fail)
 [SPF failed: Please see http://www.openspf.org/Why?s=mfrom;
 id=miha%40kamnitnik.top; ip=209.51.188.17; r=debbugs.gnu.org]
 -0.0 SPF_HELO_PASS          SPF: HELO matches SPF record
 -0.0 RCVD_IN_MSPIKE_H2      RBL: Average reputation (+2)
 [209.51.188.17 listed in wl.mailspike.net]
 0.5 FROM_SUSPICIOUS_NTLD_FP From abused NTLD
 0.5 FROM_SUSPICIOUS_NTLD   From abused NTLD
X-Debbugs-Envelope-To: submit
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: 0.1 (/)

--==-=-=
Content-Type: multipart/mixed; boundary="=-=-="

--=-=-=
Content-Type: text/plain

Please find the attached patches which enable fontification and
indentation in M-x shell and ielm. The main fontification and
indentation machinery is implemented in comint.el and should be general
enough for use by other comint derived modes. It works by making an
indirect buffer, putting it in a suitable major mode (sh-mode in
M-x shell buffers, for example) and fontifying and indenting user input
that buffer.

The fourth patch also implements highlighting of non-existent or
misspelled commands in M-x shell, which I find particularly useful to
immediately spot typos. This functionality is disabled by default
because it sacrifices a bit of performance.


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0001-Implement-a-general-input-fontification-mechanism-fo.patch
Content-Transfer-Encoding: quoted-printable

From=206afffaecf1c73f43c289a749c33cce0ab8dd727f Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 22:51:51 +0100
Subject: [PATCH 1/5] Implement a general input fontification mechanism for
 comint modes

* lisp/comint.el
(comint-indirect-setup-function): New variable for comint derived
major modes to customize.
(comint-fl-mode): New minor mode that fontifies input text through an
indirect buffer.
(comint-indirect-setup-hook):
(comint--indirect-buffer):
(comint--fl-saved-jit-lock-contextually):
(comint--fl-on):
(comint--fl-off):
(comint--fl-ppss-flush-indirect):
(comint--fl-fontify-region):
(comint--intersect-regions):
(comint-indirect-buffer):
(comint--indirect-cleanup): New functions and buffer-local variables.
=2D--
 lisp/comint.el | 223 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 223 insertions(+)

diff --git a/lisp/comint.el b/lisp/comint.el
index 544f0b8b82..54f5bcd7f7 100644
=2D-- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -1931,6 +1931,7 @@ comint-send-input
               (when comint-highlight-input
                 (add-text-properties beg end
                                      '( font-lock-face comint-highlight-in=
put
+                                        comint--fl-inhibit-fontification t
                                         front-sticky t )))
               (unless comint-use-prompt-regexp
                 ;; Give old user input a field property of `input', to
@@ -4042,6 +4043,228 @@ comint-osc-hyperlink-handler
              (cons (point-marker) (match-string-no-properties 1 text)))))
=20
 
+;;; Input fontification through an indirect buffer
+;;=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
+;;
+;; 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.
+
+(defvar-local comint-indirect-setup-function nil
+  "Function to set up an indirect buffer.
+This function is called by `comint-indirect-buffer' with zero
+arguments after making a comint 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
+`after-change-major-mode-hook' are bound to nil.")
+
+(defcustom comint-indirect-setup-hook nil
+  "Hook run after setting up the indirect fontification buffer.
+It is run after the indirect buffer is set up for fontification
+of input regions."
+  :group 'comint
+  :type 'hook
+  :version "29.1")
+
+(defvar-local comint--indirect-buffer nil
+  "Indirect buffer used for input fontification.")
+
+(defvar-local comint--fl-saved-jit-lock-contextually nil)
+
+(define-minor-mode comint-fl-mode
+  "Enable input fontification.
+Input fontification happens through an indirect buffer created
+with `comint-indirect-buffer', which see.
+
+This function signals an error if `comint-use-prompt-regexp' is
+non-nil because input fontification isn't compatible with this
+setting."
+  :lighter nil
+  (if comint-fl-mode
+      (let ((success nil))
+        (unwind-protect
+            (progn
+              (comint--fl-on)
+              (setq success t))
+          (unless success
+            (setq comint-fl-mode nil)
+            (comint--fl-off))))
+    (comint--fl-off)))
+
+(defun comint--fl-on ()
+  "Enable input fontification for the current buffer."
+  (comint--fl-off)
+
+  (when comint-use-prompt-regexp
+    (error
+     "Input fontification is incompatible with `comint-use-prompt-regexp'"=
))
+
+  (add-function :around (local 'font-lock-fontify-region-function)
+                #'comint--fl-fontify-region)
+  ;; `before-change-functions' are only run in the current buffer and
+  ;; not in its indirect buffers, which means that we must manually
+  ;; flush ppss cache
+  (add-hook 'before-change-functions
+            #'comint--fl-ppss-flush-indirect 99 t)
+
+  ;; Set up contextual fontification
+  (unless (booleanp jit-lock-contextually)
+    (setq comint--fl-saved-jit-lock-contextually
+          jit-lock-contextually)
+    (setq-local jit-lock-contextually t)
+    (when jit-lock-mode
+      (jit-lock-mode t))))
+
+(defun comint--fl-off ()
+  "Disable input fontification for the current buffer."
+  (remove-function (local 'font-lock-fontify-region-function)
+                   #'comint--fl-fontify-region)
+  (remove-hook 'before-change-functions
+               #'comint--fl-ppss-flush-indirect t)
+
+  ;; Reset contextual fontification
+  (when comint--fl-saved-jit-lock-contextually
+    (setq-local jit-lock-contextually
+                comint--fl-saved-jit-lock-contextually)
+    (setq comint--fl-saved-jit-lock-contextually nil)
+    (when jit-lock-mode
+      (jit-lock-mode t)))
+
+  (font-lock-flush))
+
+(defun comint--fl-ppss-flush-indirect (beg &rest rest)
+  (when-let ((buf (comint-indirect-buffer t)))
+    (with-current-buffer buf
+      (when (memq #'syntax-ppss-flush-cache before-change-functions)
+        (apply #'syntax-ppss-flush-cache beg rest)))))
+
+(defun comint--fl-fontify-region (fun beg end verbose)
+  "Around advice for `font-lock-fontify-region-function'.
+Fontify region between BEG and END.  First, highlight it using
+FUN.  Then highlight only the input text in the region with the
+help of an indirect buffer.  VERBOSE is passed to the
+fontify-region functions.  Skip fontification of input regions
+with non-nil `comint--fl-inhibit-fontification' text property."
+  (pcase (funcall fun beg end verbose)
+    (`(jit-lock-bounds ,beg1 . ,end1)
+     (setq beg beg1 end end1)))
+  (pcase
+      (let ((min (point-min))
+            (max (point-max)))
+        (with-current-buffer (comint-indirect-buffer)
+          (narrow-to-region min max)
+          (comint--intersect-regions
+           nil (lambda (beg end)
+                 (unless (get-text-property
+                          beg 'comint--fl-inhibit-fontification)
+                   (font-lock-fontify-region beg end verbose)))
+           beg end)))
+    (`((jit-lock-bounds ,beg1 . ,_) . (jit-lock-bounds ,_ . ,end1))
+     (setq beg (min beg beg1))
+     (setq end (max end end1))))
+
+  `(jit-lock-bounds ,beg . ,end))
+
+(defun comint--intersect-regions (fun-output fun-input beg end)
+  "Run functions for suitable regions.
+Divide the region specified by BEG and END into smaller regions
+that cover either process output (its 'field property is 'output)
+or input (all remaining text).  Interchangeably call FUN-OUTPUT on
+each output region, and FUN-INPUT on each input region.
+
+FUN-OUTPUT and FUN-INPUT are passed two arguments, the beginning
+and end of the smaller region.  Before calling each function,
+narrow the buffer to the surrounding process output or input.  You
+can also pass nil as either function to skip its corresponding
+regions.
+
+Return a cons cell of return values of the first and last
+function called, or nil, if no function was called (if BEG =3D END)."
+  (let ((beg1 beg)
+        (end1 (copy-marker nil t))
+        (return-beg nil) (return-end nil)
+        (is-output (eq (get-text-property beg 'field) 'output)))
+    (setq end (copy-marker end t))
+
+    (while (< beg1 end)
+      (set-marker
+       end1 (or (if is-output
+                    (text-property-not-all beg1 end 'field 'output)
+                  (text-property-any beg1 end 'field 'output))
+                end))
+      (when-let ((fun (if is-output fun-output fun-input)))
+        (save-restriction
+          (let ((beg2 beg1)
+                (end2 end1))
+            (when (=3D beg2 beg)
+              (setq beg2 (field-beginning beg2)))
+            (when (=3D end2 end)
+              (setq end2 (field-end end2)))
+            ;; Narrow to the whole field surrounding the region
+            (narrow-to-region beg2 end2))
+          (setq return-end (list (funcall fun beg1
+                                          (marker-position end1)))))
+        (unless return-beg
+          (setq return-beg return-end)))
+      (setq beg1 (marker-position end1))
+      (setq is-output (not is-output)))
+
+    (set-marker end nil)
+    (set-marker end1 nil)
+    (when return-beg
+      (cons (car return-beg) (car return-end)))))
+
+(defun comint-indirect-buffer (&optional no-create)
+  "Return an indirect buffer.
+If an indirect buffer for the current buffer already exists,
+return it, otherwise create it first and set it up by calling
+`comint-indirect-setup-function' with zero arguments, turning on
+font-lock, and running `comint-indirect-setup-hook'.  This setup
+happens with `delay-mode-hooks' set to t in order to prevent
+possible SETUP-FUN's mode hooks from running.
+
+If an indirect buffer doesn't exist and NO-CREATE is non-nil,
+return nil."
+  (or
+   comint--indirect-buffer
+   (unless no-create
+     (let ((setup-hook
+            (if (local-variable-p 'comint-indirect-setup-hook)
+                (list comint-indirect-setup-hook)))
+           (setup-fun comint-indirect-setup-function))
+
+       (add-hook 'change-major-mode-hook #'comint--indirect-cleanup
+                 nil t)
+
+       (with-current-buffer
+           (setq comint--indirect-buffer
+                 (make-indirect-buffer
+                  (current-buffer)
+                  (generate-new-buffer-name
+                   (concat " " (buffer-name) "-comint-indirect"))))
+         (setq-local delay-mode-hooks t)
+         (when setup-fun
+           (let ((change-major-mode-hook nil)
+                 (after-change-major-mode-hook nil))
+             (funcall setup-fun)))
+         (setq-local font-lock-dont-widen t)
+         (setq-local font-lock-support-mode nil)
+         (font-lock-mode)
+         (when setup-hook
+           (setq-local comint-indirect-setup-hook
+                       (car setup-hook)))
+         (run-hooks 'comint-indirect-setup-hook))
+       comint--indirect-buffer))))
+
+(defun comint--indirect-cleanup ()
+  (when comint--indirect-buffer
+    (kill-buffer comint--indirect-buffer)
+    (setq comint--indirect-buffer nil)))
+
+
+
 ;;; Converting process modes to use comint mode
 ;;=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
 ;; The code in the Emacs 19 distribution has all been modified to use comi=
nt
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0002-Input-fontification-for-M-x-shell.patch
Content-Transfer-Encoding: quoted-printable

From=20ca566e8813cba89236c97f9f35e3fe78a947fd11 Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:13:03 +0100
Subject: [PATCH 2/5] Input fontification for M-x shell

* lisp/shell.el (shell-comint-fl-enable): New user option to control
input fontification.
(shell-comint-fl-mode-setup-hook): New hook.
(shell-mode): Set up and enable input fontification.
=2D--
 lisp/shell.el | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/lisp/shell.el b/lisp/shell.el
index 370532ea46..347b3e04b9 100644
=2D-- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -306,6 +306,22 @@ shell-input-autoexpand
 		 (const :tag "on" t))
   :group 'shell)
=20
+(defcustom shell-comint-fl-enable t
+  "Enable highlighting of input in shell buffers.
+This variable only has effect when the shell is started.  Use the
+command `comint-fl-mode' to toggle highlighting of input."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom shell-comint-fl-mode-setup-hook nil
+  "Hook run after setting up an indirect shell fontification buffer."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
 (defvar shell-dirstack nil
   "List of directories saved by pushd in this buffer's shell.
 Thus, this does not include the shell's current directory.")
@@ -516,6 +532,8 @@ shell-completion-vars
=20
 (put 'shell-mode 'mode-class 'special)
=20
+(defvar sh-shell-file)
+
 (define-derived-mode shell-mode comint-mode "Shell"
   "Major mode for interacting with an inferior shell.
 \\<shell-mode-map>
@@ -572,6 +590,11 @@ shell-mode
 control whether input and output cause the window to scroll to the end of =
the
 buffer."
   :interactive nil
+  :after-hook
+  (and (null comint-use-prompt-regexp)
+       shell-comint-fl-enable
+       (comint-fl-mode))
+
   (setq comint-prompt-regexp shell-prompt-pattern)
   (shell-completion-vars)
   (setq-local paragraph-separate "\\'")
@@ -591,6 +614,19 @@ shell-mode
   (setq-local ansi-color-apply-face-function #'shell-apply-ansi-color)
   (shell-reapply-ansi-color)
=20
+  (add-hook 'comint-indirect-setup-hook
+            #'shell-comint-fl-mode-setup-hook 'append t)
+  (setq comint-indirect-setup-function
+        (let ((shell shell--start-prog))
+          (lambda ()
+            (require 'sh-script)
+            (cl-letf
+                (((default-value 'sh-shell-file)
+                  (or shell sh-shell-file))
+                 (inhibit-message t)
+                 (message-log-max nil))
+              (sh-mode)))))
+
   ;; 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))
@@ -645,6 +681,10 @@ shell-mode
                     ": [[:digit:]]+:[[:digit:]]+;")))
     (comint-read-input-ring t)))
=20
+(defun shell-comint-fl-mode-setup-hook ()
+  "Run `shell-comint-fl-mode-setup-hook'."
+  (run-hooks 'shell-comint-fl-mode-setup-hook))
+
 (defun shell-apply-ansi-color (beg end face)
   "Apply FACE as the ansi-color face for the text between BEG and END."
   (when face
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0003-Input-fontification-for-M-x-ielm.patch
Content-Transfer-Encoding: quoted-printable

From=20c368fd4f75ac43c38e493e86f2abb16a0c378bbe Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:16:23 +0100
Subject: [PATCH 3/5] Input fontification for M-x ielm

* lisp/ielm.el (ielm-comint-fl-enable): New user option to control
input fontification.
(ielm-indirect-setup-hook): New hook.
(inferior-emacs-lisp-mode): Set up and enable input fontification.
=2D--
 lisp/ielm.el | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/lisp/ielm.el b/lisp/ielm.el
index 39820a893a..d792bd871d 100644
=2D-- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -472,6 +472,26 @@ ielm-set-pm
   ;; Set the process mark in the current buffer to POS.
   (set-marker (process-mark (get-buffer-process (current-buffer))) pos))
=20
+;;; Input fontification
+
+(defcustom ielm-comint-fl-enable t
+  "Enable highlighting of input in ielm buffers.
+This variable only has effect when the shell is started.  Use the
+command `comint-fl-mode' to toggle highlighting of input."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom ielm-indirect-setup-hook nil
+  "Hook run after setting up an indirect ielm fontification buffer."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
+(defun ielm-indirect-setup-hook ()
+  "Run `ielm-indirect-setup-hook'."
+  (run-hooks 'ielm-indirect-setup-hook))
+
 ;;; Major mode
=20
 (define-derived-mode inferior-emacs-lisp-mode comint-mode "IELM"
@@ -526,6 +546,10 @@ inferior-emacs-lisp-mode
 Customized bindings may be defined in `ielm-map', which currently contains:
 \\{ielm-map}"
   :syntax-table emacs-lisp-mode-syntax-table
+  :after-hook
+  (and (null comint-use-prompt-regexp)
+       ielm-comint-fl-enable
+       (comint-fl-mode))
=20
   (setq comint-prompt-regexp (concat "^" (regexp-quote ielm-prompt)))
   (setq-local paragraph-separate "\\'")
@@ -564,6 +588,10 @@ inferior-emacs-lisp-mode
   (setq-local font-lock-defaults
        '(ielm-font-lock-keywords nil nil ((?: . "w") (?- . "w") (?* . "w")=
)))
=20
+  (add-hook 'comint-indirect-setup-hook
+            #'ielm-indirect-setup-hook 'append t)
+  (setq comint-indirect-setup-function #'emacs-lisp-mode)
+
   ;; A dummy process to keep comint happy. It will never get any input
   (unless (comint-check-proc (current-buffer))
     ;; Was cat, but on non-Unix platforms that might not exist, so
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0004-Highlight-non-existent-commands-in-M-x-shell.patch
Content-Transfer-Encoding: quoted-printable

From=209f2ab28d4c9b044de707acbfb0d5f4558f378738 Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:21:03 +0100
Subject: [PATCH 4/5] Highlight non-existent commands in M-x shell

* lisp/shell.el (shell-mode): Enable highlighting of non-existent
commands if requested.
(shell-highlight-undef-aliases):
(shell-highlight-undef-remote-file-name-inhibit-cache): New user
options.
(shell-highlight-undef-mode): New minor mode.
(shell-highlight-undef-defined-face):
(shell-highlight-undef-undefined-face):
(shell-highlight-undef-alias-face): New faces.
(shell-highlight-undef--exec-cache):
(shell-highlight-undef--face):
(shell-highlight-undef-keywords):
(shell-highlight-undef-regexp):
(shell-highlight-undef--executable-find):
(shell-highlight-undef-matcher):
(shell-highlight-undef--indirect):
(shell-highlight--setup):
(shell-highlight-undef-reset-mode): New functions and buffer local
variables.
=2D--
 lisp/shell.el | 227 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 224 insertions(+), 3 deletions(-)

diff --git a/lisp/shell.el b/lisp/shell.el
index 347b3e04b9..3c008ec508 100644
=2D-- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -322,6 +322,16 @@ shell-comint-fl-mode-setup-hook
   :safe 'booleanp
   :version "29.1")
=20
+(defcustom shell-highlight-undef-enable nil
+  "Enable highlighting of undefined commands in shell buffers.
+This variable only has effect when the shell is started.  Use the
+command `shell-highlight-undef-mode' to toggle highlighting of
+undefined commands."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
 (defvar shell-dirstack nil
   "List of directories saved by pushd in this buffer's shell.
 Thus, this does not include the shell's current directory.")
@@ -591,9 +601,11 @@ shell-mode
 buffer."
   :interactive nil
   :after-hook
=2D  (and (null comint-use-prompt-regexp)
=2D       shell-comint-fl-enable
=2D       (comint-fl-mode))
+  (unless comint-use-prompt-regexp
+    (if shell-comint-fl-enable
+        (comint-fl-mode))
+    (if shell-highlight-undef-enable
+        (shell-highlight-undef-mode)))
=20
   (setq comint-prompt-regexp shell-prompt-pattern)
   (shell-completion-vars)
@@ -1471,6 +1483,215 @@ shell-narrow-to-prompt
            (point-max)
          (shell--prompt-begin-position))))))
=20
+;;; Highlight undefined commands
+;;
+;; To highlight non-existent commands, customize
+;; `shell-highlight-undef-enable' to t.  To highlight some commands as
+;; aliases, add them to `shell-highlight-undef-aliases'.
+
+(defcustom shell-highlight-undef-aliases nil
+  "List commands to highlight as a command alias."
+  :group 'shell
+  :type '(repeat string)
+  :version "29.1")
+
+(defface shell-highlight-undef-defined-face
+  '((t :inherit 'font-lock-function-name-face))
+  "Face used for existent commands."
+  :group 'shell
+  :version "29.1")
+
+(defface shell-highlight-undef-undefined-face
+  '((t :inherit 'font-lock-warning-face))
+  "Face used for non-existent commands."
+  :group 'shell
+  :version "29.1")
+
+(defface shell-highlight-undef-alias-face
+  '((t :inherit 'font-lock-variable-name-face))
+  "Face used command aliases."
+  :group 'shell
+  :version "29.1")
+
+(defcustom shell-highlight-undef-remote-file-name-inhibit-cache nil
+  "Whether to use cache for finding remote executables.
+See `remote-file-name-inhibit-cache' for description."
+  :group 'faces
+  :type '(choice
+          (const :tag "Do not inhibit file name cache" nil)
+          (const :tag "Do not use file name cache" t)
+          (integer :tag "Do not use file name cache"
+                   :format "Do not use file name cache older than %v secon=
ds"
+                   :value 10))
+  :version "29.1")
+
+(defvar shell--highlight-undef-exec-cache nil
+  "Cache of executable files found in `exec-path'.
+An alist, whose elements are of the form
+\(REMOTE TIME EXECUTABLES), where REMOTE is a string, returned by
+`file-remote-p', TIME is the return value of `float-time' end
+EXECUTABLES is a hash table with keys being the base-names of
+executable files.
+
+Cache expiry is controlled by the user option
+`remote-file-name-inhibit-cache'.")
+
+(defvar shell--highlight-undef-face 'shell-highlight-undef-defined-face)
+
+(defvar shell-highlight-undef-keywords
+  `((,#'shell-highlight-undef-matcher 6 shell--highlight-undef-face)))
+
+(defvar-local shell-highlight-undef-regexp regexp-unmatchable)
+
+(defun shell--highlight-undef-executable-find (command)
+  "Return non-nil if COMMAND is found in `exec-path'.
+Similar to `executable-find', but use cache stored in
+`shell--highlight-undef-exec-cache'."
+  (let ((remote (file-remote-p default-directory))
+        as ret found-in-cache delta-time)
+    (if (null remote)
+        (executable-find command)
+
+      (setq delta-time
+            shell-highlight-undef-remote-file-name-inhibit-cache)
+
+      (pcase (setq as (assoc remote shell--highlight-undef-exec-cache))
+        (`(,_ ,time ,hash)
+         (when (pcase delta-time
+                 ((pred numberp) (<=3D (float-time) (+ time delta-time)))
+                 ('t nil)
+                 ('nil t))
+           (setq ret (gethash command hash))
+           (setq found-in-cache t)))
+        (_ (setq as (list remote 0 (make-hash-table :test #'equal)))
+           (push as shell--highlight-undef-exec-cache)))
+
+      (if found-in-cache
+          ret
+        ;; Build cache
+        (setcar (cdr as) (float-time))
+        (let ((hash (clrhash (caddr as))))
+          (dolist (dir (exec-path))
+            (pcase-dolist (`(,f . ,attr)
+                           (condition-case nil
+                               (directory-files-and-attributes
+                                (concat remote dir) nil nil 'nosort 'integ=
er)
+                             (file-error nil)))
+              ;; Approximation.  Assume every non-directory file in $PATH =
is an
+              ;; executable.  Alternatively, we could check
+              ;; `file-executable-p', but doing so for every file in $PATH=
 is
+              ;; slow on remote machines.
+              (unless (eq t (file-attribute-type attr))
+                (puthash f t hash))))
+          (gethash command hash))))))
+
+(defun shell-highlight-undef-matcher (end)
+  "Matcher used to highlight commands up to END."
+  (when (re-search-forward shell-highlight-undef-regexp end t)
+    (save-match-data
+      (let ((cmd (match-string 6))
+            (beg (match-beginning 6)))
+        (setq shell--highlight-undef-face
+              (let* ((buf (buffer-base-buffer))
+                     (default-directory
+                      (if buf (buffer-local-value 'default-directory buf)
+                        default-directory)))
+                (cond
+                 ;; Don't highlight command output.  Mostly useful if
+                 ;; `shell-comint-fl-mode' is disabled.
+                 ((text-property-any beg (point) 'field 'output)
+                  nil)
+                 ((member cmd shell-highlight-undef-aliases)
+                  'shell-highlight-undef-alias-face)
+                 ;; Check if it contains a directory separator
+                 ((file-name-directory cmd)
+                  (when (file-name-absolute-p cmd)
+                    (setq cmd (concat
+                               (or (bound-and-true-p comint-file-name-pref=
ix)
+                                   (file-remote-p default-directory))
+                               cmd)))
+                  (if (or (file-executable-p cmd)
+                          (file-directory-p cmd))
+                      'shell-highlight-undef-defined-face
+                    'shell-highlight-undef-undefined-face))
+                 ((shell--highlight-undef-executable-find cmd)
+                  'shell-highlight-undef-defined-face)
+                 (t 'shell-highlight-undef-undefined-face))))))
+    t))
+
+(defvar-local shell--highlight-undef-indirect nil
+  "Whether we are using `shell-comint-fl-mode' indirect buffer.")
+
+(declare-function sh-feature "sh-script" (alist &optional function))
+(defvar sh-leading-keywords)
+(defvar sh-other-keywords)
+
+(define-minor-mode shell-highlight-undef-mode
+  "Highlight undefined shell commands and aliases."
+  :init-value nil
+  (if shell--highlight-undef-indirect
+      (progn
+        (setq shell--highlight-undef-indirect nil)
+        (when-let ((buf (comint-indirect-buffer t)))
+          (with-current-buffer buf
+            (font-lock-remove-keywords nil shell-highlight-undef-keywords)=
)))
+    (font-lock-remove-keywords nil shell-highlight-undef-keywords))
+  (remove-hook 'comint-fl-mode-hook
+               #'shell-highlight-undef-reset-mode t)
+  (remove-hook 'comint-indirect-setup-hook #'shell--highlight-undef-setup =
t)
+
+  (when shell-highlight-undef-mode
+    (when comint-use-prompt-regexp
+      (setq shell-highlight-undef-mode nil)
+      (error
+       "`shell-highlight-undef-mode' is incompatible with `comint-use-prom=
pt-regexp'"))
+
+    (require 'sh-script)
+
+    (let ((regexp
+           ;; Adapted from `sh-font-lock-keywords-1'
+           (concat
+            "\\("
+            "[;(){}`|&]"
+            (if comint-fl-mode
+                ;; `comint-fl-mode' already puts point-min on end of
+                ;; prompt
+                ""
+              (concat "\\|" comint-prompt-regexp))
+            "\\|^"
+            "\\)"
+            "[ \t]*\\(\\("
+            (regexp-opt (sh-feature sh-leading-keywords) t)
+            "[ \t]+\\)?"
+            (regexp-opt (append (sh-feature sh-leading-keywords)
+                                (sh-feature sh-other-keywords))
+                        t)
+            "[ \t]+\\)?\\_<\\(\\(?:\\s_\\|\\sw\\|/\\)+\\)\\_>")))
+      (cond (comint-fl-mode
+             (setq shell--highlight-undef-indirect t)
+             (if-let ((buf (comint-indirect-buffer t)))
+                 (with-current-buffer buf
+                   (shell--highlight-undef-setup regexp))
+               (add-hook 'comint-indirect-setup-hook
+                         (lambda ()
+                           (shell--highlight-undef-setup regexp))
+                         nil t)))
+            (t (shell--highlight-undef-setup regexp))))
+
+    (add-hook 'comint-fl-mode-hook
+              #'shell-highlight-undef-reset-mode nil t))
+
+  (font-lock-flush))
+
+(defun shell--highlight-undef-setup (regexp)
+  (font-lock-add-keywords nil shell-highlight-undef-keywords t)
+  (setq shell-highlight-undef-regexp regexp))
+
+(defun shell-highlight-undef-reset-mode ()
+  "If `shell-highlight-undef-mode' is on, turn it off and on."
+  (when shell-highlight-undef-mode
+    (shell-highlight-undef-mode 1)))
+
 (provide 'shell)
=20
 ;;; shell.el ends here
=2D-=20
2.34.0


--=-=-=
Content-Type: text/x-patch
Content-Disposition: inline;
 filename=0005-Input-indentation-for-M-x-shell.patch
Content-Transfer-Encoding: quoted-printable

From=20cfa2293b71af972432969f784b33a29ec0ac7b44 Mon Sep 17 00:00:00 2001
From: =3D?UTF-8?q?Miha=3D20Rihtar=3DC5=3DA1i=3DC4=3D8D?=3D <miha@kamnitnik.=
top>
Date: Mon, 15 Nov 2021 23:37:36 +0100
Subject: [PATCH 5/5] 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 the 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
sh-mode.
=2D--
 lisp/comint.el | 95 +++++++++++++++++++++++++++++++++++++++++++++-----
 lisp/shell.el  |  4 +++
 2 files changed, 90 insertions(+), 9 deletions(-)

diff --git a/lisp/comint.el b/lisp/comint.el
index 54f5bcd7f7..cc3cf2a7fa 100644
=2D-- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -4043,22 +4043,22 @@ comint-osc-hyperlink-handler
              (cons (point-marker) (match-string-no-properties 1 text)))))
=20
 
=2D;;; Input fontification through an indirect buffer
+;;; Input fontification and indentation through an indirect buffer
 ;;=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
 ;;
=2D;; Modes derived from `comint-mode' can set up fontification input
=2D;; text with the help of an indirect buffer whose major mode and
=2D;; 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.
=20
 (defvar-local comint-indirect-setup-function nil
   "Function to set up an indirect buffer.
 This function is called by `comint-indirect-buffer' with zero
 arguments after making a comint indirect buffer.  It is usually
=2Dset to a major-mode command whose font-locking is desired for
=2Dinput text.  In order to prevent possible mode hooks from
=2Drunning, the variable `delay-mode-hooks' is set to t prior to
=2Dcalling this function and `change-major-mode-hook' along with
=2D`after-change-major-mode-hook' are bound to nil.")
+set to 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.")
=20
 (defcustom comint-indirect-setup-hook nil
   "Hook run after setting up the indirect fontification buffer.
@@ -4216,6 +4216,83 @@ comint--intersect-regions
     (when return-beg
       (cons (car return-beg) (car return-end)))))
=20
+(defun comint-indent-input-line (fun)
+  "Indent current input or process output line.
+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 the region 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 (=3D 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 (=3D 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 input or output line.
+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 the region 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 buffer.
 If an indirect buffer for the current buffer already exists,
diff --git a/lisp/shell.el b/lisp/shell.el
index 3c008ec508..3629171fbe 100644
=2D-- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -639,6 +639,10 @@ shell-mode
                  (message-log-max nil))
               (sh-mode)))))
=20
+  (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))
=2D-=20
2.34.0


--=-=-=--

--==-=-=
Content-Type: application/pgp-signature; name="signature.asc"

-----BEGIN PGP SIGNATURE-----

iQJHBAEBCAAxFiEEmxVnesoT5rQXvVXnswkaGpIVmT8FAmGWITkTHG1paGFAa2Ft
bml0bmlrLnRvcAAKCRCzCRoakhWZP5GyD/0aoZCQPfrPIRUSspSJTJkrLFAQIRiW
x35J+Iljst9DDNWnaQmICOyK0fe+DXLOw/lYKHYExj76msNgZZWfSCAeDU86zOYn
twzq+Aj+sndallzBP7BPRp9e4iUHafZ3smzMwApWwUdi7coVVnhvwOtSOg4Pt+7Z
DOR10XNOYqJIfXBMSPMvcNr10SvCQQNF1U2yUo85no1zjVdfJ4JvJshvhG9AXz96
+XB85GjCMiRJoHAE5ctzRaE8RRIcabNp4mqFzbmmuxtTZQy90d6AlxWMpGnzlpQZ
1qrjP0mzlcuSU0Mgyjl/AjFoX8sKI0Adv2JocAxIHbeL0Nk27QVe5k8PIg69b6DH
PGas2MqVpAmQ5c5mYhK2ZdpxzNIMFeYEFlLi0SG5ieQaKrKbjDeF0laj7/ASDfNy
rq4GBCEbzc9XgdGVG93BTx20NEhS1+2N6R07VVwILtTZUFxPn7J/Ee/ffyWyvvpW
sCldDcjfQ1kvVgSMyTlt/pcW9l9EjMzb3Ai64leURsytVMYzoHSVRS5PXLsRZKp9
qgFPm/hfahajX8axm5oPUOYtWoAwdP+CRQExHq1m2VB0M1utN1HT4zELAWBJkWvh
T2T6LaNTh4zDqdAad6q4bDSMeSLVJi08qvNg8Q4qBTxX5TC0bm9lQ8Vbq4j++Xa4
oc48jhxIqR/+lQ==
=I3wV
-----END PGP SIGNATURE-----
--==-=-=--




Acknowledgement sent to miha@HIDDEN:
New bug report received and forwarded. Copy sent to bug-gnu-emacs@HIDDEN. Full text available.
Report forwarded to bug-gnu-emacs@HIDDEN:
bug#51940; Package emacs. Full text available.
Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.
Last modified: Sun, 28 Nov 2021 12:30:02 UTC

GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997 nCipher Corporation Ltd, 1994-97 Ian Jackson.