unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Proposed extension of show-paren-mode:  Highlight parens when point is in L or R margin.
@ 2014-10-11 13:43 Alan Mackenzie
  2014-10-11 14:21 ` Eli Zaretskii
                   ` (2 more replies)
  0 siblings, 3 replies; 23+ messages in thread
From: Alan Mackenzie @ 2014-10-11 13:43 UTC (permalink / raw)
  To: emacs-devel

Hi, Emacs.

This is an idea which struck me when I was reading CC Mode code.  I find
myself frequently scrolling through it, and frequently wanting to know
where the matching paren for one at beginning of indentation is.  That
involved me typing M-m an awful lot, and I've got tired of doing that.

So: If point is in the LH margin of the code, highlight the first paren
on the line and its match, or failing that, the last paren on the line
with its match.  Then again, why not do the same if point is in a line
comment?

Here is a patch to .../lisp/paren.el which implements this.  To turn on
the new feature, set the new customizable variable
show-paren-when-point-in-margin to t.

Comments on this proposed new feature?




=== modified file 'lisp/paren.el'
*** lisp/paren.el	2014-02-10 01:34:22 +0000
--- lisp/paren.el	2014-10-11 13:34:19 +0000
***************
*** 72,77 ****
--- 72,85 ----
    :group 'paren-showing
    :version "20.3")
  
+ (defcustom show-paren-when-point-in-margin nil
+   "If non-nil, try to show parens when point is in LH or RH margin.
+ LH margin is before the first non-space/tab character on the line.
+ The RH margin is in a comment or whitespace towards the end of a line."
+   :type 'boolean
+   :group 'paren-showing
+   :version "25.1")
+ 
  (define-obsolete-face-alias 'show-paren-match-face 'show-paren-match "22.1")
  
  (define-obsolete-face-alias 'show-paren-mismatch-face
***************
*** 120,141 ****
  Where HERE-BEG..HERE-END is expected to be around point.")
  
  (defun show-paren--default ()
!   (let* ((oldpos (point))
!          (dir (cond ((eq (syntax-class (syntax-after (1- (point)))) 5) -1)
!                     ((eq (syntax-class (syntax-after (point)))      4) 1)))
!          (unescaped
!           (when dir
!             ;; Verify an even number of quoting characters precede the paren.
!             ;; Follow the same logic as in `blink-matching-open'.
!             (= (if (= dir -1) 1 0)
!                (logand 1 (- (point)
!                             (save-excursion
!                               (if (= dir -1) (forward-char -1))
!                               (skip-syntax-backward "/\\")
!                               (point)))))))
!          (here-beg (if (eq dir 1) (point) (1- (point))))
!          (here-end (if (eq dir 1) (1+ (point)) (point)))
!          pos mismatch)
      ;;
      ;; Find the other end of the sexp.
      (when unescaped
--- 128,187 ----
  Where HERE-BEG..HERE-END is expected to be around point.")
  
  (defun show-paren--default ()
!   (let* ((ind-pos (save-excursion (back-to-indentation) (point)))
! 	 (bol-pos (save-excursion (beginning-of-line) (point)))
! 	 (eol-pos (save-excursion (end-of-line)
! 				  (let ((s (syntax-ppss)))
! 				    (if (nth 4 s)
! 					(goto-char (max (nth 8 s)
! 							(point-min))))
! 				    (skip-chars-backward " \t"))
! 				  (point)))
! 	 (oldpos (point))
! 	 dir unescaped pos mismatch here-beg here-end)
!     (cond
!      ;; Point is at a paren.
!      ((eq (syntax-class (syntax-after (1- (point)))) 5)
!       (setq dir -1))
!      ((eq (syntax-class (syntax-after (point))) 4)
!       (setq dir 1))
!      ;; Point is in the LH margin.
!      ((and show-paren-when-point-in-margin
! 	   (< (point) ind-pos))
!       (cond
!        ((eq (syntax-class (syntax-after ind-pos)) 4)
! 	(setq dir 1
! 	      oldpos ind-pos))
!        ((eq (syntax-class (syntax-after ind-pos)) 5)
! 	(setq dir -1
! 	      oldpos (1+ ind-pos)))
!        ((eq (syntax-class (syntax-after (1- eol-pos))) 4)
! 	(setq dir 1
! 	      oldpos (1- eol-pos)))
!        ((eq (syntax-class (syntax-after (1- eol-pos))) 5)
! 	(setq dir -1
! 	      oldpos eol-pos))))
!      ;; Point is in a comment or whitespace to the right of the line.
!      ((and show-paren-when-point-in-margin
! 	   (>= (point) eol-pos))
!       (cond
!        ((eq (syntax-class (syntax-after (1- eol-pos))) 4)
! 	(setq dir 1
! 	      oldpos (1- eol-pos)))
!        ((eq (syntax-class (syntax-after (1- eol-pos))) 5)
! 	(setq dir -1
! 	      oldpos eol-pos)))))
!     (when dir
!       (setq unescaped
! 	    (= (if (= dir -1) 1 0)
! 	       (logand 1 (- oldpos
! 			    (save-excursion
! 			      (goto-char oldpos)
! 			      (if (= dir -1) (backward-char))
! 			      (skip-syntax-backward "/\\")
! 			      (point)))))))
!     (setq here-beg (if (eq dir 1) oldpos (1- oldpos))
!           here-end (if (eq dir 1) (1+ oldpos) oldpos))
      ;;
      ;; Find the other end of the sexp.
      (when unescaped
***************
*** 149,155 ****
            ;; Scan across one sexp within that range.
            ;; Errors or nil mean there is a mismatch.
            (condition-case ()
!               (setq pos (scan-sexps (point) dir))
              (error (setq pos t mismatch t)))
            ;; Move back the other way and verify we get back to the
            ;; starting point.  If not, these two parens don't really match.
--- 195,201 ----
            ;; Scan across one sexp within that range.
            ;; Errors or nil mean there is a mismatch.
            (condition-case ()
!               (setq pos (scan-sexps oldpos dir))
              (error (setq pos t mismatch t)))
            ;; Move back the other way and verify we get back to the
            ;; starting point.  If not, these two parens don't really match.
***************
*** 157,163 ****
            ;; or one is inside a comment.
            (when (integerp pos)
              (unless (condition-case ()
!                         (eq (point) (scan-sexps pos (- dir)))
                        (error nil))
                (setq pos nil)))
            ;; If found a "matching" paren, see if it is the right
--- 203,209 ----
            ;; or one is inside a comment.
            (when (integerp pos)
              (unless (condition-case ()
!                         (eq oldpos (scan-sexps pos (- dir)))
                        (error nil))
                (setq pos nil)))
            ;; If found a "matching" paren, see if it is the right
***************
*** 215,221 ****
          ;; Otherwise, turn off any such highlighting.
          (if (or (not here-beg)
                  (and (not show-paren-highlight-openparen)
!                      (> here-end (point))
                       (integerp there-beg)))
              (delete-overlay show-paren--overlay-1)
            (move-overlay show-paren--overlay-1
--- 261,267 ----
          ;; Otherwise, turn off any such highlighting.
          (if (or (not here-beg)
                  (and (not show-paren-highlight-openparen)
! 		     (= here-end (1+ (point)))
                       (integerp there-beg)))
              (delete-overlay show-paren--overlay-1)
            (move-overlay show-paren--overlay-1



-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-11 13:43 Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin Alan Mackenzie
@ 2014-10-11 14:21 ` Eli Zaretskii
  2014-10-12  8:39   ` Alan Mackenzie
  2014-10-12  4:12 ` Stefan Monnier
  2014-10-14 21:50 ` João Távora
  2 siblings, 1 reply; 23+ messages in thread
From: Eli Zaretskii @ 2014-10-11 14:21 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

> Date: Sat, 11 Oct 2014 13:43:12 +0000
> From: Alan Mackenzie <acm@muc.de>
> 
> So: If point is in the LH margin of the code, highlight the first paren
> on the line and its match, or failing that, the last paren on the line
> with its match.

Thanks, but PLEASE don't call this a "margin".  We have already too
many overloaded meanings of this term, so let's avoid adding yet
another one.



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-11 13:43 Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin Alan Mackenzie
  2014-10-11 14:21 ` Eli Zaretskii
@ 2014-10-12  4:12 ` Stefan Monnier
  2014-10-12 10:04   ` Alan Mackenzie
  2014-10-14 21:50 ` João Távora
  2 siblings, 1 reply; 23+ messages in thread
From: Stefan Monnier @ 2014-10-12  4:12 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

> So: If point is in the LH margin of the code, highlight the first paren
> on the line and its match, or failing that, the last paren on the line
> with its match.

Related ideas:
- show matching paren when point is right after the open paren or right
  before the close paren.  (see
  http://stackoverflow.com/questions/25648067/emacs-matching-parenthesis-when-cursor-is-on-closing-parenthesis
  for a 5-liner which does that).

- show matching paren, when there's only whitespace between point and
  the open/close paren.

This second idea is a superset of the one you suggest.  I personally
don't use show-paren-mode because I find it distracting, so maybe
a superset would be too distracting.

> Then again, why not do the same if point is in a line comment?

Sorry, I don't know what "in a line comment" means.

As for your patch, I'd rather see the new code moved to a new function.
See more detailed comments below.


        Stefan


> + (defcustom show-paren-when-point-in-margin nil

Please don't call it "margin".

> !     (cond
> !      ;; Point is at a paren.
> !      ((eq (syntax-class (syntax-after (1- (point)))) 5)
> !       (setq dir -1))
> !      ((eq (syntax-class (syntax-after (point))) 4)
> !       (setq dir 1))
> !      ;; Point is in the LH margin.
> !      ((and show-paren-when-point-in-margin
> !        (< (point) ind-pos))
> !       (cond
> !        ((eq (syntax-class (syntax-after ind-pos)) 4)
> !         (setq dir 1
> !               oldpos ind-pos))
> !        ((eq (syntax-class (syntax-after ind-pos)) 5)
> !         (setq dir -1
> !               oldpos (1+ ind-pos)))
> !        ((eq (syntax-class (syntax-after (1- eol-pos))) 4)
> !         (setq dir 1
> !               oldpos (1- eol-pos)))
> !        ((eq (syntax-class (syntax-after (1- eol-pos))) 5)
> !         (setq dir -1
> !               oldpos eol-pos))))
> !      ;; Point is in a comment or whitespace to the right of the line.
> !      ((and show-paren-when-point-in-margin
> !        (>= (point) eol-pos))
> !       (cond
> !        ((eq (syntax-class (syntax-after (1- eol-pos))) 4)
> !         (setq dir 1
> !               oldpos (1- eol-pos)))
> !        ((eq (syntax-class (syntax-after (1- eol-pos))) 5)
> !         (setq dir -1
> !               oldpos eol-pos)))))

It's not at all clear to me why there has to be so many different cases
(IOW we'd need a comment that explains why we need such complexity).

> !     (when dir
> !       (setq unescaped
> ! 	    (= (if (= dir -1) 1 0)
> ! 	       (logand 1 (- oldpos
> ! 			    (save-excursion
> ! 			      (goto-char oldpos)
> ! 			      (if (= dir -1) (backward-char))
> ! 			      (skip-syntax-backward "/\\")
> ! 			      (point)))))))

This is a code duplication.  Please move it to a separate helper function.


        Stefan



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-11 14:21 ` Eli Zaretskii
@ 2014-10-12  8:39   ` Alan Mackenzie
  2014-10-12  8:55     ` David Kastrup
  2014-10-12  9:01     ` Eli Zaretskii
  0 siblings, 2 replies; 23+ messages in thread
From: Alan Mackenzie @ 2014-10-12  8:39 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Hi, Eli.

On Sat, Oct 11, 2014 at 05:21:24PM +0300, Eli Zaretskii wrote:

> > So: If point is in the LH margin of the code, highlight the first paren
> > on the line and its match, or failing that, the last paren on the line
> > with its match.

> Thanks, but PLEASE don't call this a "margin".

OK, not "margin".  But what, then?  I tried for some time to come up
with something better that would still fit into a ~78 character first
line of the doc string for the new customisable variable.  So far, I've
had this:

"If non-nil, try to show parens when point is in LH or RH margin."

Maybe I could omit the "try to ".  Maybe I should.  How about something
like:

"If non-nil, show parens when point is before or after the line's code."

?  It's a bit clumsy, though.

As for the name "show-paren-when-point-in-margin", that would have to
become "show-parens-when-point-outside-code", or something, which isn't
quite accurate (sometimes, short comments are inside a line of code),
and isn't very nmonic.

> We have already too many overloaded meanings of this term, so let's
> avoid adding yet another one.

Hmm.  Or, perhaps we could just admit this, and allow "margin" to be
used loosely whenever appropriate.  ;-).

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-12  8:39   ` Alan Mackenzie
@ 2014-10-12  8:55     ` David Kastrup
  2014-10-12  9:25       ` Alan Mackenzie
  2014-10-12  9:01     ` Eli Zaretskii
  1 sibling, 1 reply; 23+ messages in thread
From: David Kastrup @ 2014-10-12  8:55 UTC (permalink / raw)
  To: emacs-devel

Alan Mackenzie <acm@muc.de> writes:

> Hi, Eli.
>
> On Sat, Oct 11, 2014 at 05:21:24PM +0300, Eli Zaretskii wrote:
>
>> > So: If point is in the LH margin of the code, highlight the first paren
>> > on the line and its match, or failing that, the last paren on the line
>> > with its match.
>
>> Thanks, but PLEASE don't call this a "margin".
>
> OK, not "margin".  But what, then?

"wrappings" ?  "envelope"? "perimeter"?

> Hmm.  Or, perhaps we could just admit this, and allow "margin" to be
> used loosely whenever appropriate.  ;-).

Either way, you'll need to explain more thoroughly in the following
paragraph.

-- 
David Kastrup




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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-12  8:39   ` Alan Mackenzie
  2014-10-12  8:55     ` David Kastrup
@ 2014-10-12  9:01     ` Eli Zaretskii
  2014-10-12 10:18       ` Alan Mackenzie
  1 sibling, 1 reply; 23+ messages in thread
From: Eli Zaretskii @ 2014-10-12  9:01 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

> Date: Sun, 12 Oct 2014 08:39:09 +0000
> Cc: emacs-devel@gnu.org
> From: Alan Mackenzie <acm@muc.de>
> 
> > Thanks, but PLEASE don't call this a "margin".
> 
> OK, not "margin".  But what, then?  I tried for some time to come up
> with something better that would still fit into a ~78 character first
> line of the doc string for the new customisable variable.  So far, I've
> had this:
> 
> "If non-nil, try to show parens when point is in LH or RH margin."

This does not really fit into 78 characters, since LH and RH are
entirely alien notions.

I was about to suggest

 "If non-nil, highlight parens when point is inside the indentation."

but that doesn't cover the "RH" part (which I personally find a weird
feature, but that's me).

> Maybe I could omit the "try to ".

Definitely; it doesn't add anything.

> How about something like:
> 
> "If non-nil, show parens when point is before or after the line's code."

Works for me.

> As for the name "show-paren-when-point-in-margin", that would have to
> become "show-parens-when-point-outside-code", or something, which isn't
> quite accurate (sometimes, short comments are inside a line of code),
> and isn't very nmonic.

We don't need absolute accuracy if it requires too long names, IMO.

> > We have already too many overloaded meanings of this term, so let's
> > avoid adding yet another one.
> 
> Hmm.  Or, perhaps we could just admit this, and allow "margin" to be
> used loosely whenever appropriate.  ;-).

No, please let's not.

Now, let me ask something about the feature as designed (sorry, don't
have time right now to apply the patch and try this myself).  Suppose
I have this line of C code:

    FOO = xyz + foobar (a + (b * sqrt (c) - d) * e) - some; /* foo */

Is the intent to have the parentheses of the call to 'foobar'
highlighted when point is before "FOO" or inside the comment, but
_not_ when point is between "xyz" and "foobar"?  If so, this is soooo
weird!

And what about this part of your description:

> So: If point is in the LH margin of the code, highlight the first paren
> on the line and its match, or failing that, the last paren on the line
> with its match.               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

When would the "first paren and its match" fail?  Does this mean you
are not going to look past the line with point, i.e. multi-line
parenthesized expressions will not be highlighted?



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-12  8:55     ` David Kastrup
@ 2014-10-12  9:25       ` Alan Mackenzie
  0 siblings, 0 replies; 23+ messages in thread
From: Alan Mackenzie @ 2014-10-12  9:25 UTC (permalink / raw)
  To: David Kastrup; +Cc: emacs-devel

Hello, David.

On Sun, Oct 12, 2014 at 10:55:41AM +0200, David Kastrup wrote:
> Alan Mackenzie <acm@muc.de> writes:

> > Hi, Eli.

> > On Sat, Oct 11, 2014 at 05:21:24PM +0300, Eli Zaretskii wrote:

> >> > So: If point is in the LH margin of the code, highlight the first paren
> >> > on the line and its match, or failing that, the last paren on the line
> >> > with its match.

> >> Thanks, but PLEASE don't call this a "margin".

> > OK, not "margin".  But what, then?

> "wrappings" ?  "envelope"? "perimeter"?

No.  "Periphery" is surely the right word.  Thanks for the prompt!

> > Hmm.  Or, perhaps we could just admit this, and allow "margin" to be
> > used loosely whenever appropriate.  ;-).

> Either way, you'll need to explain more thoroughly in the following
> paragraph.

I think I've done this.  Watch out for my reply to Stefan, where I'll've
incorporated the change into my patch.

> -- 
> David Kastrup

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-12  4:12 ` Stefan Monnier
@ 2014-10-12 10:04   ` Alan Mackenzie
  2014-10-14 17:49     ` Stefan Monnier
  0 siblings, 1 reply; 23+ messages in thread
From: Alan Mackenzie @ 2014-10-12 10:04 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hi, Stefan.

On Sun, Oct 12, 2014 at 12:12:12AM -0400, Stefan Monnier wrote:
> > So: If point is in the LH margin of the code, highlight the first paren
> > on the line and its match, or failing that, the last paren on the line
> > with its match.

> Related ideas:
> - show matching paren when point is right after the open paren or right
>   before the close paren.  (see
>   http://stackoverflow.com/questions/25648067/emacs-matching-parenthesis-when-cursor-is-on-closing-parenthesis
>   for a 5-liner which does that).

> - show matching paren, when there's only whitespace between point and
>   the open/close paren.

Maybe for later!

> This second idea is a superset of the one you suggest.  I personally
> don't use show-paren-mode because I find it distracting, so maybe
> a superset would be too distracting.

> > Then again, why not do the same if point is in a line comment?

> Sorry, I don't know what "in a line comment" means.

In a comment which extends to EOL, e.g. "//..." in C++, or ";..." in
Lisp.

> As for your patch, I'd rather see the new code moved to a new function.

Done.

> See more detailed comments below.


> > + (defcustom show-paren-when-point-in-margin nil

> Please don't call it "margin".

OK.  It's now called "periphery".

[ .... ]

> It's not at all clear to me why there has to be so many different cases
> (IOW we'd need a comment that explains why we need such complexity).

I've condensed these cases into a new function (see amended patch below).

[ .... ]

> This is a code duplication.  Please move it to a separate helper function.

No it's not.  It appears just once, after my patch is applied.

Here's the amended version, incorporating suggestions from you, Eli, and
David.




=== modified file 'lisp/paren.el'
*** lisp/paren.el	2014-02-10 01:34:22 +0000
--- lisp/paren.el	2014-10-12 09:46:33 +0000
***************
*** 72,77 ****
--- 72,85 ----
    :group 'paren-showing
    :version "20.3")
  
+ (defcustom show-paren-when-point-in-periphery nil
+   "If non-nil, show parens when point is in the line's periphery.
+ The periphery is either the whitespace at the beginning of a
+ line, or a comment \(or whitespace) at the end of a line."
+   :type 'boolean
+   :group 'paren-showing
+   :version "25.1")
+ 
  (define-obsolete-face-alias 'show-paren-match-face 'show-paren-match "22.1")
  
  (define-obsolete-face-alias 'show-paren-mismatch-face
***************
*** 112,117 ****
--- 120,138 ----
      (delete-overlay show-paren--overlay)
      (delete-overlay show-paren--overlay-1)))
  
+ (defun show-paren--categorize-paren (pos)
+   "Determine whether the character after POS has paren syntax,
+ and if so, return a cons (DIR . OUTSIDE).  For an open paren, DIR
+ is 1 and OUTSIDE is the position before the paren.  For a close
+ paren, DIR is -1 and OUTSIDE is the position after the paren.  If
+ the character isn't a paren, return nil."
+   (cond
+    ((eq (syntax-class (syntax-after pos)) 4)
+     (cons 1 pos))
+    ((eq (syntax-class (syntax-after pos)) 5)
+     (cons -1 (1+ pos)))
+    (t nil)))
+ 
  (defvar show-paren-data-function #'show-paren--default
    "Function to find the opener/closer at point and its match.
  The function is called with no argument and should return either nil
***************
*** 120,141 ****
  Where HERE-BEG..HERE-END is expected to be around point.")
  
  (defun show-paren--default ()
!   (let* ((oldpos (point))
!          (dir (cond ((eq (syntax-class (syntax-after (1- (point)))) 5) -1)
!                     ((eq (syntax-class (syntax-after (point)))      4) 1)))
!          (unescaped
!           (when dir
!             ;; Verify an even number of quoting characters precede the paren.
!             ;; Follow the same logic as in `blink-matching-open'.
!             (= (if (= dir -1) 1 0)
!                (logand 1 (- (point)
!                             (save-excursion
!                               (if (= dir -1) (forward-char -1))
!                               (skip-syntax-backward "/\\")
!                               (point)))))))
!          (here-beg (if (eq dir 1) (point) (1- (point))))
!          (here-end (if (eq dir 1) (1+ (point)) (point)))
!          pos mismatch)
      ;;
      ;; Find the other end of the sexp.
      (when unescaped
--- 141,190 ----
  Where HERE-BEG..HERE-END is expected to be around point.")
  
  (defun show-paren--default ()
!   (let* ((ind-pos (save-excursion (back-to-indentation) (point)))
! 	 (bol-pos (save-excursion (beginning-of-line) (point)))
! 	 (eol-pos (save-excursion (end-of-line)
! 				  (let ((s (syntax-ppss)))
! 				    (if (nth 4 s)
! 					(goto-char (max (nth 8 s)
! 							(point-min))))
! 				    (skip-chars-backward " \t"))
! 				  (point)))
! 	 (oldpos (point))
! 	 dir paren-details unescaped pos mismatch here-beg here-end)
!     (cond
!      ;; Point is at a paren.
!      ((eq (syntax-class (syntax-after (1- (point)))) 5)
!       (setq dir -1))
!      ((eq (syntax-class (syntax-after (point))) 4)
!       (setq dir 1))
!      ;; Point is in the WS before the code.
!      ((and show-paren-when-point-in-periphery
! 	   (< (point) ind-pos))
!       (setq paren-details
! 	    (or (show-paren--categorize-paren ind-pos)
! 		(show-paren--categorize-paren (1- eol-pos)))))
!      ;; Point is in a comment or whitespace after the code.
!      ((and show-paren-when-point-in-periphery
! 	   (>= (point) eol-pos))
!       (setq paren-details
! 	    (or (show-paren--categorize-paren (1- eol-pos))
! 		(show-paren--categorize-paren ind-pos)))))
!     (when paren-details
!       (setq dir (car paren-details)
! 	    oldpos (cdr paren-details)))
! 
!     (when dir
!       (setq unescaped
! 	    (= (if (= dir -1) 1 0)
! 	       (logand 1 (- oldpos
! 			    (save-excursion
! 			      (goto-char oldpos)
! 			      (if (= dir -1) (backward-char))
! 			      (skip-syntax-backward "/\\")
! 			      (point)))))))
!     (setq here-beg (if (eq dir 1) oldpos (1- oldpos))
!           here-end (if (eq dir 1) (1+ oldpos) oldpos))
      ;;
      ;; Find the other end of the sexp.
      (when unescaped
***************
*** 149,155 ****
            ;; Scan across one sexp within that range.
            ;; Errors or nil mean there is a mismatch.
            (condition-case ()
!               (setq pos (scan-sexps (point) dir))
              (error (setq pos t mismatch t)))
            ;; Move back the other way and verify we get back to the
            ;; starting point.  If not, these two parens don't really match.
--- 198,204 ----
            ;; Scan across one sexp within that range.
            ;; Errors or nil mean there is a mismatch.
            (condition-case ()
!               (setq pos (scan-sexps oldpos dir))
              (error (setq pos t mismatch t)))
            ;; Move back the other way and verify we get back to the
            ;; starting point.  If not, these two parens don't really match.
***************
*** 157,163 ****
            ;; or one is inside a comment.
            (when (integerp pos)
              (unless (condition-case ()
!                         (eq (point) (scan-sexps pos (- dir)))
                        (error nil))
                (setq pos nil)))
            ;; If found a "matching" paren, see if it is the right
--- 206,212 ----
            ;; or one is inside a comment.
            (when (integerp pos)
              (unless (condition-case ()
!                         (eq oldpos (scan-sexps pos (- dir)))
                        (error nil))
                (setq pos nil)))
            ;; If found a "matching" paren, see if it is the right
***************
*** 215,221 ****
          ;; Otherwise, turn off any such highlighting.
          (if (or (not here-beg)
                  (and (not show-paren-highlight-openparen)
!                      (> here-end (point))
                       (integerp there-beg)))
              (delete-overlay show-paren--overlay-1)
            (move-overlay show-paren--overlay-1
--- 264,270 ----
          ;; Otherwise, turn off any such highlighting.
          (if (or (not here-beg)
                  (and (not show-paren-highlight-openparen)
! 		     (= here-beg (point))
                       (integerp there-beg)))
              (delete-overlay show-paren--overlay-1)
            (move-overlay show-paren--overlay-1



>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-12  9:01     ` Eli Zaretskii
@ 2014-10-12 10:18       ` Alan Mackenzie
  0 siblings, 0 replies; 23+ messages in thread
From: Alan Mackenzie @ 2014-10-12 10:18 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Hi, Eli.

On Sun, Oct 12, 2014 at 12:01:58PM +0300, Eli Zaretskii wrote:
> > Date: Sun, 12 Oct 2014 08:39:09 +0000
> > Cc: emacs-devel@gnu.org
> > From: Alan Mackenzie <acm@muc.de>

> > > Thanks, but PLEASE don't call this a "margin".

[ ... ]

I've now called it "periphery" and for that doc-string first line I've
got:

"If non-nil, show parens when point is in the line's periphery."

[ ... ]

> Now, let me ask something about the feature as designed (sorry, don't
> have time right now to apply the patch and try this myself).  Suppose
> I have this line of C code:

>     FOO = xyz + foobar (a + (b * sqrt (c) - d) * e) - some; /* foo */

> Is the intent to have the parentheses of the call to 'foobar'
> highlighted when point is before "FOO" or inside the comment, but
> _not_ when point is between "xyz" and "foobar"?  If so, this is soooo
> weird!

No.  The only parens that get highlighted are ones at the beginning or
end of the code on the line (together with their partners).

> And what about this part of your description:

> > So: If point is in the LH margin of the code, highlight the first paren
> > on the line and its match, or failing that, the last paren on the line
> > with its match.               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

> When would the "first paren and its match" fail?

When the first non-whitespace character on the line isn't a paren.

> Does this mean you are not going to look past the line with point, i.e.
> multi-line parenthesized expressions will not be highlighted?

They will be highlighted when one of the parens touches the line's
periphery; it doesn't matter how far away the matching paren is, it will
get highlighted.

Anyhow, I've just blasted off an amended patch to Stefan.  Maybe it's
almost all right now.

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-12 10:04   ` Alan Mackenzie
@ 2014-10-14 17:49     ` Stefan Monnier
  2014-10-14 18:32       ` John Yates
  2014-10-15  9:12       ` Alan Mackenzie
  0 siblings, 2 replies; 23+ messages in thread
From: Stefan Monnier @ 2014-10-14 17:49 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

>> > Then again, why not do the same if point is in a line comment?
>> Sorry, I don't know what "in a line comment" means.
> In a comment which extends to EOL, e.g. "//..." in C++, or ";..." in
> Lisp.

But where should the open/close paren be?  And I guess the rule applies
also to the case where you're not inside the comment, but right in front
of it, or between the "end of text" and the "beginning of
comment", right?

And really, the rule should also apply to /*..*/ comments (probably with
some restrictions such as point being on the same line as the /* or the
*/) since Emacs's C code always uses them even when //... could be
used instead.

>> > + (defcustom show-paren-when-point-in-margin nil
>> Please don't call it "margin".
> OK.  It's now called "periphery".

Not great, but at least it's not conflicting with existing
terminology and I don't have a better suggestion, so: thanks.

>> This is a code duplication.  Please move it to a separate helper function.
> No it's not.  It appears just once, after my patch is applied.

Sorry, I guess I misread it.
[ BTW, I see you used context diff instead of unified diff.  If that's
  the form you prefer, that's perfectly OK (I can M-x
  diff-context->unified just fine), but otherwise I prefer the unified
  format]

> +(defun show-paren--categorize-paren (pos)
> +  "Determine whether the character after POS has paren syntax,
> +and if so, return a cons (DIR . OUTSIDE).  For an open paren, DIR
> +is 1 and OUTSIDE is the position before the paren.  For a close
> +paren, DIR is -1 and OUTSIDE is the position after the paren.  If
> +the character isn't a paren, return nil."
> +  (cond
> +   ((eq (syntax-class (syntax-after pos)) 4)
> +    (cons 1 pos))
> +   ((eq (syntax-class (syntax-after pos)) 5)
> +    (cons -1 (1+ pos)))
> +   (t nil)))
> +
>  (defvar show-paren-data-function #'show-paren--default
>    "Function to find the opener/closer at point and its match.
>  The function is called with no argument and should return either nil
> @@ -120,22 +141,50 @@
>  Where HERE-BEG..HERE-END is expected to be around point.")
>
>  (defun show-paren--default ()
> -  (let* ((oldpos (point))
> -         (dir (cond ((eq (syntax-class (syntax-after (1- (point)))) 5) -1)
> -                    ((eq (syntax-class (syntax-after (point)))      4) 1)))
> -         (unescaped
> -          (when dir
> -            ;; Verify an even number of quoting characters precede the paren.
> -            ;; Follow the same logic as in `blink-matching-open'.
> -            (= (if (= dir -1) 1 0)
> -               (logand 1 (- (point)
> -                            (save-excursion
> -                              (if (= dir -1) (forward-char -1))
> -                              (skip-syntax-backward "/\\")
> -                              (point)))))))
> -         (here-beg (if (eq dir 1) (point) (1- (point))))
> -         (here-end (if (eq dir 1) (1+ (point)) (point)))
> -         pos mismatch)
> +  (let* ((ind-pos (save-excursion (back-to-indentation) (point)))
> +	 (bol-pos (save-excursion (beginning-of-line) (point)))

Aka line-beginning-position.

> +	 (eol-pos (save-excursion (end-of-line)
> +				  (let ((s (syntax-ppss)))
> +				    (if (nth 4 s)
> +					(goto-char (max (nth 8 s)
> +							(point-min))))

I don't think we need this `max' thingy.

> +				    (skip-chars-backward " \t"))
> +				  (point)))
> +	 (oldpos (point))

Please keep oldpos as the first entry in the let* (makes the patch
slightly shorter and makes it more obvious that point hasn't moved yet).

Still looks complicated.  Can't we do something more like

   (defun show-paren--move-in-periphery ()
     (cond
      ((save-excursion (skip-chars-backward " \t") (bolp))
       (skip-chars-forward " \t"))
      ((<= (line-end-position)
           (save-excursion (forward-comment (point-max)) (point)))
       (skip-chars-backward " \t"))))
   

   (defun show-paren--default ()
     (save-excursion
       (when show-paren-when-point-in-periphery
         (show-paren--move-in-periphery))
       ..same-old-code-as-before..))

This would make it clear that the behavior is unchanged when
show-paren-when-point-in-periphery is nil, and also makes it clear what
is the effect of setting show-paren-when-point-in-periphery to non-nil.


        Stefan



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-14 17:49     ` Stefan Monnier
@ 2014-10-14 18:32       ` John Yates
  2014-10-15  9:12       ` Alan Mackenzie
  1 sibling, 0 replies; 23+ messages in thread
From: John Yates @ 2014-10-14 18:32 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Alan Mackenzie, Emacs developers

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

It seems to me that the ultimate thrust of this proposal is to offer a
variant show-paren-mode.  This new variant would differ from the current
behavior of firing only when the cursor is located immediately before an
open-paren or immediately after a close-paren to a new behavior in which
all logical whitespace (including comments) on the current line is
ignored.  Such a conceptual model has a number of virtues:
- It relates the proposed behavior to today's behavior in a clear and
intelligible way.
- It points to a fully general model that includes how to behave when the
cursor is within whitespace inside a line.
- By accounting for _all_ whitespace on the line it finesses the need for
"periphery" or similar terminology.

/john

[-- Attachment #2: Type: text/html, Size: 868 bytes --]

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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-11 13:43 Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin Alan Mackenzie
  2014-10-11 14:21 ` Eli Zaretskii
  2014-10-12  4:12 ` Stefan Monnier
@ 2014-10-14 21:50 ` João Távora
  2 siblings, 0 replies; 23+ messages in thread
From: João Távora @ 2014-10-14 21:50 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

Alan Mackenzie <acm@muc.de> writes:

> This is an idea which struck me when I was reading CC Mode code.  I find
> myself frequently scrolling through it, and frequently wanting to know
> where the matching paren for one at beginning of indentation is.  That
> involved me typing M-m an awful lot, and I've got tired of doing that.

I've gotten tired of that too, but I took a different approach (perhaps
one that has already been discussed here?)

    (global-set-key [remap move-beginning-of-line] 'joaot/move-beginning-of-line)
    (defun joaot/move-beginning-of-line ()
      (interactive)
      (let ((pos (point)))
        (back-to-indentation)
        (when (= pos (point)) (move-beginning-of-line nil))))

If the code is not self-explanatory enough, it makes C-a do-what-I-mean:
The first C-a brings me to start of indentation, a second one forces a
move to the proper beginning of line.

It solves this problem because C-a is perhaps the most ergonomic binding
of all, provided you remap caps-lock to control.

Of course, Alan, your solution requires no typing at all, but it also
does not solve the almost-as-common problem that you might want to move
the cursor to the opening parenthesis. 

This is the single feature that I miss most when using other people's
emacsen or Emacs -Q. It's the only piece of editor that I keep around
since I switched to Emacs 10 years ago, from Eclipse, which had (has?)
this on by default. I also used to have it on C-e, but I don't miss that
nearly as much.

Its current implementation as a separate command is slightly brittle for
modes that remap C-a such, as `message-mode', so I wonder if it could be
integrated in Emacs's own `move-beginning-of-line' via a suitably named
customization variable.

What do you think?
João



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-14 17:49     ` Stefan Monnier
  2014-10-14 18:32       ` John Yates
@ 2014-10-15  9:12       ` Alan Mackenzie
  2014-10-15 22:38         ` Andy Moreton
  2014-10-16  2:43         ` Stefan Monnier
  1 sibling, 2 replies; 23+ messages in thread
From: Alan Mackenzie @ 2014-10-15  9:12 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hi, Stefan.

On Tue, Oct 14, 2014 at 01:49:21PM -0400, Stefan Monnier wrote:
> >> > Then again, why not do the same if point is in a line comment?
> >> Sorry, I don't know what "in a line comment" means.
> > In a comment which extends to EOL, e.g. "//..." in C++, or ";..." in
> > Lisp.

> But where should the open/close paren be?  And I guess the rule applies
> also to the case where you're not inside the comment, but right in front
> of it, or between the "end of text" and the "beginning of
> comment", right?

> And really, the rule should also apply to /*..*/ comments (probably with
> some restrictions such as point being on the same line as the /* or the
> */) since Emacs's C code always uses them even when //... could be
> used instead.

Sorry, I expressed myself really badly.  I'll try again.  Dividing a
source file line into LH periphery, core, RH periphery:

            foo = /* */ bar; /* comment */
PPPPPPPPPPPP                PPPPPPPPPPPPPP
            CCCCCCCCCCCCCCCC

The periphery of a line does not extend outside that line.  An "internal
comment" inside a line (a fairly rare thing) is not part of the
periphery.  Comments before the core are vanishingly rare, hence not
included in the definition.

The candidates for a pertinent parenthesis are then:
(i) A paren that point is immediately outside of.  A closing paren takes
priority over an opening one.  (This is the existing implementation.)
(ii) (Only when point is in the periphery) a paren at the beginning or
end of the core.  The one nearest point takes priority.

> >> > + (defcustom show-paren-when-point-in-margin nil
> >> Please don't call it "margin".
> > OK.  It's now called "periphery".

> Not great, but at least it's not conflicting with existing
> terminology and I don't have a better suggestion, so: thanks.

:-)

> [ BTW, I see you used context diff instead of unified diff.  If that's
>   the form you prefer, that's perfectly OK (I can M-x
>   diff-context->unified just fine), but otherwise I prefer the unified
>   format]

OK.  I've just reconfigured my bzr to use unified.  It seems that unified
has "won" over context over the last decade.  Previously, context was the
Emacs project standard.  Unified is the only format accepted by hg, and I
wouldn't be surprised if git also only uses unified.

> > +(defun show-paren--categorize-paren (pos)
> > +  "Determine whether the character after POS has paren syntax,
> > +and if so, return a cons (DIR . OUTSIDE).  For an open paren, DIR
> > +is 1 and OUTSIDE is the position before the paren.  For a close
> > +paren, DIR is -1 and OUTSIDE is the position after the paren.  If
> > +the character isn't a paren, return nil."
> > +  (cond
> > +   ((eq (syntax-class (syntax-after pos)) 4)
> > +    (cons 1 pos))
> > +   ((eq (syntax-class (syntax-after pos)) 5)
> > +    (cons -1 (1+ pos)))
> > +   (t nil)))
> > +
> >  (defvar show-paren-data-function #'show-paren--default
> >    "Function to find the opener/closer at point and its match.
> >  The function is called with no argument and should return either nil
> > @@ -120,22 +141,50 @@
> >  Where HERE-BEG..HERE-END is expected to be around point.")
> >
> >  (defun show-paren--default ()
> > -  (let* ((oldpos (point))
> > -         (dir (cond ((eq (syntax-class (syntax-after (1- (point)))) 5) -1)
> > -                    ((eq (syntax-class (syntax-after (point)))      4) 1)))
> > -         (unescaped
> > -          (when dir
> > -            ;; Verify an even number of quoting characters precede the paren.
> > -            ;; Follow the same logic as in `blink-matching-open'.
> > -            (= (if (= dir -1) 1 0)
> > -               (logand 1 (- (point)
> > -                            (save-excursion
> > -                              (if (= dir -1) (forward-char -1))
> > -                              (skip-syntax-backward "/\\")
> > -                              (point)))))))
> > -         (here-beg (if (eq dir 1) (point) (1- (point))))
> > -         (here-end (if (eq dir 1) (1+ (point)) (point)))
> > -         pos mismatch)
> > +  (let* ((ind-pos (save-excursion (back-to-indentation) (point)))
> > +	 (bol-pos (save-excursion (beginning-of-line) (point)))

> Aka line-beginning-position.

Thanks!  I didn't know about that.  There's line-end-position, too.

> > +	 (eol-pos (save-excursion (end-of-line)
> > +				  (let ((s (syntax-ppss)))
> > +				    (if (nth 4 s)
> > +					(goto-char (max (nth 8 s)
> > +							(point-min))))

> I don't think we need this `max' thingy.

I think we do, but it should be (line-beginning-position), not
(point-min).  But I see I need to beef up the logic to cope with a
closed /* */ style comment being on the line.

> > +				    (skip-chars-backward " \t"))
> > +				  (point)))
> > +	 (oldpos (point))

> Please keep oldpos as the first entry in the let* (makes the patch
> slightly shorter and makes it more obvious that point hasn't moved yet).

Will do.

> Still looks complicated.  Can't we do something more like

>    (defun show-paren--move-in-periphery ()
>      (cond
>       ((save-excursion (skip-chars-backward " \t") (bolp))
>        (skip-chars-forward " \t"))
>       ((<= (line-end-position)
>            (save-excursion (forward-comment (point-max)) (point)))
>        (skip-chars-backward " \t"))))


>    (defun show-paren--default ()
>      (save-excursion
>        (when show-paren-when-point-in-periphery
>          (show-paren--move-in-periphery))
>        ..same-old-code-as-before..))

> This would make it clear that the behavior is unchanged when
> show-paren-when-point-in-periphery is nil, and also makes it clear what
> is the effect of setting show-paren-when-point-in-periphery to non-nil.

That's neat.  But it wouldn't be quite the same.  It would be a quite
restricted version of what I'm proposing: e.g. point in the LH periphery
wouldn't then trigger a paren at "end of core", nor even a close paren at
"beginning of core" (which happens a little in lisp, a lot in C).

Incidentally, I think `show-paren-highlight-openparen', which is a defvar
really ought to be a customisable variable.  Maybe
`show-paren-data-function' should be, too.  A little amendment of the
Emacs manual wrt paren matching also wouldn't go amiss.

Anyhow, here's the latest version of the patch, this time in unified
format.  It's not as big as it looks: about half of it is because I
removed a superfluous `save-excursion'.




=== modified file 'lisp/paren.el'
--- lisp/paren.el	2014-02-10 01:34:22 +0000
+++ lisp/paren.el	2014-10-14 22:14:55 +0000
@@ -72,6 +72,14 @@
   :group 'paren-showing
   :version "20.3")
 
+(defcustom show-paren-when-point-in-periphery nil
+  "If non-nil, show parens when point is in the line's periphery.
+The periphery is either the whitespace at the beginning of a
+line, or a comment \(or whitespace) at the end of a line."
+  :type 'boolean
+  :group 'paren-showing
+  :version "25.1")
+
 (define-obsolete-face-alias 'show-paren-match-face 'show-paren-match "22.1")
 
 (define-obsolete-face-alias 'show-paren-mismatch-face
@@ -112,6 +120,19 @@
     (delete-overlay show-paren--overlay)
     (delete-overlay show-paren--overlay-1)))
 
+(defun show-paren--categorize-paren (pos)
+  "Determine whether the character after POS has paren syntax,
+and if so, return a cons (DIR . OUTSIDE).  For an open paren, DIR
+is 1 and OUTSIDE is the position before the paren.  For a close
+paren, DIR is -1 and OUTSIDE is the position after the paren.  If
+the character isn't a paren, return nil."
+  (cond
+   ((eq (syntax-class (syntax-after pos)) 4)
+    (cons 1 pos))
+   ((eq (syntax-class (syntax-after pos)) 5)
+    (cons -1 (1+ pos)))
+   (t nil)))
+
 (defvar show-paren-data-function #'show-paren--default
   "Function to find the opener/closer at point and its match.
 The function is called with no argument and should return either nil
@@ -121,67 +142,97 @@
 
 (defun show-paren--default ()
   (let* ((oldpos (point))
-         (dir (cond ((eq (syntax-class (syntax-after (1- (point)))) 5) -1)
-                    ((eq (syntax-class (syntax-after (point)))      4) 1)))
-         (unescaped
-          (when dir
-            ;; Verify an even number of quoting characters precede the paren.
-            ;; Follow the same logic as in `blink-matching-open'.
-            (= (if (= dir -1) 1 0)
-               (logand 1 (- (point)
-                            (save-excursion
-                              (if (= dir -1) (forward-char -1))
-                              (skip-syntax-backward "/\\")
-                              (point)))))))
-         (here-beg (if (eq dir 1) (point) (1- (point))))
-         (here-end (if (eq dir 1) (1+ (point)) (point)))
-         pos mismatch)
+	 (ind-pos (save-excursion (back-to-indentation) (point)))
+	 (eol-pos
+	  (save-excursion
+	    (end-of-line)
+	    (let ((s (syntax-ppss)))
+	      (if (nth 4 s)
+		  (goto-char (max (nth 8 s)
+				  (line-beginning-position)))))
+	    (save-restriction
+	      (narrow-to-region (line-beginning-position) (point-max))
+	      (forward-comment (- (current-column))))
+	    (point)))
+	 dir paren-details unescaped pos mismatch here-beg here-end)
+    (cond
+     ;; Point is at a paren.
+     ((eq (syntax-class (syntax-after (1- (point)))) 5)
+      (setq dir -1))
+     ((eq (syntax-class (syntax-after (point))) 4)
+      (setq dir 1))
+     ;; Point is in the WS before the code.
+     ((and show-paren-when-point-in-periphery
+	   (< (point) ind-pos))
+      (setq paren-details
+	    (or (show-paren--categorize-paren ind-pos)
+		(show-paren--categorize-paren (1- eol-pos)))))
+     ;; Point is in a comment or whitespace after the code.
+     ((and show-paren-when-point-in-periphery
+	   (>= (point) eol-pos))
+      (setq paren-details
+	    (or (show-paren--categorize-paren (1- eol-pos))
+		(show-paren--categorize-paren ind-pos)))))
+    (when paren-details
+      (setq dir (car paren-details)
+	    oldpos (cdr paren-details)))
+
+    (when dir
+      (setq unescaped
+	    (= (if (= dir -1) 1 0)
+	       (logand 1 (- oldpos
+			    (save-excursion
+			      (goto-char oldpos)
+			      (if (= dir -1) (backward-char))
+			      (skip-syntax-backward "/\\")
+			      (point)))))))
+    (setq here-beg (if (eq dir 1) oldpos (1- oldpos))
+          here-end (if (eq dir 1) (1+ oldpos) oldpos))
     ;;
     ;; Find the other end of the sexp.
     (when unescaped
-      (save-excursion
-        (save-restriction
-          ;; Determine the range within which to look for a match.
-          (when blink-matching-paren-distance
-            (narrow-to-region
-             (max (point-min) (- (point) blink-matching-paren-distance))
-             (min (point-max) (+ (point) blink-matching-paren-distance))))
-          ;; Scan across one sexp within that range.
-          ;; Errors or nil mean there is a mismatch.
-          (condition-case ()
-              (setq pos (scan-sexps (point) dir))
-            (error (setq pos t mismatch t)))
-          ;; Move back the other way and verify we get back to the
-          ;; starting point.  If not, these two parens don't really match.
-          ;; Maybe the one at point is escaped and doesn't really count,
-          ;; or one is inside a comment.
-          (when (integerp pos)
-            (unless (condition-case ()
-                        (eq (point) (scan-sexps pos (- dir)))
-                      (error nil))
-              (setq pos nil)))
-          ;; If found a "matching" paren, see if it is the right
-          ;; kind of paren to match the one we started at.
-          (if (not (integerp pos))
-              (if mismatch (list here-beg here-end nil nil t))
-            (let ((beg (min pos oldpos)) (end (max pos oldpos)))
-              (unless (eq (syntax-class (syntax-after beg)) 8)
-                (setq mismatch
-                      (not (or (eq (char-before end)
-                                   ;; This can give nil.
-                                   (cdr (syntax-after beg)))
-                               (eq (char-after beg)
-                                   ;; This can give nil.
-                                   (cdr (syntax-after (1- end))))
-                               ;; The cdr might hold a new paren-class
-                               ;; info rather than a matching-char info,
-                               ;; in which case the two CDRs should match.
-                               (eq (cdr (syntax-after (1- end)))
-                                   (cdr (syntax-after beg)))))))
-              (list here-beg here-end
-                    (if (= dir 1) (1- pos) pos)
-                    (if (= dir 1) pos (1+ pos))
-                    mismatch))))))))
+      (save-restriction
+	;; Determine the range within which to look for a match.
+	(when blink-matching-paren-distance
+	  (narrow-to-region
+	   (max (point-min) (- (point) blink-matching-paren-distance))
+	   (min (point-max) (+ (point) blink-matching-paren-distance))))
+	;; Scan across one sexp within that range.
+	;; Errors or nil mean there is a mismatch.
+	(condition-case ()
+	    (setq pos (scan-sexps oldpos dir))
+	  (error (setq pos t mismatch t)))
+	;; Move back the other way and verify we get back to the
+	;; starting point.  If not, these two parens don't really match.
+	;; Maybe the one at point is escaped and doesn't really count,
+	;; or one is inside a comment.
+	(when (integerp pos)
+	  (unless (condition-case ()
+		      (eq oldpos (scan-sexps pos (- dir)))
+		    (error nil))
+	    (setq pos nil)))
+	;; If found a "matching" paren, see if it is the right
+	;; kind of paren to match the one we started at.
+	(if (not (integerp pos))
+	    (if mismatch (list here-beg here-end nil nil t))
+	  (let ((beg (min pos oldpos)) (end (max pos oldpos)))
+	    (unless (eq (syntax-class (syntax-after beg)) 8)
+	      (setq mismatch
+		    (not (or (eq (char-before end)
+				 ;; This can give nil.
+				 (cdr (syntax-after beg)))
+			     (eq (char-after beg)
+				 ;; This can give nil.
+				 (cdr (syntax-after (1- end))))
+			     ;; The cdr might hold a new paren-class
+			     ;; info rather than a matching-char info,
+			     ;; in which case the two CDRs should match.
+			     (eq (cdr (syntax-after (1- end)))
+				 (cdr (syntax-after beg)))))))
+	    (list here-beg here-end
+		  (if (= dir 1) (1- pos) pos)
+		  (if (= dir 1) pos (1+ pos))
+		  mismatch)))))))
 
 ;; Find the place to show, if there is one,
 ;; and show it until input arrives.
@@ -215,7 +266,7 @@
         ;; Otherwise, turn off any such highlighting.
         (if (or (not here-beg)
                 (and (not show-paren-highlight-openparen)
-                     (> here-end (point))
+		     (= here-beg (point))
                      (integerp there-beg)))
             (delete-overlay show-paren--overlay-1)
           (move-overlay show-paren--overlay-1
@@ -234,7 +285,7 @@
                                           (1- there-end) (1+ there-beg))))
                          (not (pos-visible-in-window-p closest)))))
               (move-overlay show-paren--overlay
-                            (point)
+			    (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


>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-15  9:12       ` Alan Mackenzie
@ 2014-10-15 22:38         ` Andy Moreton
  2014-10-16  2:43         ` Stefan Monnier
  1 sibling, 0 replies; 23+ messages in thread
From: Andy Moreton @ 2014-10-15 22:38 UTC (permalink / raw)
  To: emacs-devel

On Wed 15 Oct 2014, Alan Mackenzie wrote:
> Sorry, I expressed myself really badly.  I'll try again.  Dividing a
> source file line into LH periphery, core, RH periphery:
>
>             foo = /* */ bar; /* comment */
> PPPPPPPPPPPP                PPPPPPPPPPPPPP
>             CCCCCCCCCCCCCCCC
>
> The periphery of a line does not extend outside that line.  An "internal
> comment" inside a line (a fairly rare thing) is not part of the
> periphery.  Comments before the core are vanishingly rare, hence not
> included in the definition.
>
> The candidates for a pertinent parenthesis are then:
> (i) A paren that point is immediately outside of.  A closing paren takes
> priority over an opening one.  (This is the existing implementation.)
> (ii) (Only when point is in the periphery) a paren at the beginning or
> end of the core.  The one nearest point takes priority.

So if understand correctly, LH periphery is leading whitespace/comments,
and RH periphery is trailing whitespace/comments ?

    AndyM




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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-15  9:12       ` Alan Mackenzie
  2014-10-15 22:38         ` Andy Moreton
@ 2014-10-16  2:43         ` Stefan Monnier
  2014-10-16  9:53           ` Alan Mackenzie
  1 sibling, 1 reply; 23+ messages in thread
From: Stefan Monnier @ 2014-10-16  2:43 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

>             foo = /* */ bar; /* comment */
> PPPPPPPPPPPP                PPPPPPPPPPPPPP
>             CCCCCCCCCCCCCCCC

I don't understand why you'd want to consider the inside of the /*
comment */ as part of the periphery.  This increases
ambiguity/complexity of the behavior since there might be parentheses
inside the comment (and then there's the case of nested comments).

[ > OK.  I've just reconfigured my bzr to use unified.  It seems that unified
  > has "won" over context over the last decade.  Previously, context was the
  > Emacs project standard.

  Richard is a "context" guy, that's why.  I occasionally switch to
  context style for complex diffs where the interleaving of unified
  makes the code unreadable, but in most cases I find unified to be
  easier to read.  ]

>> > +	 (eol-pos (save-excursion (end-of-line)
>> > +				  (let ((s (syntax-ppss)))
>> > +				    (if (nth 4 s)
>> > +					(goto-char (max (nth 8 s)
>> > +							(point-min))))
>> I don't think we need this `max' thingy.
> I think we do, but it should be (line-beginning-position), not
> (point-min).

In that cases, indeed it's needed.  For point-min it shouldn't be (since
(goto-char -10) will happily move to point-min).

>> This would make it clear that the behavior is unchanged when
>> show-paren-when-point-in-periphery is nil, and also makes it clear what
>> is the effect of setting show-paren-when-point-in-periphery to non-nil.
> That's neat.  But it wouldn't be quite the same.  It would be a quite
> restricted version of what I'm proposing: e.g. point in the LH periphery
> wouldn't then trigger a paren at "end of core",

I don't understand which case you're referring to.  Can you give an
example of such a "point in the LH periphery; paren at end of core"?

> nor even a close paren at "beginning of core" (which happens a little
> in lisp, a lot in C).

So you also want to highlight the matching opening paren in the case

   <point>   }

?  If so, I guess we should also do it in cases such as:

   foo(blabla<point>);

> Incidentally, I think `show-paren-highlight-openparen', which is a defvar
> really ought to be a customisable variable.

That's probably a leftover/oversight from before defcustom existed.

> Maybe `show-paren-data-function' should be, too.

Why?  It was introduced for the benefit of smie.el, so it's typically
set buffer-locally by major-modes, which makes it a poor fit for
a defcustom.


        Stefan



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-16  2:43         ` Stefan Monnier
@ 2014-10-16  9:53           ` Alan Mackenzie
  2014-10-16 12:59             ` Stefan Monnier
  0 siblings, 1 reply; 23+ messages in thread
From: Alan Mackenzie @ 2014-10-16  9:53 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello, Stefan.

On Wed, Oct 15, 2014 at 10:43:21PM -0400, Stefan Monnier wrote:
> >             foo = /* */ bar; /* comment */
> > PPPPPPPPPPPP                PPPPPPPPPPPPPP
> >             CCCCCCCCCCCCCCCC

> I don't understand why you'd want to consider the inside of the /*
> comment */ as part of the periphery.  This increases
> ambiguity/complexity of the behavior since there might be parentheses
> inside the comment (and then there's the case of nested comments).

The parens inside the comment would still get shown as they do
currently.  I think my thinking was that some paren pair near point
should be shown "most of the time", but this would get annoying when
point is inside the core.

But yes, I've not taken nested comments into account.  Maybe it would be
better just to count whitespace (not comments) as being in the periphery.
If handling for nested comments were added, things would be beginning to
get out of hand.

How about the following model:

            foo = /* */ bar; /* comment */
PPPPPPPPPPPP                              P
            CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

?

[ ... ]

> >> This would make it clear that the behavior is unchanged when
> >> show-paren-when-point-in-periphery is nil, and also makes it clear what
> >> is the effect of setting show-paren-when-point-in-periphery to non-nil.
> > That's neat.  But it wouldn't be quite the same.  It would be a quite
> > restricted version of what I'm proposing: e.g. point in the LH periphery
> > wouldn't then trigger a paren at "end of core",

> I don't understand which case you're referring to.  Can you give an
> example of such a "point in the LH periphery; paren at end of core"?

(defface show-paren-mismatch
  '((((class color)) (:foreground "white" :background "purple"))
    (t (:inverse-video t)))
  "Face used for a mismatching paren."
  :group 'paren-showing-faces)   <-------- paren
^
|
point

> > nor even a close paren at "beginning of core" (which happens a little
> > in lisp, a lot in C).

> So you also want to highlight the matching opening paren in the case

>    <point>   }

Yes.

> ?  If so, I guess we should also do it in cases such as:

>    foo(blabla<point>);

Hmmm.   Maybe.  But this whole idea was meant just to be a simple
extension.

> > Incidentally, I think `show-paren-highlight-openparen', which is a defvar
> > really ought to be a customisable variable.

> That's probably a leftover/oversight from before defcustom existed.

I'll make it a defcustom, then.

> > Maybe `show-paren-data-function' should be, too.

> Why?  It was introduced for the benefit of smie.el, so it's typically
> set buffer-locally by major-modes, which makes it a poor fit for
> a defcustom.

OK.

So, to sum up, I'll remove comments from the definition of "periphery",
and make that variable a defcustom.  I don't think you need to see
another amended patch.

>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-16  9:53           ` Alan Mackenzie
@ 2014-10-16 12:59             ` Stefan Monnier
  2014-10-16 13:31               ` Alan Mackenzie
  0 siblings, 1 reply; 23+ messages in thread
From: Stefan Monnier @ 2014-10-16 12:59 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

> How about the following model:

>             foo = /* */ bar; /* comment */
> PPPPPPPPPPPP                              P
>             CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

> ?

Sounds fine, yes.  

               foo = /* */ bar; /* comment */
   PPPPPPPPPPPP                P             P
               CCCCCCCCCCCCCCCC CCCCCCCCCCCCC

would be OK as well, I think.

>> >> This would make it clear that the behavior is unchanged when
>> >> show-paren-when-point-in-periphery is nil, and also makes it clear what
>> >> is the effect of setting show-paren-when-point-in-periphery to non-nil.
>> > That's neat.  But it wouldn't be quite the same.  It would be a quite
>> > restricted version of what I'm proposing: e.g. point in the LH periphery
>> > wouldn't then trigger a paren at "end of core",
>> I don't understand which case you're referring to.  Can you give an
>> example of such a "point in the LH periphery; paren at end of core"?

> (defface show-paren-mismatch
>   '((((class color)) (:foreground "white" :background "purple"))
>     (t (:inverse-video t)))
>   "Face used for a mismatching paren."
>   :group 'paren-showing-faces)   <-------- paren
> ^
> |
> point

Ah, so now your periphery extends to the next line.  That means you get
an ambiguity for

  (defun foo ()
    (do one thing)
    (do another)
  ^
  |
  point

where both the close paren on the previous line and the open paren on
the same line could be chosen.

>> > nor even a close paren at "beginning of core" (which happens a little
>> > in lisp, a lot in C).
>> So you also want to highlight the matching opening paren in the case
>> <point>   }
> Yes.

>> ?  If so, I guess we should also do it in cases such as:
>> foo(blabla<point>);
> Hmmm.   Maybe.

OK, sounds good.  There have already been requests for that kind of feature.

> But this whole idea was meant just to be a simple extension.

It's actually simpler to make it apply "everywhere" than only in a few
particular cases.

>> > Maybe `show-paren-data-function' should be, too.
>> Why?  It was introduced for the benefit of smie.el, so it's typically
>> set buffer-locally by major-modes, which makes it a poor fit for
>> a defcustom.
> OK.
> So, to sum up, I'll remove comments from the definition of "periphery",
> and make that variable a defcustom.  I don't think you need to see
> another amended patch.

To tell you the truth, I didn't like much the last show-paren--default
you showed me.  I think this new feature should be
"completely" external and mostly keep the old code untouched.


        Stefan



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-16 12:59             ` Stefan Monnier
@ 2014-10-16 13:31               ` Alan Mackenzie
  2014-10-16 14:37                 ` Stefan Monnier
  0 siblings, 1 reply; 23+ messages in thread
From: Alan Mackenzie @ 2014-10-16 13:31 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello, Stefan.

On Thu, Oct 16, 2014 at 08:59:40AM -0400, Stefan Monnier wrote:

> > (defface show-paren-mismatch
> >   '((((class color)) (:foreground "white" :background "purple"))
> >     (t (:inverse-video t)))
> >   "Face used for a mismatching paren."
> >   :group 'paren-showing-faces)   <-------- paren
> > ^
> > |
> > point

> Ah, so now your periphery extends to the next line.

No, no, no!  By the two line arrow symbol, I meant point somewhere before
":group".

> That means you get an ambiguity for ....

Oh no I don't!

> >> If so, I guess we should also do it in cases such as:
> >> foo(blabla<point>);
> > Hmmm.   Maybe.

> OK, sounds good.  There have already been requests for that kind of feature.

OK.  How about showing the paren (and its match) when

(i) point is touching the paren, and is either outside it (current
implementation), or inside it (new feature), with an "outside" taking
prioirty;
(ii) point is in the periphery and there is a paren at an extreme edge
of the core, one at the nearest (to point) edge taking priority (already
implemented)?

> > But this whole idea was meant just to be a simple extension.

> It's actually simpler to make it apply "everywhere" than only in a few
> particular cases.

[ ... ]

> To tell you the truth, I didn't like much the last show-paren--default
> you showed me.

I'd kind of guessed that.

> I think this new feature should be "completely" external and mostly
> keep the old code untouched.

OK, then.  I'll extract a new defun paren-show--locate-near-paren which
will return a cons (DIR . OLDPOS).  I think that will satisfy you.
Shouldn't take long, I'll send you a new patch soon.

>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-16 13:31               ` Alan Mackenzie
@ 2014-10-16 14:37                 ` Stefan Monnier
  2014-10-16 15:46                   ` Alan Mackenzie
  0 siblings, 1 reply; 23+ messages in thread
From: Stefan Monnier @ 2014-10-16 14:37 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

>> > (defface show-paren-mismatch
>> >   '((((class color)) (:foreground "white" :background "purple"))
>> >     (t (:inverse-video t)))
>> >   "Face used for a mismatching paren."
>> >   :group 'paren-showing-faces)   <-------- paren
>> > ^
>> > |
>> > point
>> Ah, so now your periphery extends to the next line.
> No, no, no!  By the two line arrow symbol, I meant point somewhere before
> ":group".

Oh, I see.  That seems even more weird to me.  I think this one is
pushing things a bit too far for my taste.  I think we should stick to
parens that are separated from point only by whitespace (or something
semantically equivalent such as comments).

And of course, it gets you an ambiguity for

  (defun foo ()
    (bar))
  ^
  |
  point

>> That means you get an ambiguity for ....
> Oh no I don't!

Just to be clear: ambiguities aren't that bad (we have them already
when point is between a close and an open paren).

>> >> If so, I guess we should also do it in cases such as:
>> >> foo(blabla<point>);
>> > Hmmm.   Maybe.
>> OK, sounds good.  There have already been requests for that kind of feature.
> OK.  How about showing the paren (and its match) when
> (i) point is touching the paren, and is either outside it (current
> implementation), or inside it (new feature), with an "outside" taking
> prioirty;

Sounds good.  That's the feature requested in
http://stackoverflow.com/questions/25648067/emacs-matching-parenthesis-when-cursor-is-on-closing-parenthesis

> (ii) point is in the periphery and there is a paren at an extreme edge
> of the core, one at the nearest (to point) edge taking priority (already
> implemented)?

I think we should only consider the extreme edge of the core that's
closest to point.


        Stefan



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-16 14:37                 ` Stefan Monnier
@ 2014-10-16 15:46                   ` Alan Mackenzie
  2014-10-16 17:40                     ` Stefan Monnier
  0 siblings, 1 reply; 23+ messages in thread
From: Alan Mackenzie @ 2014-10-16 15:46 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello, Stefan.

On Thu, Oct 16, 2014 at 10:37:56AM -0400, Stefan Monnier wrote:
> >> > (defface show-paren-mismatch
> >> >   '((((class color)) (:foreground "white" :background "purple"))
> >> >     (t (:inverse-video t)))
> >> >   "Face used for a mismatching paren."
> >> >   :group 'paren-showing-faces)   <-------- paren
> >> > ^
> >> > |
> >> > point
> >> Ah, so now your periphery extends to the next line.
> > No, no, no!  By the two line arrow symbol, I meant point somewhere before
> > ":group".

> Oh, I see.  That seems even more weird to me.  I think this one is
> pushing things a bit too far for my taste.  I think we should stick to
> parens that are separated from point only by whitespace (or something
> semantically equivalent such as comments).

I've been trying this out the past few days, and it seems having a
closing paren at EOL shown when point is in LH periphery is quite useful.

On the other hand, with point at EOL showing a paren at LH core, as you
type (lisp), various parens flash on and off like a third rate web site,
and it quickly gets irritating.  So I commented that possibility out.

What is still there is an open paren direcly left of point flashing just
after you've typed it (e.g. with M-:).  This irritates somewhat, and
maybe this should come out, too.

> And of course, it gets you an ambiguity for

>   (defun foo ()
>     (bar))
>   ^
>   |
>   point

The nearest eligible paren to point currently takes priority.

> >> That means you get an ambiguity for ....
> > Oh no I don't!

> Just to be clear: ambiguities aren't that bad (we have them already
> when point is between a close and an open paren).

OK.

> >> >> If so, I guess we should also do it in cases such as:
> >> >> foo(blabla<point>);
> >> > Hmmm.   Maybe.
> >> OK, sounds good.  There have already been requests for that kind of feature.
> > OK.  How about showing the paren (and its match) when
> > (i) point is touching the paren, and is either outside it (current
> > implementation), or inside it (new feature), with an "outside" taking
> > prioirty;

> Sounds good.  That's the feature requested in
> http://stackoverflow.com/questions/25648067/emacs-matching-parenthesis-when-cursor-is-on-closing-parenthesis

> > (ii) point is in the periphery and there is a paren at an extreme edge
> > of the core, one at the nearest (to point) edge taking priority (already
> > implemented)?

> I think we should only consider the extreme edge of the core that's
> closest to point.

As I said above, currently I've got that for point in RH periphery, but
not LH periphery.

I've refactored the code somewhat, too.  I've now got
show-paren--locate-near-paren, as promised, but I've put the "not
escaped" test into this defun, too.  That way, if there're two candidate
parens, as in

    ?\)<point>)

, the code doesn't just give up when the first candidate is found to be
escaped.  I've also renamed variables from "oldpos" to "outside", since
the original name had become somewhat obscure.  show-paren--default has
become a little shorter.

A question: show-paren--default returns a list containe here-beg,
here-end, there-beg, and there-end, four positions.  Couldn't we just
return two of these t?here-beg (renamed just to t?here), saving a bit of
messing around, or are there circumstances where here-end might not be
(1+ here-beg)?

See what you think.



=== modified file 'lisp/paren.el'
--- lisp/paren.el	2014-02-10 01:34:22 +0000
+++ lisp/paren.el	2014-10-16 15:10:34 +0000
@@ -72,12 +72,20 @@
   :group 'paren-showing
   :version "20.3")
 
+(defcustom show-paren-when-point-in-periphery nil
+  "If non-nil, show parens when point is in the line's periphery.
+The periphery is at the beginning or end of a line or in any
+whitespace there."
+  :type 'boolean
+  :group 'paren-showing
+  :version "25.1")
+
 (define-obsolete-face-alias 'show-paren-match-face 'show-paren-match "22.1")
 
 (define-obsolete-face-alias 'show-paren-mismatch-face
   'show-paren-mismatch "22.1")
 
-(defvar show-paren-highlight-openparen t
+(defcustom show-paren-highlight-openparen t
   "Non-nil turns on openparen highlighting when matching forward.")
 
 (defvar show-paren--idle-timer nil)
@@ -112,76 +120,120 @@
     (delete-overlay show-paren--overlay)
     (delete-overlay show-paren--overlay-1)))
 
+(defun show-paren--categorize-paren (pos)
+  "Determine whether the character after POS has paren syntax,
+and if so, return a cons (DIR . OUTSIDE).  For an open paren, DIR
+is 1 and OUTSIDE is the position before the paren.  For a close
+paren, DIR is -1 and OUTSIDE is the position after the paren.  If
+the character isn't a paren, return nil."
+  (cond
+   ((eq (syntax-class (syntax-after pos)) 4)
+    (cons 1 pos))
+   ((eq (syntax-class (syntax-after pos)) 5)
+    (cons -1 (1+ pos)))
+   (t nil)))
+
+(defun show-paren--unescaped-p (p-cons)
+  "Is the paren in P-CONS unescaped?  If so return P-CONS, else nil.
+P-CONS is either nil or a cons (DIR . OUTSIDE) where DIR is 1 for
+an open paren, -1 for a close paren, and OUTSIDE is the buffer
+position of the outside of the paren."
+  (when p-cons
+    (save-excursion
+      (goto-char (cdr p-cons))
+      (if (= (car p-cons) -1) (backward-char))
+      (when (= (logand (skip-syntax-backward "/\\") 1) 0)
+	p-cons))))
+
+(defun show-paren--locate-near-paren ()
+  "Locate an unescaped paren \"near\" point to show.
+If one is found, return the cons (DIR . OUTSIDE), where DIR is 1
+for an open paren, -1 for a close paren, and OUTSIDE is the buffer
+position of the outside of the paren.  Otherwise return nil."
+  (let* ((ind-pos (save-excursion (back-to-indentation) (point)))
+	 (eol-pos
+	  (save-excursion
+	    (end-of-line) (skip-chars-backward " \t" ind-pos) (point)))
+	 (before (show-paren--categorize-paren (1- (point))))
+	 (after (show-paren--categorize-paren (point))))
+    (cond
+     ;; Point is immediately outside a paren.
+     ((and (eq (car before) -1) (show-paren--unescaped-p before)))
+     ((and (eq (car after) 1) (show-paren--unescaped-p after)))
+     ;; Point is immediately inside a paren.
+     ((show-paren--unescaped-p before))
+     ((show-paren--unescaped-p after))
+     ;; Point is in the whitespace before the code.
+     ((and show-paren-when-point-in-periphery
+	   (< (point) ind-pos))
+      (or (show-paren--unescaped-p (show-paren--categorize-paren ind-pos))
+	  (show-paren--unescaped-p (show-paren--categorize-paren (1- eol-pos)))))
+     ;; Point is in the whitespace after the code.
+     ((and show-paren-when-point-in-periphery
+	   (>= (point) eol-pos))
+      (or (show-paren--unescaped-p (show-paren--categorize-paren (1- eol-pos)))
+	  ;; (show-paren--unescaped-p (show-paren--categorize-paren ind-pos))
+	  )))))
+
 (defvar show-paren-data-function #'show-paren--default
-  "Function to find the opener/closer at point and its match.
+  "Function to find the opener/closer \"near\" point and its match.
 The function is called with no argument and should return either nil
-if there's no opener/closer at point, or a list of the form
+if there's no opener/closer near point, or a list of the form
 \(HERE-BEG HERE-END THERE-BEG THERE-END MISMATCH)
-Where HERE-BEG..HERE-END is expected to be around point.")
+Where HERE-BEG..HERE-END is expected to be around the paren.")
 
 (defun show-paren--default ()
-  (let* ((oldpos (point))
-         (dir (cond ((eq (syntax-class (syntax-after (1- (point)))) 5) -1)
-                    ((eq (syntax-class (syntax-after (point)))      4) 1)))
-         (unescaped
-          (when dir
-            ;; Verify an even number of quoting characters precede the paren.
-            ;; Follow the same logic as in `blink-matching-open'.
-            (= (if (= dir -1) 1 0)
-               (logand 1 (- (point)
-                            (save-excursion
-                              (if (= dir -1) (forward-char -1))
-                              (skip-syntax-backward "/\\")
-                              (point)))))))
-         (here-beg (if (eq dir 1) (point) (1- (point))))
-         (here-end (if (eq dir 1) (1+ (point)) (point)))
-         pos mismatch)
+  (let* ((temp (show-paren--locate-near-paren))
+	 (dir (car temp))
+	 (outside (cdr temp))
+	 pos mismatch here-beg here-end)
     ;;
     ;; Find the other end of the sexp.
-    (when unescaped
-      (save-excursion
-        (save-restriction
-          ;; Determine the range within which to look for a match.
-          (when blink-matching-paren-distance
-            (narrow-to-region
-             (max (point-min) (- (point) blink-matching-paren-distance))
-             (min (point-max) (+ (point) blink-matching-paren-distance))))
-          ;; Scan across one sexp within that range.
-          ;; Errors or nil mean there is a mismatch.
-          (condition-case ()
-              (setq pos (scan-sexps (point) dir))
-            (error (setq pos t mismatch t)))
-          ;; Move back the other way and verify we get back to the
-          ;; starting point.  If not, these two parens don't really match.
-          ;; Maybe the one at point is escaped and doesn't really count,
-          ;; or one is inside a comment.
-          (when (integerp pos)
-            (unless (condition-case ()
-                        (eq (point) (scan-sexps pos (- dir)))
-                      (error nil))
-              (setq pos nil)))
-          ;; If found a "matching" paren, see if it is the right
-          ;; kind of paren to match the one we started at.
-          (if (not (integerp pos))
-              (if mismatch (list here-beg here-end nil nil t))
-            (let ((beg (min pos oldpos)) (end (max pos oldpos)))
-              (unless (eq (syntax-class (syntax-after beg)) 8)
-                (setq mismatch
-                      (not (or (eq (char-before end)
-                                   ;; This can give nil.
-                                   (cdr (syntax-after beg)))
-                               (eq (char-after beg)
-                                   ;; This can give nil.
-                                   (cdr (syntax-after (1- end))))
-                               ;; The cdr might hold a new paren-class
-                               ;; info rather than a matching-char info,
-                               ;; in which case the two CDRs should match.
-                               (eq (cdr (syntax-after (1- end)))
-                                   (cdr (syntax-after beg)))))))
-              (list here-beg here-end
-                    (if (= dir 1) (1- pos) pos)
-                    (if (= dir 1) pos (1+ pos))
-                    mismatch))))))))
+    (when dir
+      (setq here-beg (if (eq dir 1) outside (1- outside))
+	    here-end (if (eq dir 1) (1+ outside) outside))
+      (save-restriction
+	;; Determine the range within which to look for a match.
+	(when blink-matching-paren-distance
+	  (narrow-to-region
+	   (max (point-min) (- (point) blink-matching-paren-distance))
+	   (min (point-max) (+ (point) blink-matching-paren-distance))))
+	;; Scan across one sexp within that range.
+	;; Errors or nil mean there is a mismatch.
+	(condition-case ()
+	    (setq pos (scan-sexps outside dir))
+	  (error (setq pos t mismatch t)))
+	;; Move back the other way and verify we get back to the
+	;; starting point.  If not, these two parens don't really match.
+	;; Maybe the one at point is escaped and doesn't really count,
+	;; or one is inside a comment.
+	(when (integerp pos)
+	  (unless (condition-case ()
+		      (eq outside (scan-sexps pos (- dir)))
+		    (error nil))
+	    (setq pos nil)))
+	;; If found a "matching" paren, see if it is the right
+	;; kind of paren to match the one we started at.
+	(if (not (integerp pos))
+	    (if mismatch (list here-beg here-end nil nil t))
+	  (let ((beg (min pos outside)) (end (max pos outside)))
+	    (unless (eq (syntax-class (syntax-after beg)) 8)
+	      (setq mismatch
+		    (not (or (eq (char-before end)
+				 ;; This can give nil.
+				 (cdr (syntax-after beg)))
+			     (eq (char-after beg)
+				 ;; This can give nil.
+				 (cdr (syntax-after (1- end))))
+			     ;; The cdr might hold a new paren-class
+			     ;; info rather than a matching-char info,
+			     ;; in which case the two CDRs should match.
+			     (eq (cdr (syntax-after (1- end)))
+				 (cdr (syntax-after beg)))))))
+	    (list here-beg here-end
+		  (if (= dir 1) (1- pos) pos)
+		  (if (= dir 1) pos (1+ pos))
+		  mismatch)))))))
 
 ;; Find the place to show, if there is one,
 ;; and show it until input arrives.
@@ -215,7 +267,7 @@
         ;; Otherwise, turn off any such highlighting.
         (if (or (not here-beg)
                 (and (not show-paren-highlight-openparen)
-                     (> here-end (point))
+		     (= here-beg (point))
                      (integerp there-beg)))
             (delete-overlay show-paren--overlay-1)
           (move-overlay show-paren--overlay-1
@@ -234,7 +286,7 @@
                                           (1- there-end) (1+ there-beg))))
                          (not (pos-visible-in-window-p closest)))))
               (move-overlay show-paren--overlay
-                            (point)
+			    (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


>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-16 15:46                   ` Alan Mackenzie
@ 2014-10-16 17:40                     ` Stefan Monnier
  2014-10-16 21:26                       ` Alan Mackenzie
  0 siblings, 1 reply; 23+ messages in thread
From: Stefan Monnier @ 2014-10-16 17:40 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

> I've been trying this out the past few days, and it seems having a
> closing paren at EOL shown when point is in LH periphery is quite useful.

I think for

  (defun foo
  <1>  blabla
  <2>  blilbi)
  <3>

I'd prefer we highlight the matching parens when we're at <3> than when
we're at <2>.  Would that be "about as useful", you think?

Otherwise it seems way too weird/ad-hoc to hardcode it, so make
it dependent on a config var.

Another option is to highlight the "nearest surrounding parens", in
which case we'd highlight the parens above both in cases <1> and <2>
(plus various others), but this is probably too intrusive.

> What is still there is an open paren direcly left of point flashing just
> after you've typed it (e.g. with M-:).  This irritates somewhat, and
> maybe this should come out, too.

Let's keep it for now.

>> And of course, it gets you an ambiguity for
>> (defun foo ()
>> (bar))
>> ^
>> |
>> point
> The nearest eligible paren to point currently takes priority.

Define "nearest".  Can the user mentally compute this "nearest" as well
(e.g. does it depend on "invisible" trailing whitespace, or does it
depend on "invisible" differences between spaces and tabs)?

I think giving precedence always in the same direction (when there are
separating spaces on both sides) is a simpler choice for the user.

> As I said above, currently I've got that for point in RH periphery, but
> not LH periphery.

OK (tho please make it conditional on some config variable: doesn't have
to be a defcustom for now, and you can use whichever default you like).

> I've refactored the code somewhat, too.  I've now got
> show-paren--locate-near-paren, as promised, but I've put the "not
> escaped" test into this defun, too.  That way, if there're two candidate
> parens, as in
>     ?\)<point>)
> , the code doesn't just give up when the first candidate is found to be
> escaped.  I've also renamed variables from "oldpos" to "outside", since
> the original name had become somewhat obscure.  show-paren--default has
> become a little shorter.

Sounds good, thanks.  See comments below.

> A question: show-paren--default returns a list containe here-beg,
> here-end, there-beg, and there-end, four positions.  Couldn't we just
> return two of these t?here-beg (renamed just to t?here), saving a bit of
> messing around, or are there circumstances where here-end might not be
> (1+ here-beg)?

show-paren--default will never return something else, but other
functions can, such as SMIE's when matching paired keywords like "begin
... end".

> +(defcustom show-paren-when-point-in-periphery nil

Maybe a shorter name would be `show-paren-when-nearby'.
But let's not wait for an agreement here.

> +  "If non-nil, show parens when point is in the line's periphery.
> +The periphery is at the beginning or end of a line or in any
> +whitespace there."
> +  :type 'boolean
> +  :group 'paren-showing
> +  :version "25.1")

":group 'paren-showing" is redundant.

> -(defvar show-paren-highlight-openparen t
> +(defcustom show-paren-highlight-openparen t
>    "Non-nil turns on openparen highlighting when matching forward.")

Add a ":type 'boolean".

> +(defun show-paren--unescaped-p (p-cons)
> +  "Is the paren in P-CONS unescaped?  If so return P-CONS, else nil.
> +P-CONS is either nil or a cons (DIR . OUTSIDE) where DIR is 1 for
> +an open paren, -1 for a close paren, and OUTSIDE is the buffer
> +position of the outside of the paren."
> +  (when p-cons
> +    (save-excursion
> +      (goto-char (cdr p-cons))
> +      (if (= (car p-cons) -1) (backward-char))
> +      (when (= (logand (skip-syntax-backward "/\\") 1) 0)
> +	p-cons))))

Functions that end in "-p" should only return a boolean (i.e. the return
value doesn't have to be "t or nil" but the non-nil value should have no
other meaning than being non-nil and the callers shouldn't rely on it
being anything in particular other than "non-nil").

I know we sadly don't always follow this principle (e.g. `framep'), but
let's try not to make things worse.

> +     ((and (eq (car before) -1) (show-paren--unescaped-p before)))
> +     ((and (eq (car after) 1) (show-paren--unescaped-p after)))

So, here I recommend you explicitly return `before' and `after' since
the code otherwise looks weird since it seems to return a boolean when
a pair is expected.

> +     ;; Point is immediately inside a paren.
> +     ((show-paren--unescaped-p before))
> +     ((show-paren--unescaped-p after))

This should be controlled by a config var since it's new behavior.
AFAIK we can (re)use show-paren-when-point-in-periphery just fine.

BTW, maybe the "unescaped" check could be folded into
show-paren--categorize-paren.

> -Where HERE-BEG..HERE-END is expected to be around point.")
> +Where HERE-BEG..HERE-END is expected to be around the paren.")

I think "near point" is closer to the original intention than "around
the paren", especially since it might not be a parenthesis.  Also since
we shouldn't expect it to be around point, the additional patch below is
probably needed.

Other than that, feel free to install it,


        Stefan


=== modified file 'lisp/paren.el'
--- lisp/paren.el	2014-02-10 01:34:22 +0000
+++ lisp/paren.el	2014-10-16 17:36:28 +0000
@@ -216,6 +216,7 @@
         (if (or (not here-beg)
                 (and (not show-paren-highlight-openparen)
                      (> here-end (point))
+                     (<= here-beg (point))
                      (integerp there-beg)))
             (delete-overlay show-paren--overlay-1)
           (move-overlay show-paren--overlay-1




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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-16 17:40                     ` Stefan Monnier
@ 2014-10-16 21:26                       ` Alan Mackenzie
  2014-10-17  0:14                         ` Stefan Monnier
  0 siblings, 1 reply; 23+ messages in thread
From: Alan Mackenzie @ 2014-10-16 21:26 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello, Stefan.

On Thu, Oct 16, 2014 at 01:40:22PM -0400, Stefan Monnier wrote:
> > I've been trying this out the past few days, and it seems having a
> > closing paren at EOL shown when point is in LH periphery is quite useful.

> I think for

>   (defun foo
>   <1>  blabla
>   <2>  blilbi)
>   <3>

> I'd prefer we highlight the matching parens when we're at <3> than when
> we're at <2>.  Would that be "about as useful", you think?

More complicated to implement.  Up to now, the "here" paren is always on
the same line as point.  I personally would find it less useful if point
at <2> didn't trigger the paren at EOL.  My main use case is in long
rambling defuns (such as are common in CC Mode), being able to match
parens which are far apart, e.g. 60 lines apart, just by C-n'ing and
C-p'ing in the left periphery.  If this wasn't triggered for an isolated
line which _had_ a ) or (, this would be irritating.

> Otherwise it seems way too weird/ad-hoc to hardcode it, so make
> it dependent on a config var.

I think that our marginal utility will drop off very sharply, the more
complicated our configurability becomes.

> Another option is to highlight the "nearest surrounding parens", in
> which case we'd highlight the parens above both in cases <1> and <2>
> (plus various others), but this is probably too intrusive.

I don't think I'd like this, but haven't tried it.

> > What is still there is an open paren direcly left of point flashing just
> > after you've typed it (e.g. with M-:).  This irritates somewhat, and
> > maybe this should come out, too.

> Let's keep it for now.

OK.

> >> And of course, it gets you an ambiguity for
> >> (defun foo ()
> >> (bar))
> >> ^
> >> |
> >> point
> > The nearest eligible paren to point currently takes priority.

> Define "nearest".  Can the user mentally compute this "nearest" as well
> (e.g. does it depend on "invisible" trailing whitespace, or does it
> depend on "invisible" differences between spaces and tabs)?

Since point is on the same line as the paren, and is in the WS at either
BOL or EOL, and the (at most) two eligible parameters are on the edge of
the "core", point can't be between the two candidates.  The nearest one
is clear, surely.

If we extend the functionality along the lines you suggested a bit
earlier, "nearest" becomes less clear.

> I think giving precedence always in the same direction (when there are
> separating spaces on both sides) is a simpler choice for the user.

> > As I said above, currently I've got that for point in RH periphery, but
> > not LH periphery.

> OK (tho please make it conditional on some config variable: doesn't have
> to be a defcustom for now, and you can use whichever default you like).

Whoops, sorry, this got left out when I committed.

[ ... ]

> > A question: show-paren--default returns a list containe here-beg,
> > here-end, there-beg, and there-end, four positions.  Couldn't we just
> > return two of these t?here-beg (renamed just to t?here), saving a bit of
> > messing around, or are there circumstances where here-end might not be
> > (1+ here-beg)?

> show-paren--default will never return something else, but other
> functions can, such as SMIE's when matching paired keywords like "begin
> ... end".

OK, that's a very good answer.  :-)

[ ... ]

> > +  "If non-nil, show parens when point is in the line's periphery.
> > +The periphery is at the beginning or end of a line or in any
> > +whitespace there."
> > +  :type 'boolean
> > +  :group 'paren-showing
> > +  :version "25.1")

> ":group 'paren-showing" is redundant.

Hmm.  The :prefix keyword for `defgroup' is not mentioned in the doc
string for `defcustom' (which is where `defgroup' points you for details
of keywords).  I haven't checked the elisp manual, though.  This seems
like a little bug.

I've removed all the instances of :group.

> Add a ":type 'boolean".

Done.

> Functions that end in "-p" should only return a boolean (i.e. the return
> value doesn't have to be "t or nil" but the non-nil value should have no
> other meaning than being non-nil and the callers shouldn't rely on it
> being anything in particular other than "non-nil").

> I know we sadly don't always follow this principle (e.g. `framep'), but
> let's try not to make things worse.

By incorporating the pertinent function into s-p--categorize-paren, the
issue has resolved itself.

> > +     ;; Point is immediately inside a paren.
> > +     ((show-paren--unescaped-p before))
> > +     ((show-paren--unescaped-p after))

> This should be controlled by a config var since it's new behavior.
> AFAIK we can (re)use show-paren-when-point-in-periphery just fine.

I invented a fresh defconfig for that.

> BTW, maybe the "unescaped" check could be folded into
> show-paren--categorize-paren.

Done.  (More precisely, a function to do the check is called directly
from s-p--categorize-p).

> > -Where HERE-BEG..HERE-END is expected to be around point.")
> > +Where HERE-BEG..HERE-END is expected to be around the paren.")

> I think "near point" is closer to the original intention than "around
> the paren", especially since it might not be a parenthesis.  Also since
> we shouldn't expect it to be around point, the additional patch below is
> probably needed.

"near point" it is.  I've put in the change below.  This is presumably
needed for when point is inside a keyword like "begin" when the smie-mode
function is in use.

> Other than that, feel free to install it,

Done.

I'll start reworking the page "Matching" of programs.texi.  It looks like
the page could be becoming uncomfortably big.  Some of paren.el's config
variables will need documenting, but not all of them, some of them being
somewhat arcane (e.g. the 0.125s delay).

>         Stefan


> === modified file 'lisp/paren.el'
> --- lisp/paren.el	2014-02-10 01:34:22 +0000
> +++ lisp/paren.el	2014-10-16 17:36:28 +0000
> @@ -216,6 +216,7 @@
>          (if (or (not here-beg)
>                  (and (not show-paren-highlight-openparen)
>                       (> here-end (point))
> +                     (<= here-beg (point))
>                       (integerp there-beg)))
>              (delete-overlay show-paren--overlay-1)
>            (move-overlay show-paren--overlay-1

-- 
Alan Mackenzie (Nuremberg, Germany).



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

* Re: Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin.
  2014-10-16 21:26                       ` Alan Mackenzie
@ 2014-10-17  0:14                         ` Stefan Monnier
  0 siblings, 0 replies; 23+ messages in thread
From: Stefan Monnier @ 2014-10-17  0:14 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: emacs-devel

>> Otherwise it seems way too weird/ad-hoc to hardcode it, so make
>> it dependent on a config var.
> I think that our marginal utility will drop off very sharply, the more
> complicated our configurability becomes.

The purpose is not really to make it configurable, but to isolate the
code that provides this part of the functionality.

> Since point is on the same line as the paren, and is in the WS at either
> BOL or EOL, and the (at most) two eligible parameters are on the edge of
> the "core", point can't be between the two candidates.  The nearest one
> is clear, surely.

Right, I was thinking about the case in my suggestion of highlighting
the closing paren in at the end of the previous line.

> Hmm.  The :prefix keyword for `defgroup' is not mentioned in the doc
> string for `defcustom' (which is where `defgroup' points you for details
> of keywords).

I think the :prefix only makes sense for defgroup, not for defcustom.
Many keywords are like that, so maybe defgroup shouldn't delegate to
defcustom's docstring.

> I've removed all the instances of :group.

Thanks.

> Done.  (More precisely, a function to do the check is called directly
> from s-p--categorize-p).

Yup, looks nice, thanks.


        Stefan



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

end of thread, other threads:[~2014-10-17  0:14 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-10-11 13:43 Proposed extension of show-paren-mode: Highlight parens when point is in L or R margin Alan Mackenzie
2014-10-11 14:21 ` Eli Zaretskii
2014-10-12  8:39   ` Alan Mackenzie
2014-10-12  8:55     ` David Kastrup
2014-10-12  9:25       ` Alan Mackenzie
2014-10-12  9:01     ` Eli Zaretskii
2014-10-12 10:18       ` Alan Mackenzie
2014-10-12  4:12 ` Stefan Monnier
2014-10-12 10:04   ` Alan Mackenzie
2014-10-14 17:49     ` Stefan Monnier
2014-10-14 18:32       ` John Yates
2014-10-15  9:12       ` Alan Mackenzie
2014-10-15 22:38         ` Andy Moreton
2014-10-16  2:43         ` Stefan Monnier
2014-10-16  9:53           ` Alan Mackenzie
2014-10-16 12:59             ` Stefan Monnier
2014-10-16 13:31               ` Alan Mackenzie
2014-10-16 14:37                 ` Stefan Monnier
2014-10-16 15:46                   ` Alan Mackenzie
2014-10-16 17:40                     ` Stefan Monnier
2014-10-16 21:26                       ` Alan Mackenzie
2014-10-17  0:14                         ` Stefan Monnier
2014-10-14 21:50 ` João Távora

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).