From 9f505c476eb1a8e85ba26964abf218cab7db0e57 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Daniel=20Mart=C3=ADn?= Date: Sat, 16 Oct 2021 20:24:19 +0200 Subject: [PATCH] New option show-paren-context-when-offscreen * lisp/simple.el (blink-paren-open-paren-line-string): Extract functionality that shows the open paren line in the echo area into its own function, to reuse it from paren.el. (blink-matching-open): Use blink-paren-open-paren-line-string. * lisp/paren.el (show-paren-context-when-offscreen): New option show-paren-context-when-offscreen. (show-paren-function): Implement it using blink-paren-open-paren-line-string. * lisp/emacs-lisp/eldoc.el (eldoc-display-message-no-interference-p): Make sure the feature works well with eldoc. * test/lisp/paren-tests.el (paren-tests-open-paren-line): Test blink-paren-open-paren-line-string. * doc/emacs/programs.texi (Matching): Update the documentation. * etc/NEWS: And announce the new feature. --- doc/emacs/programs.texi | 9 +++++ etc/NEWS | 8 +++++ lisp/emacs-lisp/eldoc.el | 9 ++++- lisp/paren.el | 21 ++++++++++++ lisp/simple.el | 71 +++++++++++++++++++++------------------- test/lisp/paren-tests.el | 31 ++++++++++++++++++ 6 files changed, 114 insertions(+), 35 deletions(-) diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi index 51a48df2e27..0056906e1f7 100644 --- a/doc/emacs/programs.texi +++ b/doc/emacs/programs.texi @@ -868,6 +868,15 @@ highlighting also when point is in whitespace at the beginning of a line and there is a paren at the first or last non-whitespace position on the line, or when point is at the end of a line and there is a paren at the last non-whitespace position on the line. + +@item +@vindex show-paren-context-when-offscreen +@code{show-paren-context-when-offscreen}, when non-@code{nil}, shows +some context in the echo area when point is in a closing delimiter and +the opening delimiter is offscreen. The context is usually the line +that contains the opening delimiter, except if the opening delimiter +is on its own line, in which case the context includes the previous +nonblank line. @end itemize @cindex Electric Pair mode diff --git a/etc/NEWS b/etc/NEWS index d6188919155..f4b462516fa 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -86,6 +86,14 @@ effectively dragged. Customize this option to limit the amount of entries in the menu "Edit->Paste from Kill Menu". The default is 60. +** show-paren-mode + ++++ +*** New user option 'show-paren-context-when-offscreen'. +When non-nil, if the point is in a closing delimiter and the opening +delimiter is offscreen, shows some context around the opening +delimiter in the echo area. + * Changes in Specialized Modes and Packages in Emacs 29.1 diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el index a1c3c3268f2..b30d3fc30f4 100644 --- a/lisp/emacs-lisp/eldoc.el +++ b/lisp/emacs-lisp/eldoc.el @@ -380,7 +380,14 @@ Also store it in `eldoc-last-message' and return that value." ;; it undesirable to print eldoc messages right this instant. (defun eldoc-display-message-no-interference-p () "Return nil if displaying a message would cause interference." - (not (or executing-kbd-macro (bound-and-true-p edebug-active)))) + (not (or executing-kbd-macro + (bound-and-true-p edebug-active) + ;; The following configuration shows "Matches..." in the + ;; echo area when point is after a closing bracket, which + ;; conflicts with eldoc. + (and show-paren-context-when-offscreen + (not (pos-visible-in-window-p + (overlay-end show-paren--overlay))))))) (defvar eldoc-documentation-functions nil diff --git a/lisp/paren.el b/lisp/paren.el index ce6aa9ae13b..7e7cf6c262a 100644 --- a/lisp/paren.el +++ b/lisp/paren.el @@ -88,6 +88,14 @@ is not highlighted, the cursor being regarded as adequate to mark its position." :type 'boolean) +(defcustom show-paren-context-when-offscreen nil + "If non-nil, show context in the echo area when the openparen is offscreen. +The context is usually the line that contains the openparen, +except if the openparen is on its own line, in which case the +context includes the previous nonblank line." + :type 'boolean + :version "29.1") + (defvar show-paren--idle-timer nil) (defvar show-paren--overlay (let ((ol (make-overlay (point) (point) nil t))) (delete-overlay ol) ol) @@ -312,6 +320,19 @@ It is the default value of `show-paren-data-function'." (current-buffer)) (move-overlay show-paren--overlay there-beg there-end (current-buffer))) + ;; If `show-paren-open-line-when-offscreen' is t and point + ;; is at a close paren, show the line that contains the + ;; openparen in the echo area. + (let ((openparen (min here-beg there-beg))) + (if (and show-paren-context-when-offscreen + (< there-beg here-beg) + (not (pos-visible-in-window-p openparen))) + (let ((open-paren-line-string + (blink-paren-open-paren-line-string openparen)) + (message-log-max nil)) + (minibuffer-message + "Matches %s" + (substring-no-properties open-paren-line-string))))) ;; Always set the overlay face, since it varies. (overlay-put show-paren--overlay 'priority show-paren-priority) (overlay-put show-paren--overlay 'face face)))))) diff --git a/lisp/simple.el b/lisp/simple.el index c7bb928cd73..4f711d60ea2 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -8577,40 +8577,43 @@ The function should return non-nil if the two tokens do not match.") (current-buffer)) (sit-for blink-matching-delay)) (delete-overlay blink-matching--overlay))))) - (t - (let ((open-paren-line-string - (save-excursion - (goto-char blinkpos) - ;; Show what precedes the open in its line, if anything. - (cond - ((save-excursion (skip-chars-backward " \t") (not (bolp))) - (buffer-substring (line-beginning-position) - (1+ blinkpos))) - ;; Show what follows the open in its line, if anything. - ((save-excursion - (forward-char 1) - (skip-chars-forward " \t") - (not (eolp))) - (buffer-substring blinkpos - (line-end-position))) - ;; Otherwise show the previous nonblank line, - ;; if there is one. - ((save-excursion (skip-chars-backward "\n \t") (not (bobp))) - (concat - (buffer-substring (progn - (skip-chars-backward "\n \t") - (line-beginning-position)) - (progn (end-of-line) - (skip-chars-backward " \t") - (point))) - ;; Replace the newline and other whitespace with `...'. - "..." - (buffer-substring blinkpos (1+ blinkpos)))) - ;; There is nothing to show except the char itself. - (t (buffer-substring blinkpos (1+ blinkpos))))))) - (minibuffer-message - "Matches %s" - (substring-no-properties open-paren-line-string)))))))) + ((not show-paren-context-when-offscreen) + (minibuffer-message + "Matches %s" + (substring-no-properties + (blink-paren-open-paren-line-string blinkpos)))))))) + +(defun blink-paren-open-paren-line-string (pos) + "Return the line string that contains the openparen at POS." + (save-excursion + (goto-char pos) + ;; Show what precedes the open in its line, if anything. + (cond + ((save-excursion (skip-chars-backward " \t") (not (bolp))) + (buffer-substring (line-beginning-position) + (1+ pos))) + ;; Show what follows the open in its line, if anything. + ((save-excursion + (forward-char 1) + (skip-chars-forward " \t") + (not (eolp))) + (buffer-substring pos + (line-end-position))) + ;; Otherwise show the previous nonblank line, + ;; if there is one. + ((save-excursion (skip-chars-backward "\n \t") (not (bobp))) + (concat + (buffer-substring (progn + (skip-chars-backward "\n \t") + (line-beginning-position)) + (progn (end-of-line) + (skip-chars-backward " \t") + (point))) + ;; Replace the newline and other whitespace with `...'. + "..." + (buffer-substring pos (1+ pos)))) + ;; There is nothing to show except the char itself. + (t (buffer-substring pos (1+ pos)))))) (defvar blink-paren-function 'blink-matching-open "Function called, if non-nil, whenever a close parenthesis is inserted. diff --git a/test/lisp/paren-tests.el b/test/lisp/paren-tests.el index c4bec5d86de..11249ee9bc1 100644 --- a/test/lisp/paren-tests.el +++ b/test/lisp/paren-tests.el @@ -117,5 +117,36 @@ (- (point-max) 1) (point-max) nil))))) +(ert-deftest paren-tests-open-paren-line () + (cl-flet ((open-paren-line () + (let* ((data (show-paren--default)) + (here-beg (nth 0 data)) + (there-beg (nth 2 data))) + (blink-paren-open-paren-line-string + (min here-beg there-beg))))) + ;; Lisp-like + (with-temp-buffer + (insert "(defun foo () + (dummy))") + (goto-char (point-max)) + (should (string= "(defun foo ()" (open-paren-line)))) + + ;; C-like + (with-temp-buffer + (insert "int foo() { + int blah; + }") + (goto-char (point-max)) + (should (string= "int foo() {" (open-paren-line)))) + + ;; C-like with hanging { + (with-temp-buffer + (insert "int foo() + { + int blah; + }") + (goto-char (point-max)) + (should (string= "int foo()...{" (open-paren-line)))))) + (provide 'paren-tests) ;;; paren-tests.el ends here -- 2.39.2