all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Psionic K <psionik@positron.solutions>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: Ihor Radchenko <yantar92@posteo.net>,
	Emacs developers <emacs-devel@gnu.org>
Subject: Re: Delegating user-reserved key binding space definition to users
Date: Mon, 28 Nov 2022 23:22:24 -0600	[thread overview]
Message-ID: <CADQMGAQfS6cf3CkH8Y+Qg7xoth9prcJtk3rTP70E1SPRYFtX6w@mail.gmail.com> (raw)
In-Reply-To: <jwvfse2e8wr.fsf-monnier+emacs@gnu.org>

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

> Can you try and make it more
> concrete with an example?

;; The user will specify concept / abstract commands -> key sequences
(setq-custom concept-sequences '((user-next ?\M-n) (user-prev ?\M-p)))
;; The package will specify concepts / abstract commands -> concrete
commands
;; So, something like a magit section mode intended to be derived from, the
package author writes:
(setq-local concept-commands '((user-next #'magit-section-next) (user-prev
#'magit-section-prev)))
;; And then the package tells the implementation to consume the
declarations, which might occur after the mode hook to let the user
intervene
(emacs-function-setup-concept-keymap)

`emacs-function-setup-concept-keymap' would be an implementation that ties
the user's declaration of sequence -> concept mappings together with the
package's concept -> command mappings.  The result is that the local map
should contain some command remap shadows corresponding to what the user
and the package declared.

I neglected overloading in my example, but basically a user might be okay
with several concepts being able to map to one key and the package might be
okay with mapping the same command to one of several concepts.

IMO these declarations one-to-many declarations should only be used with
two specific conventions:

   1. Package authors specify a list of increasingly more generic concepts,
   ending with `user-generic' which would just mean "any key that the user has
   bestowed upon packages by default.".  An example of more abstract
   definitions would be a declaration like (#'magit-section-next (user-next
   user-navigation)).   The implementation could try to map to user-next and
   if that's not available, use an available sequence from user-navigation.
   2. The user's convention for one-to-many mappings is different.  They
   might want to map a generic concept onto several key sequences in order, so
   they might declare something like '(user-navigation (?\M-p ?\M-n ?\M-f
   ?\M-b)).  If a package declares a user-next, user-prev, and two more
   user-navigation commands, the implementation will give them all keymap
   elements.

> I get the impression that your
> SOME-INFO includes info for several commands.

Yes.  The reason commands should not handle this declaration is because the
implementation would have to discover the commands, and this would be
fragile if the user starts replacing commands or wants to specify commands
that the package has no idea about.

I'm also preoccupied with automating the user-driven re-mapping of commands
to key sequences when those commands express an extremely similar idea,
such as "next".  I see this as a prerequisite for what I think you want.  I
don't think I agree with what I think you want, and this could be messing
with my interpretation, but it is a prerequisite still.

We have lots of convention, lots of similarity in mode keymaps, but we it's
ad-hoc, and we just need to make it official so that the conventions are
useful and user's can change one declaration to move, for example, C-g.

> It should likely
> include also some notion of scope (global, buffer-local, local to
> a region, specific to some particular set of major mode, only
> meaningful when the region is active, ...)

The purpose of an abstract key mapping scheme is to allow the user to
coherently move a binding whenever that binding expresses the same idea in
several modes.  Complex, highly configurable declarations would actually
make the situation worse, requiring more user effort to accomplish the
move.  There are already tools available for handling 1:1 edge cases, one
declaration at a time.  Many commands that need such configurability tweak
their behavior based on the call context or some state.  We don't need new
infrastructure for specifying a large number of edge cases.  Such schemes
become baked into modal systems like Evil.

> Emacs passes that to a "procedural keymap" which will
>   *compute* (rather than lookup) which command to run, according to the
>   current keybinding style.

This kind of dynamic dispatch is not actually necessary because commands
already can decide to do something differently.  A command can wrap several
commands.  Commands can react to variables.  Commands can inspect their
context.  The big reason we want this dynamic dispatch style behavior
encoded into commands themselves is because it plays well with the notion
of mapping commands and sequences.  Where-is doesn't work well with dynamic
maps like transient.  Users can't declare keys succinctly if they are
supposed to make their declarations aware of state that the commands should
instead know in their body.  An implementation for generating the concrete
bindings would be much more complex if it had to know about information
that should be internal to the command.

Package authors can always create the most power for users by having a good
data model and good functions with which users can construct their own
commands.  No matter how dynamic dispatch declarations are done, they are
going to be harder to understand and implement than simply re-writing new
command bodies.  Even writing schemes for evil, which only has to support
one implementation, makes some gnarly looking declarations.

The temptation to add power will harm the poor user with the humble goal to
coherently move C-n to M-n in modes where it makes obvious sense because
their pinky hurts.

On Mon, Nov 28, 2022 at 10:01 PM Stefan Monnier <monnier@iro.umontreal.ca>
wrote:

> >> a solution should allow packages to declare that command FOO
> >> should be bound to some key based on SOME-INFO, such that it will be
> >> bound to one key in "normal Emacs mode", and to another in `evil-mode`
> >> and to yet another in `god-mode`, etc...
> >
> > SOME-INFO will be of the form:
> > ((concept command) (concept command) ...)) ; package provide
> > ((concept (concept concept concept)) (concept concept)) ; overload
> > remappings, perhaps user or package provided or both
> > ((concept binding) (concept binding)) ; user or emulation mode etc
> provided
>
> I'm afraid I don't know what this means.  Can you try and make it more
> concrete with an example?
>
> Note that in the text I wrote, SOME-INFO was supposed to be a piece of
> information specific to the command FOO.  I get the impression that your
> SOME-INFO includes info for several commands.  Maybe we'll have to do
> that, but I was hoping we could avoid it.
>
> > In the beginning, before packages provide their half, the user will
> provide
> > it, likely through a package.  This is analogous to evil collection.
>
> I don't want SOME-INFO to explicitly say what should happen for
> `evil-mode`, vanilla Emacs mode, `god-mode`, `boon-mode`,
> `ergoemacs-mode`, modalka, ...
> This is the mess we already have.
>
> Instead SOME-INFO should give just enough data to those modes such that
> they can wisely (and predictably) choose a binding for that command.
>
> >> To me a solution should allow packages to declare that command FOO
> >> should be bound to some key based on SOME-INFO, such that it will be
> >> bound to one key in "normal Emacs mode", and to another in `evil-mode`
> >> and to yet another in `god-mode`, etc...
> >
> > This places too much emphasis on package authors.
>
> I hope with my above explanation you can agree that it also gives a lot
> of power to the keybinding styles.  And of course, ultimately the
> end-user can override any of those decisions (this is Emacs we're
> talking about, after all).
>
> > As stated above, I believe it's the job of modal systems to
> > decide how to consume the package-defined half of SOME-INFO.
>
> IIUC we violently agree.
>
> I personally don't yet have a clear idea of what SOME-INFO may look
> like.  I suspect it could include some letters (used as hints to help
> the keybinding-style choose keys) plus a set of "features" maybe (like
> forward/up/down/backward for direction, or add/remove, or
> char/word/symbol/sexp/buffer for granularity/subject).  It should likely
> include also some notion of scope (global, buffer-local, local to
> a region, specific to some particular set of major mode, only
> meaningful when the region is active, ...).
>
>
>         Stefan
>
>

-- 

Psionic K <psionik@positron.solutions>
Software Engineer

*Positron Solutions <https://positron.solutions>*

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

  reply	other threads:[~2022-11-29  5:22 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-21 14:40 Delegating user-reserved key binding space definition to users Psionic K
2022-11-21 19:37 ` Stefan Monnier
2022-11-22  2:07   ` Phil Sainty
2022-11-25  2:48     ` Psionic K
2022-11-25  3:31       ` Ihor Radchenko
2022-11-25 13:53         ` xenodasein--- via Emacs development discussions.
2022-11-25 15:16       ` Stefan Monnier
2022-11-26  6:44         ` Ihor Radchenko
2022-11-26 17:29           ` Stefan Monnier
2022-11-27  5:45             ` Ihor Radchenko
2022-11-27 11:26               ` Psionic K
2022-11-27 11:53                 ` Psionic K
2022-11-28 18:15               ` Stefan Monnier
2022-11-28 18:37                 ` Eli Zaretskii
2022-11-29  2:38                 ` Psionic K
2022-11-29  4:01                   ` Stefan Monnier
2022-11-29  5:22                     ` Psionic K [this message]
2022-11-29 13:03                       ` Eli Zaretskii
2022-11-30  6:23                         ` Psionic K
2022-11-30  9:01                           ` xenodasein--- via Emacs development discussions.
2022-11-30 13:44                           ` Eli Zaretskii
2022-11-29 11:54                 ` John Yates

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CADQMGAQfS6cf3CkH8Y+Qg7xoth9prcJtk3rTP70E1SPRYFtX6w@mail.gmail.com \
    --to=psionik@positron.solutions \
    --cc=emacs-devel@gnu.org \
    --cc=monnier@iro.umontreal.ca \
    --cc=yantar92@posteo.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.