garybernhardt/selecta

Alternative to Ctr-c

Closed this issue · 20 comments

At the moment, if I want to get out the selecta prompt (essantially cancelling the search), I have to press Ctrl-c. That is often tedious. It would be better is I could get out of selecta by pressing Esc, or even Enter when no search query has been entered; That assumes that no search result is pre-selected.

Well, ^C is the most common keyboard shortcut in Unix. It's a little awkward with an unmodified keyboard layout, but has worked everywhere for many decades. I'd rather not introduce more inconsistencies than Unix already has. :) I recommend remapping your caps lock key to be a control key, which makes ^C (and the rest of Unix) very natural.

@garybernhardt That's actually fair enough. However, I still think that it would nice(r) if I had an alternative way of getting out of selecta right after getting in. Or at least have the choice to enable this through an option. It doesn't really make sense to me why a search result has to be preselected straight away, rather than having no "default" and filtered search results after characters have been typed.

Allowing RET to exit Selecta with nothing selected would break the symmetry between user action and Selecta status codes. Today, Selecta makes these two guarantees (among others):

  1. if you press ^C, it exits with status code 1; if you exit with RET, it exits with status code 0.
  2. If the status code is 0, Selecta will write one of the input options to stdout; if it's 1, it will write nothing.

These make Selecta more predictable for both high-speed use and scripting. Starting with no initial highlight and allowing RET to select "nothing" would mean that either:

  • RET with no selection causes an exit with status code 1, violating the correspondence between user action and status code guaranteed in (1), or
  • RET always exits with status code 0, but an empty "selection" might be returned, even though no such choice was in the input list, violating guarantee (2)

I agree with you about Enter but Esc should escape. It is the escape key, after all. That's what it's for.

If you want to argue based on meaning, it's called the escape key because it begins an escape sequence, not because it escapes "from" things. It's also traditionally the meta key in Unix; it doesn't escape "from". Only a few Unix tools use it for that (Vim is an obvious example). You should build muscle memory for ^C because that's how Unix tools are killed.=

I do have muscle memory for ^C (and always remap capslock to ctrl, as God intended). But what I want to use ESC for is to kill the selector but not the entire command I'm in the middle of typing (for which selecta is being used as a link in a pipeline). So maybe I'd like ESC to exit with a value of 0, sending nothing to stdout. This use is consistent with (as you mentioned) vi but also video games, dialog boxes, IDEs/editors with their own selection menus... emacs triple-escape meaning "cancel no really cancel i mean it cancel"...

And I'm ok with arguing from usage, but if you want to argue on meaning...

According to lore, the original original use for the ESC key was to switch modes between character encoding sets, and the term really was chosen because it escapes "from" things; the term you reference, "escape sequence", came a bit later and probably should have been called a meta sequence, and maybe would have if PC keyboards didn't drop the META key and go with ESC only.

(And thanks for indulging this bikeshedding. :-))

@alexch do you have an example illustrating why ESC should cause an exit 0? I use selecta a lot and ^C works fine for me.

OT I use ^[ to exit insert mode in vim, which is more finger-friendly than ESC to me (but the same sequence AFAIK). In fact, I use ^[ instead of ^C almost everywhere (e.g. tmux), not just vim.

Edit: Nevermind the only time you want "nothing" to be returned is when "nothing" is a valid choice.

@alexch Currently, failure and empty output are explicitly combined and documented. From the README: "If no selection is made [...], it will write nothing to stdout and exit with status code 1." This was a conscious choice: it makes Selecta's contract simpler. If $? == 0, there's non-empty output; else, there's never output. If a Python programmer pipes to Selecta with check_call, for example, they don't have to check for empty string, because it will either raise an exception or provide output. If this corner case never crossed the programmer's mind, they still won't get tripped up because Selecta never emits empty strings.

Allowing a successful exit with empty output introduces a third case. Three is the magic number where people start forgetting things, and it breaks Selecta's ability to protect you when you forget to handle edge cases.

However! You're right: selecting nothing is different from aborting. It doesn't seem right to only allow aborting Selecta via a keystroke that will (normally) SIGINT the whole pipeline. Double-however! At a glance, I don't see anything in that gist where empty output from Selecta would be meaningful. In fact, all of the examples except the cd one would totally break in the presence of empty output. (Which is exactly why Selecta acts as it does. I am vindicated! ;)

What about supporting ESC, but making it do what ^C does (exit with status 1, no output)? That allows the user to kill Selecta without killing the pipeline, but keeps the conservative output contract in place. If Selecta eventually learns to take a default value, ESC could theoretically return that (with status 0), though I'm very skeptical of the asymmetry there (ESC means a different thing when there's a default).

I agree with you 100%. Well done! (And bravo for Selecta, another unix gem (pun intended))

I just pushed 53ba4cc, which adds support for ESC with the semantics in my last comment. Can you try it out and let me know if it meets your expectations?

Works great! In a pipe ESC aborts

ls | ./selecta | xargs cd  # quietly aborts

, but inside backticks it returns the empty string

cd `ls | ./selecta`    # cd's to ~

OK, awesome. I'll let this settle for a bit and it'll get rolled into the next release if nothing goes wrong.

Thanks for taking the time to convince me. Re "bikeshedding": I don't think this is that. Or, if this is bikeshedding, I'm pro-bikeshedding. With long-term use, these little distinctions matter a lot and stack up to make a big difference in the composability of tools. So thanks again.

a slightly better example: can use it for default values

x=`ls | ./selecta`; echo ${x:-hi}

In zsh, you can even do that with echo ${$(ls | selecta):-hi} (but most readers will be confused by it, I think).

fj commented

I didn't even know I wanted this until it landed. 👍

I've reverted ESC support in 2db9e66 due to the problem described in #71.

eek! yeah, that's unfortunate... If I start to use selecta again and put ESC back with a small delay like you suggested, would you accept a patch?

Maybe it's such an obviously silly idea, but still, I didn't get any response in #71:

What about simply requiring ESC to be hit twice? This would make ?\C-[ a prefix of both the arrow keys and the double-ESC, allowing selecta to distinguish between those without implementing a timeout. Whether this is worth the UI weirdness I don't know (depends on how easy it is to implement the timeout hack)

@alexch @untitaker I really don't want to debug and maintain any kind of input state machine. I suspect that even something this simple wouldn't be portable. We once tried to add arrow key support, and that didn't even work across different Macs running OS X.