Subject: Re: bug#79047: Add some multi-character pairs to some major modes.

From: Alan Mackenzie <[email-masked]>
Date: Thu, 24 Jul 2025 15:54:14 +0000

Hello, Eli.

On Thu, Jul 24, 2025 at 17:53:55 +0300, Eli Zaretskii wrote:
> > Date: Thu, 24 Jul 2025 13:39:16 +0000
> > Cc: [email-masked], Eli Zaretskii <[email-masked]>,
> >   John Muhl <[email-masked]>, Stefan Monnier <[email-masked]>
> > From: Alan Mackenzie <[email-masked]>

> > Yes, sort of.  But electric pair mode's implementation violates an
> > (unwritten) Emacs convention about hooks, i.e. that a hook function
> > doesn't mess with the functionality that invoked it.  For example, a
> > function in window-scroll-functions may not change the scrolling state of
> > a window, and an after-change-functions function never changes the text
> > of a buffer.

> > By contrast, electric-pair-post-self-insert-function does mess with the
> > invoking functionality, namely self-insert-command; it inserts further
> > characters.  From the point of view of a Lisp program, this makes
> > self-insert-function an undefined function - what it does varies from
> > time to time and from buffer to buffer.

> ?? post-self-insert-hook is specifically mean for features that insert
> additional characters, or even replace characters by others.
> Otherwise, what would that hook be good for?

Nothing, as far as I can see.  I see no legitimate need for that hook.

> The ELisp manual says about this: "You could use this to automatically
> reindent text as it is typed, for example."  And what is reindentation
> if not removal of some characters and insertion of others?

> So I don't understand the nature of your dislike in this case.

I'll start off with the abstract:

It breaks the abstraction mentioned in my first paragraph above that
hook functions do things ancillary to the main function, and do not
constitute part of it.  So that, for example, in an
after-change-functions function, you know that no deletion by a previous
a-c-f will have made the START, END, and OLD-LEN arguments invalid.

This principle holds for all hook uses I'm aware of except for
post-self-insert-function.  It enables Lisp coding to be done sensibly
without having to program around all the "what if"s which would arise by
people programming buffer changes into a-c-f's, or amending the
scrolling inside a window-scroll-functions function for example.

Now the concrete:

c-electric-paren, for example, does EOL cleanups after inserting the ?\(
the user typed using self-insert-command - this involves detecting that
the newly inserted ?\( is at EOL (modulo whitespace).  This breaks when
electric-pair-post-self-insert-function (or anything else on that hook)
inserts further characters after the one the user has typed.

As a workaround, I bind post-self-insert-hook to nil around the call to
self-insert-command, this being the only way I know to get predictable
functionality from self-insert-command.  Later I have to call
electric-pair-post-self-insert-function explicitly from
c-electric-paren.  This is neither robust nor future proof.

The error here is that post-self-insert-hook is invoked from the middle
of the command using self-insert-function, rather than after its end.
One way of fixing this would be to put
electric-pair-post-self-insert-function on post-command-hook instead.

-- 
Alan Mackenzie (Nuremberg, Germany).