GNU bug report logs - #16981
24.3.50; electric-pair-delete-adjacent-pairs broken in c-mode, python-mode, maybe-others

Previous Next

Package: emacs;

Reported by: joaotavora <at> gmail.com (João Távora)

Date: Mon, 10 Mar 2014 19:49:01 UTC

Severity: normal

Found in version 24.3.50

Done: joaotavora <at> gmail.com (João Távora)

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 16981 in the body.
You can then email your comments to 16981 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#16981; Package emacs. (Mon, 10 Mar 2014 19:49:01 GMT) Full text and rfc822 format available.

Acknowledgement sent to joaotavora <at> gmail.com (João Távora):
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Mon, 10 Mar 2014 19:49:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: joaotavora <at> gmail.com (João Távora)
To: bug-gnu-emacs <at> gnu.org
Subject: 24.3.50;
 electric-pair-delete-adjacent-pairs broken in c-mode, python-mode,
 maybe-others
Date: Mon, 10 Mar 2014 19:47:39 +0000
This variable, t by default, acts when `electric-pair-mode' is enabled.

It doesn't work in c-mode, python-mode and maybe other modes in
emacs. In also doesn't work in other modes outside of emacs, such as
markdown-mode.el [1]

All these modes rebind the backspace key to a command that does some
mode-specific action, then calls `backward-delete-char-untabify'.

elec-pair.el does mostly the same through remapping in its
`electric-pair-mode-map' but this binding is overriden by the
major-mode's.

elec-pair.el remaps two other commands similarly. Here's the value of
electric-pair-mode-map

    (keymap
     (remap keymap
            (delete-backward-char . electric-pair-backward-delete-char)
            (backward-delete-char . electric-pair-backward-delete-char)
            (backward-delete-char-untabify
               . electric-pair-backward-delete-char-untabify)))


Perhaps a hook in `backward-delete-char' is in order. Or the other major
modes can find other ways to overload the backspace key.

Thanks,
João

[1]: http://jblevins.org/git/markdown-mode.git/plain/markdown-mode.el






Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#16981; Package emacs. (Thu, 13 Mar 2014 22:12:02 GMT) Full text and rfc822 format available.

Message #8 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Alan Mackenzie <acm <at> muc.de>
To: gnu-emacs-bug <at> moderators.isc.org
Subject: Re: bug#16981: 24.3.50;
 electric-pair-delete-adjacent-pairs broken in c-mode,
 python-mode,	maybe-others
Date: Thu, 13 Mar 2014 22:04:33 +0000 (UTC)
Jo?o T?vora <joaotavora <at> gmail.com> wrote:
> This variable, t by default, acts when `electric-pair-mode' is enabled.

> It doesn't work in c-mode, python-mode and maybe other modes in
> emacs. In also doesn't work in other modes outside of emacs, such as
> markdown-mode.el [1]

> All these modes rebind the backspace key to a command that does some
> mode-specific action, then calls `backward-delete-char-untabify'.

> elec-pair.el does mostly the same through remapping in its
> `electric-pair-mode-map' but this binding is overriden by the
> major-mode's.

There's something funny going on here.  A minor mode's keymap should
normally take precedence over the major mode's keymap.  (See the page 
"Searching Keymaps" in the Elisp manual.)  So why is electric-pair-mode's
keymap being overridden by CC Mode's here?

> elec-pair.el remaps two other commands similarly. Here's the value of
> electric-pair-mode-map

>    (keymap
>     (remap keymap
>            (delete-backward-char . electric-pair-backward-delete-char)
>            (backward-delete-char . electric-pair-backward-delete-char)
>            (backward-delete-char-untabify
>               . electric-pair-backward-delete-char-untabify)))


> Perhaps a hook in `backward-delete-char' is in order. Or the other major
> modes can find other ways to overload the backspace key.

I would say it's legitimate for a major mode to bind the backspace key, but
it's more questionable for a minor mode to do the same.  Perhaps the minor
mode really ought to use defadvice rather than rebinding the key.

> Thanks,
> Jo?o

> [1]: http://jblevins.org/git/markdown-mode.git/plain/markdown-mode.el

-- 
Alan Mackenzie (Nuremberg, Germany).





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#16981; Package emacs. (Sat, 05 Apr 2014 11:59:02 GMT) Full text and rfc822 format available.

Message #11 received at 16981 <at> debbugs.gnu.org (full text, mbox):

From: joaotavora <at> gmail.com (João Távora)
To: Alan Mackenzie <acm <at> muc.de>
Cc: 16981 <at> debbugs.gnu.org, monnier <at> iro.umontreal.ca
Subject: Re: bug#16981: 24.3.50;
 electric-pair-delete-adjacent-pairs broken in c-mode,
 python-mode,	maybe-others
Date: Sat, 05 Apr 2014 12:58:36 +0100
Hi Alan,

Sorry for the delay, I only check emacs-devel regularly, do cc me next
time please if you remember.

> There's something funny going on here.  A minor mode's keymap should
> normally take precedence over the major mode's keymap.  (See the page 
> "Searching Keymaps" in the Elisp manual.)  So why is electric-pair-mode's
> keymap being overridden by CC Mode's here?

You're right, but I can't figure out what. Probably has to do with
remapping and translation. But if it were the other way around, you'd
probably be reporting the bug :-) so it doesn't matter.

>> Perhaps a hook in `backward-delete-char' is in order. Or the other major
>> modes can find other ways to overload the backspace key.
> I would say it's legitimate for a major mode to bind the backspace key, but
> it's more questionable for a minor mode to do the same.  Perhaps the minor
> mode really ought to use defadvice rather than rebinding the key.

Yes, I think so too. Or both can use a (possibly excessively
complicated) system of backspace-related hooks.  Advice would probably
work too, but do we want that in emacs? Stefan, when you suggested the
rebinding originally, was it with any particular concern in mind?

Why not this, just the single `delete-backward-char-before-hook'. Seems
to work. Maybe also add a "don't use this in programs" to
`delete-backward-char'...

=== modified file 'lisp/elec-pair.el'
*** lisp/elec-pair.el	2014-04-04 23:31:02 +0000
--- lisp/elec-pair.el	2014-04-05 11:28:42 +0000
***************
*** 166,176 ****
  quotes or comments.  If lookup fails here, `electric-pair-text-pairs' will
  be considered.")
  
! (defun electric-pair-backward-delete-char (n &optional killflag untabify)
!   "Delete characters backward, and maybe also two adjacent paired delimiters.
! 
! Remaining behavior is given by `backward-delete-char' or, if UNTABIFY is
! non-nil, `backward-delete-char-untabify'."
    (interactive "*p\nP")
    (let* ((prev (char-before))
           (next (char-after))
--- 166,173 ----
  quotes or comments.  If lookup fails here, `electric-pair-text-pairs' will
  be considered.")
  
! (defun electric-pair-delete-pair-maybe (killflag)
!   "Forward-delete pair of soon-to-be-deleted char before point."
    (interactive "*p\nP")
    (let* ((prev (char-before))
           (next (char-after))
***************
*** 184,200 ****
                   electric-pair-delete-adjacent-pairs)
                 (memq syntax '(?\( ?\" ?\$))
                 (eq pair next))
!       (delete-char 1 killflag))
!     (if untabify
!         (backward-delete-char-untabify n killflag)
!         (backward-delete-char n killflag))))
! 
! (defun electric-pair-backward-delete-char-untabify (n &optional killflag)
!   "Delete characters backward, and maybe also two adjacent paired delimiters.
  
! Remaining behavior is given by `backward-delete-char-untabify'."
!   (interactive "*p\nP")
!   (electric-pair-backward-delete-char n killflag t))
  
  (defun electric-pair-conservative-inhibit (char)
    (or
--- 181,189 ----
                   electric-pair-delete-adjacent-pairs)
                 (memq syntax '(?\( ?\" ?\$))
                 (eq pair next))
!       (delete-char 1 killflag))))
  
! (add-hook 'delete-backward-char-before-hook 'electric-pair-delete-pair-maybe)
  
  (defun electric-pair-conservative-inhibit (char)
    (or
***************
*** 546,562 ****
         (memq (car (electric-pair-syntax-info last-command-event))
               '(?\( ?\) ?\" ?\$))))
  
- (defvar electric-pair-mode-map
-   (let ((map (make-sparse-keymap)))
-     (define-key map [remap backward-delete-char-untabify]
-       'electric-pair-backward-delete-char-untabify)
-     (define-key map [remap backward-delete-char]
-       'electric-pair-backward-delete-char)
-     (define-key map [remap delete-backward-char]
-       'electric-pair-backward-delete-char)
-     map)
-   "Keymap used by `electric-pair-mode'.")
- 
  ;;;###autoload
  (define-minor-mode electric-pair-mode
    "Toggle automatic parens pairing (Electric Pair mode).
--- 535,540 ----

=== modified file 'lisp/simple.el'
*** lisp/simple.el	2014-04-02 15:14:50 +0000
--- lisp/simple.el	2014-04-05 11:27:17 +0000
***************
*** 949,954 ****
--- 949,959 ----
  is undefined.  If DELETE is nil, just return the content as a string.
  If anything else, delete the region and return its content as a string.")
  
+ (defvar delete-backward-char-before-hook nil
+   "Hook run just before `delete-backward-char' actually deletes.
+ Each function in this list is passed the KILLFLAG arg to
+ `delete-backward-char' call.")
+ 
  (defun delete-backward-char (n &optional killflag)
    "Delete the previous N characters (following if N is negative).
  If Transient Mark mode is enabled, the mark is active, and N is 1,
***************
*** 984,990 ****
  	   (save-excursion
  	     (insert-char ?\s (- ocol (current-column)) nil))))
  	;; Otherwise, do simple deletion.
! 	(t (delete-char (- n) killflag))))
  
  (defun delete-forward-char (n &optional killflag)
    "Delete the following N characters (previous if N is negative).
--- 989,997 ----
  	   (save-excursion
  	     (insert-char ?\s (- ocol (current-column)) nil))))
  	;; Otherwise, do simple deletion.
! 	(t
!          (run-hook-with-args 'delete-backward-char-before-hook killflag)
!          (delete-char (- n) killflag))))
  
  (defun delete-forward-char (n &optional killflag)
    "Delete the following N characters (previous if N is negative).






Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#16981; Package emacs. (Sat, 05 Apr 2014 15:46:02 GMT) Full text and rfc822 format available.

Message #14 received at 16981 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> IRO.UMontreal.CA>
To: joaotavora <at> gmail.com (João Távora)
Cc: 16981 <at> debbugs.gnu.org, Alan Mackenzie <acm <at> muc.de>
Subject: Re: bug#16981: 24.3.50;
 electric-pair-delete-adjacent-pairs broken in c-mode,
 python-mode,	maybe-others
Date: Sat, 05 Apr 2014 11:45:09 -0400
>> There's something funny going on here.  A minor mode's keymap should
>> normally take precedence over the major mode's keymap.  (See the page 
>> "Searching Keymaps" in the Elisp manual.)  So why is electric-pair-mode's
>> keymap being overridden by CC Mode's here?
> You're right, but I can't figure out what.

That's because it's how remapping is works: first we lookup the key
(minor-maps, local-map, global-map), then we lookup the command to see
if it's remapped (minor-maps, local-map, global-map).
So the local-map and global-map's normal bindings take precedence over the
minor-map's remapping.

>>> Perhaps a hook in `backward-delete-char' is in order. Or the other major
>>> modes can find other ways to overload the backspace key.
>> I would say it's legitimate for a major mode to bind the backspace key, but
>> it's more questionable for a minor mode to do the same.  Perhaps the minor
>> mode really ought to use defadvice rather than rebinding the key.

How 'bout

  (define-key electric-pair-mode-map "\177"
    `(menu-item "" electric-pair-delete-pair
      :filter
      ,(lambda (cmd)
         (let* ((prev (char-before))
                (next (char-after))
                (syntax-info (and prev
                                  (electric-pair-syntax-info prev)))
                (syntax (car syntax-info))
                (pair (cadr syntax-info)))
           (and next pair
                (memq syntax '(?\( ?\" ?\$))
                (eq pair next)
                (if (functionp electric-pair-delete-adjacent-pairs)
                    (funcall electric-pair-delete-adjacent-pairs)
                  electric-pair-delete-adjacent-pairs)
                cmd))))))


-- Stefan




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#16981; Package emacs. (Sun, 06 Apr 2014 01:52:02 GMT) Full text and rfc822 format available.

Message #17 received at 16981 <at> debbugs.gnu.org (full text, mbox):

From: joaotavora <at> gmail.com (João Távora)
To: Stefan Monnier <monnier <at> IRO.UMontreal.CA>
Cc: 16981 <at> debbugs.gnu.org, Alan Mackenzie <acm <at> muc.de>
Subject: Re: bug#16981: 24.3.50;
 electric-pair-delete-adjacent-pairs broken in c-mode,
 python-mode,	maybe-others
Date: Sun, 06 Apr 2014 02:51:07 +0100
Stefan Monnier <monnier <at> IRO.UMontreal.CA> writes:

>>> There's something funny going on here.  A minor mode's keymap should
>> You're right, but I can't figure out what.
> That's because it's how remapping is works: first we lookup the key
> So the local-map and global-map's normal bindings take precedence over the
> minor-map's remapping.

Yeah that's it I remembered that this afternoon.

>   (define-key electric-pair-mode-map "\177"
>     `(menu-item "" electric-pair-delete-pair
>       :filter

>                   electric-pair-delete-adjacent-pairs)
>                 cmd))))))

Aha, the famed :filter trick I had heard about. Looks good enough while
we wait for that multiple-commands-per-keybinding refactoring. Not as
clean as remapping, but probably good enough, and it's arguable we want
this on any other key than backspace.

Hmmm now I remember that's it's even harder than defadvice to find the
extra code, docstring-wise I mean. How would a hacker discover we're
using the filter trick. describe-key doesn't help. .

Anyway should I install it? To emacs-24 only? I need a little help
VC-wise:

- I got two commits that are in trunk, r116926 and r116940, and should
be in (cherry-picked to?) emacs-24

- Three different fixes (bugs 16981,17192 and 17183) not yet pushed that
I think you want me to only push to emacs-24, since that will be merged
back to trunk later.

Is this correct? I also don't know much bzr to do this, especially the
first item. Is there a wiki as clear as BzrForEmacsDevs on this
particular workflow? I just did a "bzr switch emacs-24" from what I
believe to be a heavyweight checkout or something. It seems to have
worked.









Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#16981; Package emacs. (Sun, 06 Apr 2014 12:49:02 GMT) Full text and rfc822 format available.

Message #20 received at 16981 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> IRO.UMontreal.CA>
To: joaotavora <at> gmail.com (João Távora)
Cc: 16981 <at> debbugs.gnu.org, Alan Mackenzie <acm <at> muc.de>
Subject: Re: bug#16981: 24.3.50;
 electric-pair-delete-adjacent-pairs broken in c-mode,
 python-mode,	maybe-others
Date: Sun, 06 Apr 2014 08:48:21 -0400
> Hmmm now I remember that's it's even harder than defadvice to find the
> extra code, docstring-wise I mean. How would a hacker discover we're
> using the filter trick. describe-key doesn't help. .

Right.  C-h k will give you the binding, but only when
electric-pair-delete-pair would be run.  To make this trick more widely
usable, we'd need to change C-h k so that it shows all "possible bindings".

> Anyway should I install it?

If that's OK with you, yes.

> To emacs-24 only?

Yes.

> - I got two commits that are in trunk, r116926 and r116940, and should
> be in (cherry-picked to?) emacs-24

Just take the corresponding diffs (IIRC you can use
"bzr merge -r116925..r116926 .../trunk" for that) and commit them into
emacs-24.

Bzr doesn't really know about cherry picking, but if you put
"backported" into your commit message, our bzrmerge.el script
will know that this commit is already in the trunk and will know how to
avoid the corresponding conflicts when merging emacs-24 back into trunk.

> - Three different fixes (bugs 16981,17192 and 17183) not yet pushed that
> I think you want me to only push to emacs-24, since that will be merged
> back to trunk later.

Right.


        Stefan




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#16981; Package emacs. (Sun, 06 Apr 2014 15:10:01 GMT) Full text and rfc822 format available.

Message #23 received at 16981 <at> debbugs.gnu.org (full text, mbox):

From: João Távora <joaotavora <at> gmail.com>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: 16981 <at> debbugs.gnu.org, Alan Mackenzie <acm <at> muc.de>
Subject: Re: bug#16981: 24.3.50; electric-pair-delete-adjacent-pairs broken in
 c-mode, python-mode, maybe-others
Date: Sun, 6 Apr 2014 16:08:39 +0100
On Sun, Apr 6, 2014 at 1:48 PM, Stefan Monnier <monnier <at> iro.umontreal.ca> wrote:
>> Hmmm now I remember that's it's even harder than defadvice to find the
>> extra code, docstring-wise I mean. How would a hacker discover we're
>> using the filter trick. describe-key doesn't help. .
>
> Right.  C-h k will give you the binding, but only when
> electric-pair-delete-pair would be run.  To make this trick more widely
> usable, we'd need to change C-h k so that it shows all "possible bindings".

Yes, I realized that later on.

In the meantime, we can use the trick that yasnippet uses
(documentation-function if I'm not mistaken) to show what other command
electric-pair-delete-pair is shadowing in that situation.

>> Anyway should I install it?
> If that's OK with you, yes.

Yes, I think it's cleaner than all alternatives, unless add-function is
the way to go for these situations.

> Bzr doesn't really know about cherry picking, but if you put
> "backported" into your commit message, our bzrmerge.el script
> will know that this commit is already in the trunk and will know how to
> avoid the corresponding conflicts when merging emacs-24 back into trunk.

Alright, I'll include "backported from trunk, revs x and y".




Reply sent to joaotavora <at> gmail.com (João Távora):
You have taken responsibility. (Mon, 07 Apr 2014 00:05:02 GMT) Full text and rfc822 format available.

Notification sent to joaotavora <at> gmail.com (João Távora):
bug acknowledged by developer. (Mon, 07 Apr 2014 00:05:03 GMT) Full text and rfc822 format available.

Message #28 received at 16981-done <at> debbugs.gnu.org (full text, mbox):

From: joaotavora <at> gmail.com (João Távora)
To: Stefan Monnier <monnier <at> IRO.UMontreal.CA>
Cc: 16981-done <at> debbugs.gnu.org, 17192-done <at> debbugs.gnu.org,
 17183-done <at> debbugs.gnu.org, Alan Mackenzie <acm <at> muc.de>
Subject: Re: bug#16981: 24.3.50;
 electric-pair-delete-adjacent-pairs broken in c-mode,
 python-mode,	maybe-others
Date: Mon, 07 Apr 2014 01:04:35 +0100
Stefan Monnier <monnier <at> IRO.UMontreal.CA> writes:

> Just take the corresponding diffs (IIRC you can use
> "bzr merge -r116925..r116926 .../trunk" for that) and commit them into
> emacs-24.
>
> Bzr doesn't really know about cherry picking, but if you put
> "backported" into your commit message, our bzrmerge.el script
> will know that this commit is already in the trunk and will know how to
> avoid the corresponding conflicts when merging emacs-24 back into trunk.
>
>> - Three different fixes (bugs 16981,17192 and 17183) not yet pushed that
>> I think you want me to only push to emacs-24, since that will be merged
>> back to trunk later.
>
> Right.

These three I did successfully, but failed miserably in the
backporting. Oh git where art thou... Now I'm downloading a new repo
(with sharing properly setup hopefully), since I must have done
something that messed up my bzr conf, apparently I shouldn't have moved
my repo dir.

Should be done tomorrow morning, so I'll backport r116926 and r116940
then.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Mon, 05 May 2014 11:24:04 GMT) Full text and rfc822 format available.

This bug report was last modified 11 years and 35 days ago.

Previous Next


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