From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Psionic K Newsgroups: gmane.emacs.devel Subject: Re: Delegating user-reserved key binding space definition to users Date: Thu, 24 Nov 2022 20:48:04 -0600 Message-ID: References: <57b69c22e167d429d21bb969feb22887@webmail.orcon.net.nz> Mime-Version: 1.0 Content-Type: multipart/alternative; boundary="000000000000b1a0b805ee428da9" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="23368"; mail-complaints-to="usenet@ciao.gmane.io" To: Emacs developers Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Fri Nov 25 03:49:05 2022 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1oyOm1-0005us-Eu for ged-emacs-devel@m.gmane-mx.org; Fri, 25 Nov 2022 03:49:05 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1oyOlU-0001ue-9l; Thu, 24 Nov 2022 21:48:32 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oyOlM-0001ty-2z for emacs-devel@gnu.org; Thu, 24 Nov 2022 21:48:28 -0500 Original-Received: from mail-pf1-x42f.google.com ([2607:f8b0:4864:20::42f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1oyOlI-0006p8-Rp for emacs-devel@gnu.org; Thu, 24 Nov 2022 21:48:23 -0500 Original-Received: by mail-pf1-x42f.google.com with SMTP id w129so2967018pfb.5 for ; Thu, 24 Nov 2022 18:48:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=positron.solutions; s=google; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :from:to:cc:subject:date:message-id:reply-to; bh=rieTxiQiKrw+KYJfw7lDmxCCrgW1055jj2ahoNFoMX4=; b=YeTQCypP97UEkC0zn9pxwFdlk91ZBqnrXDe44vu7jfL6JpynfAvwJrSPLz/myNDVOg FQwZxY5Ex9ma3pY/mUoYSjgsdZ52xzgA1dfDz8ca2+iBCD8aE1W0FY5gkIjMmVZGgzkf S21tbvFCRZJ7maEf4KWV0P2nFzvEli/uvcqGuCNKnSzsyJPNVLr+w6MBVWtawOovNNBL CltNPi38jxH8m6RuvyFV0cFy8OuNvPJtQN9uoR4lK6Wa2O0aebu1QSsZ2evH961y3lHi r2Eb65zK6EM5/NecjSTdqF7+Gsx/U1KFlIyrXILXXYiDVgjB4atVHG9Juyw440Jqm42q gHBg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=to:subject:message-id:date:from:in-reply-to:references:mime-version :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=rieTxiQiKrw+KYJfw7lDmxCCrgW1055jj2ahoNFoMX4=; b=EbcD0RpCPvuN6490DImrX3GsXI/C19VWukkAbeUgWL4RuBZdqgAWecPTYxFi9UFC+r Twb4Nni+Wlcf027RxiusZvJJC31zsdMS8q6XR+S/CKvoSGZxZiPH3eqLmNMAlxdybP2D +ITbdFJVsKQNSsoHe1nrIgdPSxaOgPBgjc3ov6qsm08wZPRSea/ZJcevd8Mi/HFpgnVH oKV5rZ5cdBaHWW7/UicGNDEhBaSmsL2QmOy2fGDBlAyiM2NEdhivgGnWVREKY+KqxeND pip5dcjgRTedRToES6tBWwsbwvxFOZcjecFF0DjFtnotikfshj0MQIc/QR8nI2/Me0MD 1PLQ== X-Gm-Message-State: ANoB5pnduGY6JYdlSpuLXrcIGh0csYltDeo4lhquKg1z25tNMYiToyxY 9smAFEIZjqtLw3f0h8dAoakUtaJXcE2jjrM8XCJ1ExMfdwBRTA== X-Google-Smtp-Source: AA0mqf4HkFUZRsAifzAMg9bd0FMER8BNT9bwaIpMbvmQatKBqbvQBy2RQmMV69mKlaBtmeGHuYLDSvD2sQjil0n0aIo= X-Received: by 2002:aa7:8250:0:b0:56b:fe1d:5735 with SMTP id e16-20020aa78250000000b0056bfe1d5735mr37066737pfn.24.1669344496027; Thu, 24 Nov 2022 18:48:16 -0800 (PST) In-Reply-To: <57b69c22e167d429d21bb969feb22887@webmail.orcon.net.nz> Received-SPF: pass client-ip=2607:f8b0:4864:20::42f; envelope-from=exec@positron.solutions; helo=mail-pf1-x42f.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_FILL_THIS_FORM_SHORT=0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:300456 Archived-At: --000000000000b1a0b805ee428da9 Content-Type: text/plain; charset="UTF-8" The problem I'm working to reach is allowing packages to correctly shadow the user's bindings (and not incorrectly shadow) instead of ad-hoc imitation of the global map defaults. Ad-hoc imitation makes it very tedious to, for instance, change the key sequence for expressing "next" without losing coherence among all packages with a corresponding "next" concept. The solution we're discussing is to provide "abstract" commands. I will discuss the solution and then discuss what I'm currently working on. Abstract commands are just command remap targets in the global map. The concrete global commands would directly command remap in the global map. Abstract command bindings would be consumed when modes generate keymaps for major & minor modes. A Corfu map generator would for example see an abstract command like "user-next" bound with the C-n sequence and choose to shadow C-n in its map. If the user rebinds the global abstract command, the other modes could easily follow this change, perhaps even though a hook to immediately propagate the change. This scheme would scale much better than imitating global map defaults. Any user or emulation system desiring to re-bind basic concepts like next and undo could achieve and maintain coherence. Such a mechanism should also make it easier to maintain modes in the future. Expressing abstract commands could be a basis for coordinating special mode behavior in ways that would currently be likely to break with many users' modifications. I will not introduce a modal system I've worked on, but it has a quirky notion of next / previous, and yet it will adapt fine to abstract key bindings. When switching keyboards to an alternative layout, I lose physical correspondence. "J" is a good high-frequency binding unless "J" is moved to a physically inconvenient location. Switching abstract maps and regenerating is a solution for this as well. Expressing "never shadow this key" would need a distinct map from the global map. The problem is that an abstract command like "user-nomap" would not distinctly map to a concrete command if 20 sequences were to be protected from shadowing. There needs to be a global-no-map where all sequences bound to user-nomap are prohibited by convention. For generating maps where a binding would collide with an abstract binding, a configurable successor function could be made available. It could be set to nil or return nil to flatly reject all sequence suggestions that were bad to begin with instead of finding places to place every command. We have really good command completions these days, so personally I would opt for a nil successor. Abstract key definition would be adopted by first providing map generators for many modes (similar to Evil collection) and then expecting those map generators to migrate out to the modes themselves if the abstract map support were to be adopted into Emacs itself. Regarding where I'm starting out: I'm working on a more modest and immediate goal of depopulating unwanted bindings, focusing on overly complex or high-priority bindings first. I will define my future abstract keys implementation in the same package. While users in the future should not have to battle with so many bindings in the many mode maps, for now it will be appropriate to generate mass-unbind statements for packages like general to consume. I'm using a report-and-configure workflow. This is the lowest effort implementation to identify "bad" bindings and provide a fast remedy to the user. While I can see many maps in for example minor-mode-map-alist, I was unsure of where to find a list of major modes except from inferring while using mapatoms. I could notice the cost of calling this to find major modes / maps. State tracking is necessary to differentiate user defined bindings from defaults. While I had not wanted to add the complexity, inheriting keymaps may provide an elegant solution. My hunch is to first destructively clean out default maps and then define inheriting maps and swap the inheriting maps into the keymap variables. If the user adds bindings afterward, it's almost clearly visible. Is there a way I could add a sentinel to an inherited map to indicate that I've cleaned the parent map already? What are some good choices for list elements of no consequence? This would also tell me that the child was created for the user. I don't want to implement package-private state tracking if possible. I have no ambition to work with non-list maps at this time. So far every map I want to work with is just a plain list. While working on my implementation, I have encountered the kinds of issue that make me believe that alternatives such as key-sequence translation are also not scalable. (key-binding "\M-g") is incorrect after remapping M-g to C-g unless I collect a key sequence interactively. Keeping this kind of indirection around has consequences. I believe key sequence remapping is a solution more aimed at misbehaving input systems where correct bindings cannot accommodate the situation. On Mon, Nov 21, 2022 at 8:07 PM Phil Sainty wrote: > AFAIK... > > You can't arbitrarily prevent key sequences from being bound in > keymaps, because you can't control how or when that happens. > > You can't control when it happens because that time might be "before > you started (or even installed) Emacs". Keymaps are sometimes > generated at byte-compilation time, and thus even if you were to take > the extreme measure of preventing define-key from doing anything at > all, you will still acquire populated keymaps when certain libraries > are loaded (although if the libraries were byte-compiled while the > neutralised define-key was in place, you would then typically be > loading empty keymaps; but you would need to recompile everything to > get to that point; and of course most installations of Emacs will > include pre-compiled .elc files). > > Native compilation might preclude even that, as IIRC it compiles > everything asynchronously in isolation, so the intended clobbering > of define-key might not actually be in effect when the native code > was being generated. You would then have to edit the core code to > enforce your override irrespective of whether your custom code was > loaded. > > You can't control how it happens because libraries can set bindings > without using define-key. In the simplest case `use-local-map' for > one of the aforementioned pre-generated keymaps may easily occur; but > also a keymap is, after all, just a list, so it can be manipulated in > any number of ways. > > (In practice I think that messing with define-key and recompiling All > Of The Things would affect almost all keymaps; but in general there's > no guarantee.) > > I would suggest that your best option is to instead ensure that the > key lookup sequence always finds your preferred bindings. Despite > your concerns about that approach, it seems more viable to me. > > -- Psionic K Software Engineer *Positron Solutions * --000000000000b1a0b805ee428da9 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
The problem I'm wor= king to reach is allowing packages to correctly shadow the user's bindi= ngs (and not incorrectly shadow) instead of ad-hoc imitation of the global = map defaults.=C2=A0 Ad-hoc imitation makes it very tedious to, for instance= , change the key sequence for expressing "next" without losing co= herence among all packages with a corresponding "next" concept.

The solution we're discussing is to provide &quo= t;abstract" commands.=C2=A0 I will discuss the solution and then discu= ss what I'm currently working on.

Abstract commands are just co= mmand remap targets in the global map.=C2=A0 The concrete global commands w= ould directly command remap in the global map. =C2=A0 Abstract command bind= ings would be consumed when modes generate keymaps for major & minor mo= des.=C2=A0 A Corfu map generator would for example see an abstract command = like "user-next" bound with the C-n sequence and choose to shadow= C-n in its map.=C2=A0 If the user rebinds the global abstract command, the= other modes could easily follow this change, perhaps even though a hook to= immediately propagate the change.

This scheme would scal= e much better than imitating global map defaults.=C2=A0 Any user or emulati= on system desiring to re-bind basic concepts like next and undo could achie= ve and maintain coherence.=C2=A0 Such a mechanism should also make it easie= r to maintain modes in the future.=C2=A0 Expressing abstract commands could= be a basis for coordinating special mode behavior in ways that would curre= ntly be likely to break with many users' modifications.

I will not introduce a modal system I've worked on, but it has a qui= rky notion of next / previous, and yet it will adapt fine to abstract key b= indings.=C2=A0 When switching keyboards to an alternative layout, I lose ph= ysical correspondence.=C2=A0 "J" is a good high-frequency binding= unless "J" is moved to a physically inconvenient location.=C2=A0= Switching abstract maps and regenerating is a solution for this as well.

Expressing "never shadow this key" would need a disti= nct map from the global map.=C2=A0 The problem is that an abstract command = like "user-nomap" would not distinctly map to a concrete command = if 20 sequences were to be protected from shadowing.=C2=A0 There needs to b= e a global-no-map where all sequences bound to user-nomap are prohibited by= convention.

For generating maps where a binding would collide= with an abstract binding, a configurable successor function could be made = available.=C2=A0 It could be set to nil or return nil to flatly reject all = sequence suggestions that were bad to begin with instead of finding places = to place every command.=C2=A0 We have really good command completions these= days, so personally I would opt for a nil successor.

Abstract key definition would be adopted by first providing map generator= s for many modes (similar to Evil collection) and then expecting those map = generators to migrate out to the modes themselves if the abstract map suppo= rt were to be adopted into Emacs itself.

Regarding where = I'm starting out:

I'm working on a more modest a= nd immediate goal of depopulating unwanted bindings, focusing on overly com= plex or high-priority bindings first.=C2=A0 I will define my future abstrac= t keys implementation in the same package.=C2=A0 While users in the future = should not have to battle with so many bindings in the many mode maps, for = now it will be appropriate to generate mass-unbind statements for packages = like general to consume.

I'm using a report-and-configure = workflow.=C2=A0 This is the lowest effort implementation to identify "= bad" bindings and provide a fast remedy to the user.=C2=A0 While I can= see many maps in for example minor-mode-map-alist, I was unsure of where t= o find a list of major modes except from inferring while using mapatoms.=C2=A0 I could= =20 notice the cost of calling this to find major modes / maps.

St= ate tracking is necessary to differentiate user defined bindings from defau= lts.=C2=A0 While I had not wanted to add the complexity, inheriting keymaps= may provide an elegant solution.=C2=A0 My hunch is to first destructively = clean out default maps and then define inheriting maps and swap the inherit= ing maps into the keymap variables.=C2=A0 If the user adds bindings afterwa= rd, it's almost clearly visible.=C2=A0 Is there a way I could add a sen= tinel to an inherited map to indicate that I've cleaned the parent map = already?=C2=A0 What are some good choices for list elements of no consequen= ce?=C2=A0 This would also tell me that the child was created for the user.= =C2=A0 I don't want to implement package-private state tracking if poss= ible.

I have no ambition to work with non-list= maps at this time.=C2=A0 So far every map I want to work with is just a pl= ain list.

While working on my implementation, I have enco= untered the kinds of issue that make me believe that alternatives such as k= ey-sequence translation are also not scalable.=C2=A0 (key-binding "\M-= g") is incorrect after remapping M-g to C-g unless I collect a key seq= uence interactively.=C2=A0=C2=A0 Keeping this kind of indirection around ha= s consequences.=C2=A0 I believe key sequence remapping is a solution more a= imed at misbehaving input systems where correct bindings cannot accommodate= the situation.

On Mon, Nov 21, 2022 at 8:07 PM Phil Sainty <psainty@orcon.net.nz> wrote:
AFAIK...

You can't arbitrarily prevent key sequences from being bound in
keymaps, because you can't control how or when that happens.

You can't control when it happens because that time might be "befo= re
you started (or even installed) Emacs".=C2=A0 Keymaps are sometimes generated at byte-compilation time, and thus even if you were to take
the extreme measure of preventing define-key from doing anything at
all, you will still acquire populated keymaps when certain libraries
are loaded (although if the libraries were byte-compiled while the
neutralised define-key was in place, you would then typically be
loading empty keymaps; but you would need to recompile everything to
get to that point; and of course most installations of Emacs will
include pre-compiled .elc files).

Native compilation might preclude even that, as IIRC it compiles
everything asynchronously in isolation, so the intended clobbering
of define-key might not actually be in effect when the native code
was being generated.=C2=A0 You would then have to edit the core code to
enforce your override irrespective of whether your custom code was
loaded.

You can't control how it happens because libraries can set bindings
without using define-key.=C2=A0 In the simplest case `use-local-map' fo= r
one of the aforementioned pre-generated keymaps may easily occur; but
also a keymap is, after all, just a list, so it can be manipulated in
any number of ways.

(In practice I think that messing with define-key and recompiling All
Of The Things would affect almost all keymaps; but in general there's no guarantee.)

I would suggest that your best option is to instead ensure that the
key lookup sequence always finds your preferred bindings.=C2=A0 Despite
your concerns about that approach, it seems more viable to me.



--

Software Engineer
<= /div>
--000000000000b1a0b805ee428da9--