On macOS, swap alt-{left,right,backspace,delete} with ctrl-*
Commit 6af96a81a8 (Default bindings for token movement commands,
2024-10-05) changed the behavior of keys like alt-left to move by
tokens instead of words.
This seems fine on non-macOS systems where GUI text fields use
ctrl-left for word movement.
But it's surprising on macOS where word movement is alt-left
everywhere.
Sublime Text uses the native key binding for word movement, and uses
the vacant chord (e.g. alt-left on Linux) for sub-word movement
(in camel case words).
Let's try to do the same, but for token movements.
There are not too many terminals that run on macOS. Try to detect some of them in various ways, some more hacky than others.
- Kitty supports reporting the client OS via
XTGETTCAP[^XTGETTCAP]. This is the most correct method, overriding others[^os-name]. By default tmux doesn't let this request pass through. Add a workaround to bypass tmux, just like we do infish_clipboard_copy. - We can detect iTerm2 based on LC_TERMINAL, even inside SSH.
- Assume macOS if fish is built for macOS. This is not fully correct since the terminal may run on another system.
One of my concerns is that so far I personally override this change. (when on macOS and running SSH into fish). Part of the reason is that the version of tmux I use doesn't support binding ctrl-backspace yet. I'm not sure if users prefer a native experience or consistency across platforms. But users who regularly switch platforms have to switch muscle memory regardless.
I don't know yet what I'd apply to the release branch. Maybe a subset of detection methods, or revert 6af96a81a8, I'm not sure how many people are affected.
Alternatives:
- revert and/or find different key bindings
- leave things as they are
[^XTGETTCAP]: The current patch detects macOS only after sourcing
config files. We can fix this by reshuffling some code.
[^os-name]: We might want to expose the (arbitrary) OS name,
however the name reported by Kitty is like
"bsd"/"linux"/"macos", which does not match uname(1),
so I'm not sure if that's stable enough for us. Hence
the boolean macOS flag.
Closes #10926
TODOs:
- [ ] Changes to fish usage are reflected in user documentation/manpages.
- [ ] Tests have been added for regressions fixed
- [ ] User-visible changes noted in CHANGELOG.rst
This is pretty dirty.
It's a special status subcommand just for a specific OS. I'm also not a fan of executing tmux inside of __fish_config_interactive.
I would much prefer a status client-operating-system subcommand that tries to get the OS via query, and if it can't falls back on uname (or target_os or whatever). We can either expose these directly, or post-process them to give us "linux", "bsd", "macos" - or get kitty to use the uname names.
That's more generally useful and cleaner, and the key bindings check could then be if contains -- (status client-operating-system) Darwin macos; or test "$LC_TERMINAL" = iTerm2.
yeah I think we should either ask kitty for uname names, so we can have
a consistent fallback. Or do lossy translation ("uname=FreeBSD" to "bsd"),
which is probably fine but I'm not sure.
We can probably pass tmux' underlying tty to status client-operating-system
to remove the tmux-specific code from Rust sources.
We could keep caching the result, or not.
Either way there will be some tmux scenarios that will not work.
For 4.0, I wouldn't want to add this since it adds querying.
So I'd probably either keep the bindings consistent (by reverting the token bindings or keeping them everywhere), or use a simple uname check (possibly with $TERM_PROGRAM or $LC_TERMINAL) there. If we're planning on making it os-dependent later it's probably nicer to revert the binding change and implement it after with os-detection.
Tbh I'm not entirely sure if this query is worth it because it'll currently only work on kitty, and other than for bindings I can't think of anything we need the terminal operating system for. E.g. if you want to know what flavor of coreutils you're dealing with uname is a decent proxy, the terminal OS isn't (as soon as it differs it's wrong!).
I don't think we should add this - a new status command seems like overkill and all of the checks around tmux, kitty, seem fragile and difficult to test. As faho says, if we must have checks, then let's make them simple based on uname and certain environment variables set by the system, not the terminal (sadly, none are ideal). I suggest:
set -q XPC_FLAGS || set -q __CFBundleIdentifier || test (uname) = "Darwin"
Reverting 6af96a81a8c6ff5e632d4dda7448f5f01d7a5d35 is also fine with me, but I'm primarily a Mac user so I'm biased.
Pulling this into 4.0 - I didn't realize this had regressed since apparently my custom key bindings were masking it. But it really is a regression on macOS
But it's surprising on macOS where word movement is alt-left everywhere.
It's not just that - the more painful thing is that the new prevd-or-backward-word mapping is on ctrl-left and ctrl-right for forward-movements.
On MacOS, these keys are (often) mapped to switch to the left and right desktop, respectively.
The first thing I did was map alt-<arrow> back to backward-word with a custom bind, until I realised that the directory-switching doesn't work anymore 😄
I was also a bit confused as functions doesn't seem to list the old prevd-or-backward-word implementation:
$ functions | rg 'prevd'
prevd
prevd-or-backward-token
but it seems like bind -M insert alt-left prevd-or-backward-word works anyways 🤔
I appreciate that this PR might not be the solution you strive for, and I guess as users it's easy enough to get the old bindings back 🙏
It would be nice to make this a bit clearer in the release notes though, as it took me a second to get to the bottom of this :)
A relevant question is whether an application running in a terminal may have key bindings that depend on the terminal OS; this question can been seen as independent from current implementation details. There are arguments for both ways.
That is a general question.
Personally, I think if bindings are going to be OS-dependent it's cool if they're dependent on what OS you're typing on. But it's also not really necessary - it's an edge case where it's understandable that it's not handled, and it is not one we can currently solve for everyone (because only kitty supports that query).
I don't like having bindings OS-dependent, but if it's really such an annoyance for macOS users specifically and if we can't find other chords to use then so be it.
Now, moving on from that general question, I would like to find something here for the 4.0 release, and I would prefer to have the answer sooner rather than later. And it should be simple, without much added in the way of features or checks.
So I propose we revert the binding changes for 4.0. The functions etc can stay, but we restore the defaults for everyone (for the bindings that annoy macOS users). And then for 4.1 we try again, in whatever form (different bindings for macOS via uname, querying, new keychords for these functions, whatever).
well then we're on the same page. This PR is not a snap proposal and we do not need to rush it. I think the chicken-and-egg problem ("only kitty") is very minor. I intentionally did not bother other terminal emulator developers until we know we have a concrete use case. Case in point -- we don't have consensus yet.
On the release branch I'd also prefer a revert to a half-baked solution but I don't super care.
shift-delete is another regression on macOS. I don't know if it's relevant.
378f452eaa0abbe1a6e3d41963adfcee11803a09 reverts the *-token* bindings to their old state.
Technically we could have kept alt-delete because that wasn't bound before, but I thought it weird to have one token-moving binding in there.
I hadn't really paid close attention to this, to me it seems like it could supersede the "bigword" behavior - which we barely used. I'm not entirely clear why it's a third movement style instead of just a change to bigword, but I know changing keybindings is annoying because people get particular about their muscle memory.
Regarding the feature at hand, in 4.1:
The only use we would ever have for a client-OS query is bindings. With anything else I can come up with you're interested in the OS that runs fish, not the terminal (and that's easy to get, via uname - tho I've played with the idea of removing that dependency by introducing a variable or status subcommand).
Or does anyone know any other feature that we would want to gate on where the terminal runs?
replacing uses of bigword with token would be a good change I think, but regardless we want better bindings than shift-arrows (shift is not suitable for multiple reasons as explained in the issue)
I think test (uname) = Darwin is favored over the status quo, so if we don't want the rest,
I'd add that for now.
Using the client OS makes more sense to me but ultimately I don't care;
macOS users should (eventually) decide which behavior they want inside
ssh. Not sure if we want to work around tmux; not doing so seems okay.
status is-in-macos-terminal sure is awkward but that's easily remedied by replacing it with
something like status xtgettcap uname[^1]. Then any user can debug it based on bind alt-left
output. They may see the behavior vary across terminals, tmux, mosh etc.
The only use we would ever have for a client-OS query is bindings. With anything else I can come up with you're interested in the OS that runs fish, not the terminal (and that's easy to get, via uname - tho I've played with the idea of removing that dependency by introducing a variable or status subcommand).
Or does anyone know any other feature that we would want to gate on where the terminal runs?
Can't think of any -- I believe mainly because many features (such as mouse/clipboard support) already require support by the terminal[^2]. This means we can usually ask the terminal if it has a feature.
But the "native key bindings" feature is different, it doesn't depend on the terminal. Given that a lot of platform-dependent keystrokes are intercepted by the terminal and not sent to fish, it's a small feature. But the differences can be jarring and it feels somewhat odd to decide for one behavior when native bindings are opposites.
[^1]: status xtgettcap wants to be a builtin instead of an external command, else we might
drop keyboard input that immediately precedes the XTGETTCAP response).
[^2]: termios(3) seems to list exceptions - it allows fish to talk to the terminal on the client
@ridiculousfish I don't know if your concerns about using the client OS are purely due to this awkward/opaque interface (which can be fixed) or if it's also about the behavior.
As a macOS user, this would be a large QoL improvement for me.
This was superceded by 2bb5cbc95943b0168c8ceb5b639391299e767e72 and can be closed.