bugGNU nano - Bugs: bug #61692, [Wish] allow binding a key to a...

 
 

You are not allowed to post comments on this tracker with your current authentication level.

bug #61692: [Wish] allow binding a key to a sequence of functions and strings

Submitter:  Tasos Papastylianou <tpapastylianou>
Submitted:  Fri 17 Dec 2021 03:40:09 PM UTC
 
Severity:  1 - Wish Status:  None
Assigned to:  None Open/Closed:  Open
* Mandatory Fields

Add a New Comment (Rich Markup)
   

Comment Type & Canned Response:
       

 

( Jump to the original submission )

Tue 18 Jan 2022 03:05:18 PM UTC, comment #9: 

One argument against introducing the escaping of a double-quote (in whatever way) is that it will break any existing string bind that contains a double-quote.  But I don't see how each and every breakage can be avoided: even if the whole sequence of function names and strings has to be inside double-quotes, we would need to be able to tell which things are meant as function names, so some character or character sequence needs to be special, which will break some string binds -- maybe exceedingly few, but still.  So... I prefer the more elegant way and have it be obvious what are function names and what are literal strings.

Benno Schulenberg <bens>
Project Administrator
Mon 17 Jan 2022 04:10:01 PM UTC, comment #8: 

Hi Benno. Yes, you're right. Sorry, I see what you were saying now.

I thought it might cause problems with things like """ in python, or quotes in comments etc, but obviously it doesn't because no such parsing is needed, as long as the quotes are 'escaped' appropriately; and as long as the number of quotes per token before processing the escapes is an odd number, the string is considered 'still open' and the semicolon is treated as part of the string. You're right.

Agreed, this is more elegant. :)

Tasos Papastylianou <tpapastylianou>
Wed 12 Jan 2022 03:19:14 PM UTC, comment #7: 

There is no necessity for a double-character delimiter.  I see no problem with parsing something like:

bind ^K "echo ""hello"";";enter main

Here a doubled double-quote means one escaped double-quote, and a semicolon within a string is not special.  Of course, this requires a new string parsing routine for the key binding code, but I don't see a problem with that, as the code has to change anyway, to allow for the concatenating of several function names and strings.

Benno Schulenberg <bens>
Project Administrator
Sat 08 Jan 2022 05:35:49 PM UTC, comment #6: 

Hi Benno, sorry for the delay, I did not spot your response on my email.

The surrounding spaces aren't necessary, obviously. I just generally prefer spacing over cramping in my own code, but obviously that's only a personal preference and I'd be equally happy with a syntax that does not enforce surrounding spaces.

The main thing I'm pointing out here isn't the spacing, but the necessity for a "double-character" delimiter over a single one. The main advantage of this is that you can then trivially disambiguate between two symbols intended as a delimiter vs two symbols intended as actual keystrokes, since the latter can be trivially split into a "symbol" + delimiter + "symbol" sequence, to obtain the equivalent of "symbolsymbol" without sacrificing tokenization or "end quote closes whole quote" semantics within tokens.

I.e. if the delimiter is ; then you can have something like

bind ^C "case NAME in";;enter;;tab;;"1) echo "hello" ;";;";";;enter;;"esac";;enter main

This would result in

case NAME in
  1) echo "hello" ;;
esac

printed on screen. I.e. it allows us to print ";;" by 'joining' two separate strings as separate commands, and it also allows preserves the whole "end quote closes whole quote" behaviour within ;; delimited tokens. (note how this is used in the example above to do the 'echo "hello"' bit).

Tasos Papastylianou <tpapastylianou>
Wed 05 Jan 2022 09:58:09 AM UTC, comment #5: 

Using a double colon and two spaces to separate function names is horribly verbose.  Remember what I liked about your first suggested syntax?  That is was lean and clean.  Also, the semicolon is a well-known command separator, and using no whitespace glues the function names nicely together, showing that they are single unit.  (Of course there could still be spaces inside strings, but that is the nature of strings: they end only at an unescaped closing quote.  And of course a semicolon inside a string is not special.)

Yes, currently string binds that contain commands are cumbersome to type.  And that is exactly what this feature request should be trying to get away from: make the long binds readable, typable, pastable.

Benno Schulenberg <bens>
Project Administrator
Sat 01 Jan 2022 08:40:33 PM UTC, comment #4: 

> So, if binding to a sequence of things is going to happen, it will have to be a syntax without control codes: human-readable, copy-pastable, and easy to type.  It will require some changes to the bind-parsing code of rcfile.c (to stop also at a semicolon and at an unescaped double quote), but that looks feasible.

If you still think this is a good idea, perhaps there is a better delimiter that avoids potential bugs with plain semicolons, and can still respect the 'final double quote closes the quote' after tokenization. Perhaps ' :: ' (i.e. two colons with spaces on either side)? E.g.:

bind function1 :: "String with a single double quote at the end"" :: function2 :: "Another " string" main

This way, in the worst case scenario that one really needs the character sequence ' :: ' in a string, they could still work around it by splitting it in two,  e.g.

bind prevword :: "Classname" :: " :" :: ": " :: nextword main   # prepend c++ classname to method name

> Creating an 'evaluate' function would avoid having to make changes to the parsing routines in rcfile.c, true, but the resulting string-bind commands would still suffer from most of your original objections against the current state of things.

I don't necessarily disagree, but I would like to 'defend' the points nonetheless for the sake of the completeness:

> They are ugly / visually obfuscated.

Perhaps, yes. But, I think it would still be sufficiently friendlier than using only prebound keystrokes. Compare:

(clean)     : firstline :: mark :: lastline :: writeout :: "draft" :: enter :: mark
(obfuscated): "^[\^^^[/^Odraft^M^^"
(okayish?)  : "^[!firstline^M^[!mark^M^[!lastline^M^[!writeout^Mdraft^[!enter^M^[!mark^M"

> They are a nuisance to type: M-V M-! functionname M-V <Enter> ...

Even without the involvement of functionnames, string macros containing control codes are already a bit cumbersome to type anyway, but their usefulness more than makes up for it. While I agree that the 'clean' syntax above would be preferable, I don't necessarily think the 'okayish' syntax would be more of a 'nuisance' than current string macros, where things need to be bound to non-clashing keys first before use.

> They cannot be copy-pasted from a web page.

Perhaps not, but they would still be shareable as files. The recent uppercasing/lowercasing macros you included in the sample.nanorc are a good example of this.
I don't think lack of web-copying is a serious issue in this case.

> An 'evaluate' prompt is what I hate about editors like vim or emacs or micro or dte.

I whole-heartedly agree with you on this! Though, in vim, this is partly because the alternative to typing commands sucks: there's no menu, the shorcuts are not particularly intuitive or memorable, and discovering the right keyboard shortcut is an exercise in frustration. In nano's case, however, an 'evaluate' menu wouldn't abolish existing functionality or replace nano's menu-based approach; it would simply be extra functionality for whoever needs it, and invisible to whoever doesn't. But, if present, it becomes very useful (e.g. for macros); and if this menu were to accept 'set' and 'bind' commands as well, then it becomes very powerful indeed.

Of course, an 'evaluate' menu, and a clean syntax for command sequences are not necessarily mutually exclusive features!

Tasos Papastylianou <tpapastylianou>
Sat 01 Jan 2022 02:56:02 PM UTC, comment #3: 

Creating an 'evaluate' function would avoid having to make changes to the parsing routines in rcfile.c, true, but the resulting string-bind commands would still suffer from most of your original objections against the current state of things.

  1. They are ugly (or visually obfuscated, as you say).
  2. They are a nuisance to type: M-V M-! functionname M-V <Enter> ...
  3. They cannot be copy-pasted from a web page.

Besides, an 'evaluate' prompt is what I hate about editors like vim or emacs or micro or dte.  When the cursor is blinking at that prompt, I think: Arrw... Now I have to know the names of the functions... Grrr! Just give me a fakking menu!

So, if binding to a sequence of things is going to happen, it will have to be a syntax without control codes: human-readable, copy-pastable, and easy to type.  It will require some changes to the bind-parsing code of rcfile.c (to stop also at a semicolon and at an unescaped double quote), but that looks feasible.

Benno Schulenberg <bens>
Project Administrator
Thu 30 Dec 2021 05:21:59 PM UTC, comment #2: 

My proposed syntax was simply a visual example. I don't know if I would suggest that explicitly as the actual syntax for this; in fact, I can see one big problem with it, which relates to how nanorc treats quoted values specifically, by treating the last double quote as the closing quote, and interpreting all other in-between quotes as literal quotes.

This makes something like

bind ^F function;"string";function;"string";function main

rather problematic.

If I may change the context slightly, there is a way to avoid having to introduce brand new (and possibly problematic) syntax for this, but it would require a different change to nano: the ability to execute a function directly, via a bespoke menu serving that specific purpose. This is not too far from what you propose, but implies an explicit (sub)menu for running functions, rather than performing 'under the hood expansions' instead as you propose. I can see how that may be more work than can be justified though (though it would greatly expand nano's versatility).

E.g. assume a new menu (and corresponding menu-launching function) called, say, 'evaluate', whose purpose is to execute internal functions.

If you then do:

bind M-! evaluate main

then a sequence of functions/strings can be placed in a macro simply as:

bind "^[!function1^M^[!function2^MMyStringContent^![function3^M" main

without having to bind a key to functions 1, 2, and 3 in advance, just for the sake of using them in such a sequential macro. To me such an approach would be straightforward and visually acceptable.

Furthermore, if such an 'evaluate' menu allows a user to call not just internal functions, but also to set options live, maybe even rebind keys live, etc (i.e. whatever line is valid in a nanorc file should be valid input in this menu), then this would be a very powerful feature indeed. E.g. I note that it would make lots of tickets (including bug #61703) obsolete, since one could, e.g. do "set fill 80" from such an evaluate menu to change the editor's state live (rather than 'edit and then reload .nanorc' ).

Sorry for the verbiage, this is obviously extremely low priority and more for brainstorming purposes. :)

Tasos Papastylianou <tpapastylianou>
Tue 28 Dec 2021 03:34:18 PM UTC, comment #1: 

Yes, being able to bind a key to a sequence of functions and strings would be cool.  It has been proposed before (see [1]; continued in [2]), but your proposed syntax is much leaner and cleaner:

bind key function;function;"string";function... menu

I can imagine how this could work: when such a sequence of functions and/or strings is detected while parsing an rc file, then for each function a special keycode is added to the 'expansion' string followed by the function name.  This rcfile routine should check that it is an existing function name, but not whether it is available in the specified menu, as preceding function names could have entered a different menu.  When the bound key is pressed, the whole expansion string is implanted in the keyboard buffer, as is already done.  When then parse_kbinput() encounters the special keycode, it will read the function name, check whether it exists in the current menu, and if so, execute it, and when not, report an error on the status bar and discard the entire rest of the keyboard buffer.  Anything I'm overlooking?

[1] https://lists.gnu.org/archive/html/nano-devel/2018-08/msg00101.html
[2] https://lists.gnu.org/archive/html/nano-devel/2018-09/msg00000.html

Benno Schulenberg <bens>
Project Administrator
Fri 17 Dec 2021 03:40:09 PM UTC, original submission:  

At the moment (nano 6.0), the 'bind' command can:

  • bind a single function to a particular key, in the context of a particular menu
  • bind a "string" to a particular key, in the context of a particular menu.

For the sake of this discussion, we define the following terms:

  • Let functions and strings be collectively known as actions
  • Given an action which has been bound to a specific key, let that keystroke be known as a command which produces that action. (this is effectively how this term is used in 'man nanorc').
  • Strings are a special kind of action, because they can intermix normal text with commands; the latter are introduced into the string by typing "verbatim keystrokes" (entered via Alt-V). This creates a handy way of creating pre-recorded macros. (related: #52931, #58991). To disambiguate from simple, text-only strings, let us refer to such complex, command-containing strings as commandstrings

At the moment, commandstrings are the only way to bind a sequence of actions to a single keystroke. This has the unfortunate side-effect that, in order to bind a sequence of actions, all actions involved in the sequence must have their own keybindings. This is not ideal, for two reasons:

  1. It wastes keybindings unnecessarily
  2. It makes the sharing of macros between users problematic, since one cannot guarantee that temporary bindings created by one user for the explicit purpose of creating a suitable commandstring, won't result in the overriding of another user's custom key bindings.
  3. It generally results in visually obfuscated commandstrings, whose underlying functionality is hard to decipher by eye.

It would be useful if bind could accept a sequence of actions instead of only a single action.

E.g., this could take the following form:

bind key function;function;"commandstring";function menu

Tasos Papastylianou <tpapastylianou>

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

No files currently attached

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by bens (Posted a comment)
  • -email is unavailable- added by tpapastylianou (Submitted the item)
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only logged-in users can vote.

     

     

     

     

    Follow 2 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2021-12-28 bens Severity3 - Normal 1 - Wish
        Summary[Wish] Enable bind to perform a sequence of functions/commandstrings rather than just a single function or commandstring [Wish] allow binding a key to a sequence of functions and strings

    Back to the top


    Powered by Savane 3.9