From 80c8c26876f82c9c8ab20f47b28ded62f0b13990 Mon Sep 17 00:00:00 2001
From: Shynur <one.last.kiss@outlook.com>
Date: Fri, 5 May 2023 04:30:42 +0800
Subject: [PATCH] Show-paren-mode highlights entire delimiter in the echo area

Show-paren-mode now displays the context string containing the entire
matched delimiter and highlights it conditionally.
* lisp/simple.el (blink-matching-paren-offscreen): Set a color for
light background.
(blink-paren-open-paren-line-string): Can return the context string
containing the entire matched delimiter by specifying the value of the
argument DELIMITER-LENGTH.
* lisp/paren.el (show-paren-function): Can highlight the matched
delimiter in the echo area now.
* doc/emacs/programs.texi (Matching Parentheses): Add the description
of `blink-matching-paren-highlight-offscreen'.
; * doc/emacs/files.texi (Visiting Files): Fix typo.
---
 doc/emacs/files.texi    |  2 +-
 doc/emacs/programs.texi | 13 ++++++++++++-
 lisp/paren.el           | 11 +++++++----
 lisp/simple.el          | 41 ++++++++++++++++++++++++++++-------------
 4 files changed, 48 insertions(+), 19 deletions(-)

diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi
index 74315a05bbd..46056b6d4fa 100644
--- a/doc/emacs/files.texi
+++ b/doc/emacs/files.texi
@@ -232,7 +232,7 @@ Visiting
 characters, Emacs visits all the files that match it.  (On
 case-insensitive filesystems, Emacs matches the wildcards disregarding
 the letter case.)  Wildcards include @samp{?}, @samp{*}, and
-@samp{[@dots{}]} sequences.  To enter the wild card @samp{?} in a file
+@samp{[@dots{}]} sequences.  To enter the wildcard @samp{?} in a file
 name in the minibuffer, you need to type @kbd{C-q ?}.  @xref{Quoted
 File Names}, for information on how to visit a file whose name
 actually contains wildcard characters.  You can disable the wildcard
diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi
index 62df88a731e..3ee2a48de08 100644
--- a/doc/emacs/programs.texi
+++ b/doc/emacs/programs.texi
@@ -930,7 +930,8 @@ Matching
 @vindex blink-matching-paren
 @vindex blink-matching-paren-distance
 @vindex blink-matching-delay
-  Three variables control the display of matching parentheses:
+@vindex blink-matching-paren-highlight-offscreen
+  Four variables control the display of matching parentheses:
 
 @itemize @bullet
 @item
@@ -950,6 +951,16 @@ Matching
 back to search to find the matching opening delimiter.  If the match
 is not found in that distance, Emacs stops scanning and nothing is
 displayed.  The default is 102400.
+
+@item
+@code{blink-matching-paren-highlight-offscreen} says whether to use
+highlighting to indicate the location of the matched opening delimiter
+when it is off the screen and thus shown in the echo area.  The
+default is @code{nil}.  Note that only the 1st character of the
+delimiter is highlit; if you want to highlight the entire delimiter
+in the echo area, enable @code{show-paren-mode} (see below) and set
+both this option and @code{show-paren-context-when-offscreen} to
+@code{t}.
 @end itemize
 
 @cindex Show Paren mode
diff --git a/lisp/paren.el b/lisp/paren.el
index 4c91fd29490..c03605987d8 100644
--- a/lisp/paren.el
+++ b/lisp/paren.el
@@ -500,15 +500,15 @@ show-paren-function
             (move-overlay show-paren--overlay
                           there-beg there-end (current-buffer)))
           ;; If `show-paren-context-when-offscreen' is non-nil and
-          ;; point is at a closing paren, show the context around the
-          ;; opening paren.
+          ;; point is at a closing paren / delimiter, show the
+          ;; context around the opening paren / delimiter.
           (let ((openparen (min here-beg there-beg)))
             (when (and show-paren-context-when-offscreen
                        (not (eql show-paren--last-pos (point)))
                        (< there-beg here-beg)
                        (not (pos-visible-in-window-p openparen)))
               (let ((context (blink-paren-open-paren-line-string
-                              openparen))
+                              openparen (- there-end there-beg)))
                     (message-log-max nil))
                 (cond
                  ((and
@@ -518,7 +518,10 @@ show-paren-function
                  ((eq show-paren-context-when-offscreen 'overlay)
                   (show-paren--show-context-in-overlay context))
                  (show-paren-context-when-offscreen
-                  (minibuffer-message "Matches %s" context))))))
+                  (minibuffer-message
+                   "%s %s"
+                   (propertize "Matches" 'face 'shadow)
+                   context))))))
           (setq show-paren--last-pos (point))
           ;; Always set the overlay face, since it varies.
           (overlay-put show-paren--overlay 'priority show-paren-priority)
diff --git a/lisp/simple.el b/lisp/simple.el
index 58517dd81f9..4ba748c83d6 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -9235,7 +9235,10 @@ blink-matching-paren-highlight-offscreen
   :group 'paren-blinking)
 
 (defface blink-matching-paren-offscreen
-  '((t :foreground "green"))
+  '((((class color) (background light))
+     :background "yellow")
+    (((class color) (background dark))
+     :foreground "green"))
   "Face for showing in the echo area matched open paren that is off-screen.
 This face is used only when `blink-matching-paren-highlight-offscreen'
 is non-nil."
@@ -9339,14 +9342,25 @@ blink-matching-open
                  (delete-overlay blink-matching--overlay)))))
        ((not show-paren-context-when-offscreen)
         (minibuffer-message
-         "%s%s"
-         (propertize "Matches " 'face 'shadow)
+         "%s %s"
+         (propertize "Matches" 'face 'shadow)
          (blink-paren-open-paren-line-string blinkpos)))))))
 
-(defun blink-paren-open-paren-line-string (pos)
+(defun blink-paren-open-paren-line-string (pos &optional delimiter-length)
   "Return the line string that contains the openparen at POS.
-Remove the line string's properties but give the openparen a distinct
-face if `blink-matching-paren-highlight-offscreen' is non-nil."
+
+Before returning, the line string's properties are removed,
+and then its substring of DELIMITER-LENGTH characters
+starting from openparen is propertized with a distinct face
+if `blink-matching-paren-highlight-offscreen' is non-nil."
+  ;; If the openparen is essentially the 1st charater of a matched opening
+  ;; delimiter, we may want the string to be returned to contain the
+  ;; entire delimiter.  DELIMITER-LENGTH charaters starting from openparen
+  ;; are guaranteed to be contained.
+  ;; By default, it is 1, assuming that what is matched is an openparen
+  ;; instead of a delimiter whose length >= 2.
+  (when (null delimiter-length)
+    (setq delimiter-length 1))
   (save-excursion
     (goto-char pos)
     ;; Capture the regions in terms of (beg . end) conses whose
@@ -9358,11 +9372,11 @@ blink-paren-open-paren-line-string
        ;; Show what precedes the open in its line, if anything.
        ((save-excursion (skip-chars-backward " \t") (not (bolp)))
         (let ((bol (line-beginning-position)))
-          (setq regions (list (cons bol (1+ pos)))
+          (setq regions (list (cons bol (+ pos delimiter-length)))
                 openparen-idx (- pos bol))))
        ;; Show what follows the open in its line, if anything.
        ((save-excursion
-          (forward-char 1)
+          (forward-char delimiter-length)
           (skip-chars-forward " \t")
           (not (eolp)))
         (setq regions (list (cons pos (line-end-position)))
@@ -9385,9 +9399,9 @@ blink-paren-open-paren-line-string
                                                       ;; (length "...")
                                                       3))
                                eol))
-                            (cons pos (1+ pos)))))
+                            (cons pos (+ pos delimiter-length)))))
        ;; There is nothing to show except the char itself.
-       (t (setq regions (list (cons pos (1+ pos)))
+       (t (setq regions (list (cons pos (+ pos delimiter-length)))
                 openparen-idx 0)))
       ;; Ensure we've font-locked the context region.
       (font-lock-ensure (caar regions) (cdar (last regions)))
@@ -9397,20 +9411,21 @@ blink-paren-open-paren-line-string
                 (buffer-substring (car region) (cdr region)))
               regions
               "..."))
-            (openparen-next-char-idx (1+ openparen-idx)))
+            (highlit-substring-end
+             (+ openparen-idx delimiter-length)))
         (setq line-string (substring-no-properties line-string))
         (concat
          (substring line-string
                     0 openparen-idx)
          (let ((matched-offscreen-openparen
                 (substring line-string
-                           openparen-idx openparen-next-char-idx)))
+                           openparen-idx highlit-substring-end)))
            (if blink-matching-paren-highlight-offscreen
                (propertize matched-offscreen-openparen
                            'face 'blink-matching-paren-offscreen)
              matched-offscreen-openparen))
          (substring line-string
-                    openparen-next-char-idx))))))
+                    highlit-substring-end))))))
 
 (defvar blink-paren-function 'blink-matching-open
   "Function called, if non-nil, whenever a close parenthesis is inserted.
-- 
2.34.1

