unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Show show-paren context in a child frame
@ 2022-02-05 12:33 Tassilo Horn
  2022-02-05 18:00 ` Daniel Martín
  0 siblings, 1 reply; 5+ messages in thread
From: Tassilo Horn @ 2022-02-05 12:33 UTC (permalink / raw)
  To: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 782 bytes --]

Hi all,

today I've discovered `show-paren-context-when-offscreen' which displays
the context around the opening paren in the echo area.  That's a very
nice feature, however it competes with eldoc, i.e., it'll show the
context in the echo area and a fraction of a second later, that's
repressed by eldoc's text (or the other way round depending on the
delays one uses).

Therefore, I've experimented with adding a special `child-frame' value
for `show-paren-context-when-offscreen' which displays the context in a
child frame.  This works pretty well although setting up a child frame
with corresponding buffer for a kind of tooltip pane requires quite some
gymnastics in frame parameters and buffer-local variables which I've
copied from vertico.  Comments welcome!

Bye,
Tassilo


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Allow-showing-show-paren-context-in-child-frame.patch --]
[-- Type: text/x-patch, Size: 5723 bytes --]

From f3300f0cffd5344c013e3be0690f2ff1b50a1703 Mon Sep 17 00:00:00 2001
From: Tassilo Horn <tsdh@gnu.org>
Date: Sat, 5 Feb 2022 13:22:55 +0100
Subject: [PATCH] Allow showing show-paren context in child frame

---
 lisp/paren.el | 91 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 87 insertions(+), 4 deletions(-)

diff --git a/lisp/paren.el b/lisp/paren.el
index 0065bba72e..f8a56ddf24 100644
--- a/lisp/paren.el
+++ b/lisp/paren.el
@@ -260,6 +260,86 @@ show-paren--default
 		  (if (= dir 1) pos (1+ pos))
 		  mismatch)))))))
 
+(defvar show-paren--context-child-frame nil)
+
+(defun show-paren--context-child-frame-buffer (text)
+  (with-current-buffer
+      (get-buffer-create " *show-paren context*")
+    (dolist (var '((mode-line-format . nil)
+                   (header-line-format . nil)
+                   (tab-line-format . nil)
+                   (tab-bar-format . nil) ;; Emacs 28 tab-bar-format
+                   (frame-title-format . "")
+                   (truncate-lines . t)
+                   (cursor-in-non-selected-windows . nil)
+                   (cursor-type . nil)
+                   (show-trailing-whitespace . nil)
+                   (display-line-numbers . nil)
+                   (left-fringe-width . nil)
+                   (right-fringe-width . nil)
+                   (left-margin-width . 0)
+                   (right-margin-width . 0)
+                   (fringes-outside-margins . 0)
+                   (buffer-read-only . t)))
+      (set (make-local-variable (car var)) (cdr var)))
+    (let ((inhibit-modification-hooks t)
+          (inhibit-read-only t))
+      (erase-buffer)
+      (insert text)
+      (goto-char (point-min)))
+    (current-buffer)))
+
+(defun show-paren--show-context-in-child-frame (text)
+  (let ((minibuffer (minibuffer-window (window-frame)))
+        (buffer (show-paren--context-child-frame-buffer text))
+        (x (window-pixel-left))
+        (y (window-pixel-top)))
+    (show-paren--delete-context-child-frame)
+    (setq show-paren--context-child-frame
+          (make-frame
+           `((parent-frame . ,(window-frame))
+             (minibuffer . ,minibuffer)
+             (visibility . nil)
+             (width . 0)
+             (height . 0)
+             (no-accept-focus . t)
+             (no-focus-on-map . t)
+             (min-width . t)
+             (min-height . t)
+             (border-width . 0)
+             (child-frame-border-width . 1)
+             (left-fringe . 0)
+             (right-fringe . 0)
+             (vertical-scroll-bars . nil)
+             (horizontal-scroll-bars . nil)
+             (menu-bar-lines . 0)
+             (tool-bar-lines . 0)
+             (tab-bar-lines . 0)
+             (no-other-frame . t)
+             (no-other-window . t)
+             (no-delete-other-windows . t)
+             (unsplittable . t)
+             (undecorated . t)
+             (cursor-type . nil)
+             (no-special-glyphs . t)
+             (desktop-dont-save . t))))
+    (let ((win (frame-root-window show-paren--context-child-frame))
+          (window-min-height 0)
+          (window-min-width 0))
+      (set-window-buffer win buffer)
+      (set-window-dedicated-p win t)
+      (set-frame-size show-paren--context-child-frame
+                      (string-width text)
+                      (length (string-lines text)))
+      (set-frame-position show-paren--context-child-frame x y)
+      (make-frame-visible show-paren--context-child-frame)
+      (add-hook 'post-command-hook #'show-paren--delete-context-child-frame))))
+
+(defun show-paren--delete-context-child-frame ()
+  (when show-paren--context-child-frame
+    (delete-frame show-paren--context-child-frame))
+  (remove-hook 'post-command-hook #'show-paren--delete-context-child-frame))
+
 (defun show-paren-function ()
   "Highlight the parentheses until the next input arrives."
   (let ((data (and show-paren-mode (funcall show-paren-data-function))))
@@ -299,8 +379,8 @@ show-paren-function
         ;; Otherwise, turn off any such highlighting.
         (if (or (not here-beg)
                 (and (not show-paren-highlight-openparen)
-		     (> here-end (point))
-		     (<= here-beg (point))
+                     (> here-end (point))
+                     (<= here-beg (point))
                      (integerp there-beg)))
             (delete-overlay show-paren--overlay-1)
           (move-overlay show-paren--overlay-1
@@ -315,7 +395,7 @@ show-paren-function
             (delete-overlay show-paren--overlay)
           (if highlight-expression
               (move-overlay show-paren--overlay
-			    (if (< there-beg here-beg) here-end here-beg)
+                            (if (< there-beg here-beg) here-end here-beg)
                             (if (< there-beg here-beg) there-beg there-end)
                             (current-buffer))
             (move-overlay show-paren--overlay
@@ -330,7 +410,10 @@ show-paren-function
                 (let ((open-paren-line-string
                        (blink-paren-open-paren-line-string openparen))
                       (message-log-max nil))
-                  (minibuffer-message "Matches %s" open-paren-line-string))))
+                  (if (and (eq show-paren-context-when-offscreen 'child-frame)
+                           (display-graphic-p))
+                      (show-paren--show-context-in-child-frame open-paren-line-string)
+                    (minibuffer-message "Matches %s" 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))))))
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: Show show-paren context in a child frame
  2022-02-05 12:33 Show show-paren context in a child frame Tassilo Horn
@ 2022-02-05 18:00 ` Daniel Martín
  2022-02-05 19:03   ` Tassilo Horn
  0 siblings, 1 reply; 5+ messages in thread
From: Daniel Martín @ 2022-02-05 18:00 UTC (permalink / raw)
  To: Tassilo Horn; +Cc: emacs-devel

Tassilo Horn <tsdh@gnu.org> writes:

> Hi all,
>
> today I've discovered `show-paren-context-when-offscreen' which displays
> the context around the opening paren in the echo area.  That's a very
> nice feature, however it competes with eldoc, i.e., it'll show the
> context in the echo area and a fraction of a second later, that's
> repressed by eldoc's text (or the other way round depending on the
> delays one uses).

I'm glad you like the feature! Yes, unfortunately there are some cases
where this feature competes with Eldoc.  I taught
`eldoc-display-message-no-interference-p' about this new option, but
perhaphs the mechanism needs some more refinement.

>
> Therefore, I've experimented with adding a special `child-frame' value
> for `show-paren-context-when-offscreen' which displays the context in a
> child frame.  This works pretty well although setting up a child frame
> with corresponding buffer for a kind of tooltip pane requires quite some
> gymnastics in frame parameters and buffer-local variables which I've
> copied from vertico.  Comments welcome!

Thank you! I've given your code a try and I think it can be a useful
option.  Some comments:

- Is there a way to make the header more prominent? I feel it can be
  easily confused with the rest of the buffer.  Perhaps putting a thin
  border around the child frame might be enough.

- I've found an error when I clicked on the child frame by mistake and
  scrolled (I wanted to scroll the parent frame instead):

mouse-scroll-subr: Wrong type argument: window-live-p, #<window 386> [30
times]

Not sure if it's a bug in the child frame API or in the way you use it;
I'm not familiar enough with the child frame API.

- The way the child frame is positioned makes me think that using the
  header line would be more reliable and imply much less code than using
  a child frame.  I'd like to give a header line-based approach a try to
  see how it compares against using a child frame.  To make it a good
  citizen, we need to store the current contents of the header line and
  restore them when the point is not in a closing delimiter.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Show show-paren context in a child frame
  2022-02-05 18:00 ` Daniel Martín
@ 2022-02-05 19:03   ` Tassilo Horn
  2022-02-05 20:06     ` Tassilo Horn
  0 siblings, 1 reply; 5+ messages in thread
From: Tassilo Horn @ 2022-02-05 19:03 UTC (permalink / raw)
  To: Daniel Martín; +Cc: emacs-devel

Daniel Martín <mardani29@yahoo.es> writes:

Hi Daniel,

>> today I've discovered `show-paren-context-when-offscreen' which
>> displays the context around the opening paren in the echo area.
>> That's a very nice feature, however it competes with eldoc, i.e.,
>> it'll show the context in the echo area and a fraction of a second
>> later, that's repressed by eldoc's text (or the other way round
>> depending on the delays one uses).
>
> I'm glad you like the feature! Yes, unfortunately there are some cases
> where this feature competes with Eldoc.  I taught
> `eldoc-display-message-no-interference-p' about this new option, but
> perhaphs the mechanism needs some more refinement.

Ah, I see.  Shouldn't in the code

  (and (boundp 'show-paren-context-when-offscreen)
                show-paren-context-when-offscreen
                (not (pos-visible-in-window-p
                      (overlay-end show-paren--overlay))))

the `overlay-end' be `overlay-start'?  I mean the show-paren context
message is shown when the `show-paren--overlay's start is further up
than the window is showing, no?

>> Therefore, I've experimented with adding a special `child-frame'
>> value for `show-paren-context-when-offscreen' which displays the
>> context in a child frame.  This works pretty well although setting up
>> a child frame with corresponding buffer for a kind of tooltip pane
>> requires quite some gymnastics in frame parameters and buffer-local
>> variables which I've copied from vertico.  Comments welcome!
>
> Thank you! I've given your code a try and I think it can be a useful
> option.  Some comments:
>
> - Is there a way to make the header more prominent? I feel it can be
> easily confused with the rest of the buffer.  Perhaps putting a thin
> border around the child frame might be enough.

Yeah, just give the `child-frame-border' some :background.

> - I've found an error when I clicked on the child frame by mistake and
>   scrolled (I wanted to scroll the parent frame instead):
>
> mouse-scroll-subr: Wrong type argument: window-live-p, #<window 386> [30
> times]
>
> Not sure if it's a bug in the child frame API or in the way you use
> it; I'm not familiar enough with the child frame API.

Neither am I.  I stole most of the child-frame code from vertico which
adds some more hacks to redirect accidental focus from the child frame
back to the parent.  Seems like those are needed.

> - The way the child frame is positioned makes me think that using the
>   header line would be more reliable and imply much less code than using
>   a child frame.

Indeed, but the header-line has the drawback that it can only display
one line.  The context shown here can be two lines if the opening paren
is on its own line (like in c-ish languages).

> I'd like to give a header line-based approach a try to see how it
> compares against using a child frame.

Sure, give it a go! :-)

Bye,
Tassilo



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Show show-paren context in a child frame
  2022-02-05 19:03   ` Tassilo Horn
@ 2022-02-05 20:06     ` Tassilo Horn
  2022-02-06 10:18       ` Tassilo Horn
  0 siblings, 1 reply; 5+ messages in thread
From: Tassilo Horn @ 2022-02-05 20:06 UTC (permalink / raw)
  Cc: emacs-devel, Daniel Martín

[-- Attachment #1: Type: text/plain, Size: 711 bytes --]

Tassilo Horn <tsdh@gnu.org> writes:

>> mouse-scroll-subr: Wrong type argument: window-live-p, #<window 386> [30
>> times]
>>
>> Not sure if it's a bug in the child frame API or in the way you use
>> it; I'm not familiar enough with the child frame API.
>
> Neither am I.  I stole most of the child-frame code from vertico

Gosh, I've written vertico over and over.  Of course, it's *corfu*!  But
both are very good packages from the same author, so credits to Daniel
Mendler.

> which adds some more hacks to redirect accidental focus from the child
> frame back to the parent.  Seems like those are needed.

New version attached which should make it impossible to somehow focus
the child frame.

Bye,
Tassilo

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Allow-showing-show-paren-context-in-child-frame.patch --]
[-- Type: text/x-patch, Size: 7151 bytes --]

From 5a5e6b78d0926791f2593c79c3d9c017315b8979 Mon Sep 17 00:00:00 2001
From: Tassilo Horn <tsdh@gnu.org>
Date: Sat, 5 Feb 2022 13:22:55 +0100
Subject: [PATCH] Allow showing show-paren context in child frame

---
 lisp/paren.el | 124 +++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 117 insertions(+), 7 deletions(-)

diff --git a/lisp/paren.el b/lisp/paren.el
index 0065bba72e..d0c093a511 100644
--- a/lisp/paren.el
+++ b/lisp/paren.el
@@ -89,11 +89,19 @@ show-paren-highlight-openparen
   :type 'boolean)
 
 (defcustom show-paren-context-when-offscreen nil
-  "If non-nil, show context in the echo area when the openparen is offscreen.
+  "If non-nil, show context around the opening paren if it 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
+context includes the previous nonblank line.
+
+By default, the context is shown in the echo area.
+
+If set to the symbol `child-frame', the context is shown in a
+child frame at the left top of the window.  On non-graphical
+frames, show the context in the echo area."
+  :type '(choice (const :tag "Off" nil)
+                 (const :tag "In echo area" t)
+                 (const :tag "Child frame" child-frame))
   :version "29.1")
 
 (defvar show-paren--idle-timer nil)
@@ -260,6 +268,103 @@ show-paren--default
 		  (if (= dir 1) pos (1+ pos))
 		  mismatch)))))))
 
+(defvar show-paren--context-child-frame nil)
+
+(defun show-paren--context-child-frame-redirect-focus ()
+  "Redirect focus from child frame."
+  (redirect-frame-focus
+   show-paren--context-child-frame
+   (frame-parent corfu--frame)))
+
+(defun show-paren--context-child-frame-buffer (text)
+  (with-current-buffer
+      (get-buffer-create " *show-paren context*")
+    ;; Redirect focus to parent.
+    (add-hook 'pre-command-hook
+              #'show-paren--delete-context-child-frame
+              nil t)
+    ;; Use an empty keymap.
+    (use-local-map (make-keymap))
+    (dolist (var '((mode-line-format . nil)
+                   (header-line-format . nil)
+                   (tab-line-format . nil)
+                   (tab-bar-format . nil) ;; Emacs 28 tab-bar-format
+                   (frame-title-format . "")
+                   (truncate-lines . t)
+                   (cursor-in-non-selected-windows . nil)
+                   (cursor-type . nil)
+                   (show-trailing-whitespace . nil)
+                   (display-line-numbers . nil)
+                   (left-fringe-width . nil)
+                   (right-fringe-width . nil)
+                   (left-margin-width . 0)
+                   (right-margin-width . 0)
+                   (fringes-outside-margins . 0)
+                   (buffer-read-only . t)))
+      (set (make-local-variable (car var)) (cdr var)))
+    (let ((inhibit-modification-hooks t)
+          (inhibit-read-only t))
+      (erase-buffer)
+      (insert text)
+      (goto-char (point-min)))
+    (current-buffer)))
+
+(defvar show-paren--context-child-frame-parameters
+  `((visibility . nil)
+    (width . 0) (height . 0)
+    (min-width . t) (min-height . t)
+    (no-accept-focus . t)
+    (no-focus-on-map . t)
+    (border-width . 0)
+    (child-frame-border-width . 1)
+    (left-fringe . 0)
+    (right-fringe . 0)
+    (vertical-scroll-bars . nil)
+    (horizontal-scroll-bars . nil)
+    (menu-bar-lines . 0)
+    (tool-bar-lines . 0)
+    (tab-bar-lines . 0)
+    (no-other-frame . t)
+    (no-other-window . t)
+    (no-delete-other-windows . t)
+    (unsplittable . t)
+    (undecorated . t)
+    (cursor-type . nil)
+    (no-special-glyphs . t)
+    (desktop-dont-save . t)))
+
+(defun show-paren--delete-context-child-frame ()
+  (when show-paren--context-child-frame
+    (delete-frame show-paren--context-child-frame))
+  (remove-hook 'post-command-hook
+               #'show-paren--delete-context-child-frame))
+
+(defun show-paren--show-context-in-child-frame (text)
+  "Show TEXT in a child-frame at the top-left of the current window."
+  (let ((minibuffer (minibuffer-window (window-frame)))
+        (buffer (show-paren--context-child-frame-buffer text))
+        (x (window-pixel-left))
+        (y (window-pixel-top))
+        (window-min-height 1)
+        (window-min-width 1)
+        after-make-frame-functions)
+    (show-paren--delete-context-child-frame)
+    (setq show-paren--context-child-frame
+          (make-frame
+           `((parent-frame . ,(window-frame))
+             (minibuffer . ,minibuffer)
+             ,@show-paren--context-child-frame-parameters)))
+    (let ((win (frame-root-window show-paren--context-child-frame)))
+      (set-window-buffer win buffer)
+      (set-window-dedicated-p win t)
+      (set-frame-size show-paren--context-child-frame
+                      (string-width text)
+                      (length (string-lines text)))
+      (set-frame-position show-paren--context-child-frame x y)
+      (make-frame-visible show-paren--context-child-frame)
+      (add-hook 'post-command-hook
+                #'show-paren--delete-context-child-frame))))
+
 (defun show-paren-function ()
   "Highlight the parentheses until the next input arrives."
   (let ((data (and show-paren-mode (funcall show-paren-data-function))))
@@ -299,8 +404,8 @@ show-paren-function
         ;; Otherwise, turn off any such highlighting.
         (if (or (not here-beg)
                 (and (not show-paren-highlight-openparen)
-		     (> here-end (point))
-		     (<= here-beg (point))
+                     (> here-end (point))
+                     (<= here-beg (point))
                      (integerp there-beg)))
             (delete-overlay show-paren--overlay-1)
           (move-overlay show-paren--overlay-1
@@ -315,7 +420,7 @@ show-paren-function
             (delete-overlay show-paren--overlay)
           (if highlight-expression
               (move-overlay show-paren--overlay
-			    (if (< there-beg here-beg) here-end here-beg)
+                            (if (< there-beg here-beg) here-end here-beg)
                             (if (< there-beg here-beg) there-beg there-end)
                             (current-buffer))
             (move-overlay show-paren--overlay
@@ -330,7 +435,12 @@ show-paren-function
                 (let ((open-paren-line-string
                        (blink-paren-open-paren-line-string openparen))
                       (message-log-max nil))
-                  (minibuffer-message "Matches %s" open-paren-line-string))))
+                  (if (and (eq show-paren-context-when-offscreen
+                               'child-frame)
+                           (display-graphic-p))
+                      (show-paren--show-context-in-child-frame
+                       open-paren-line-string)
+                    (minibuffer-message "Matches %s" 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))))))
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: Show show-paren context in a child frame
  2022-02-05 20:06     ` Tassilo Horn
@ 2022-02-06 10:18       ` Tassilo Horn
  0 siblings, 0 replies; 5+ messages in thread
From: Tassilo Horn @ 2022-02-06 10:18 UTC (permalink / raw)
  Cc: emacs-devel, Daniel Martín

Tassilo Horn <tsdh@gnu.org> writes:

> New version attached which should make it impossible to somehow focus
> the child frame.

I've now installed this after some more refactoring and with appropriate
ChangeLog style commit message.  I've also extended your NEWS entry.
The defcustom is now a (choice...).  Feel free to add the header-line
approach as well.

I'm currently thinking about how one could make the context a bit more
suitable for non-lisp languages in a mostly language-agnostic way.
Basically, it would be nice if with

  if (foobar
      && !baz)
    {
      ...
    }<== point here

you'd see the complete "if (...)" instead of only "%% !baz)...{".

Bye,
Tassilo



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-02-06 10:18 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-05 12:33 Show show-paren context in a child frame Tassilo Horn
2022-02-05 18:00 ` Daniel Martín
2022-02-05 19:03   ` Tassilo Horn
2022-02-05 20:06     ` Tassilo Horn
2022-02-06 10:18       ` Tassilo Horn

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).