yazi
yazi copied to clipboard
Discussing how to handle different variants of emacs keybinding
In the recent PR (#345), there is a discussion on behaviors of navigation and deletion, most of which are related to word boundary. Since users are from different shell backgrounds and have different preferences, it would be better have an elegant solution for customization, or at least reach a certain degree of
To make the issue clear, here are some examples based on the original behavior of #345. Suppose we have a buffer with contents .config/yazi/
, and every line stands for the result of pressing ctrl-w/alt-b/alt-f exactly once:
Deletion
yazi && vanilla zsh
.config/yazi/ # original
.config/yazi
.config/
.config
.
# here we have nothing in the buffer
default oh-my-zsh
.config/yazi/ # original
.config/
.
# here we have nothing in the buffer
fish
.config/yazi/ # original
.config/
# here we have nothing in the buffer
Navigation
We use @
as the indicator of cursor
yazi
.config/yazi/@
.config/yazi@/
.config@/yazi/
.@config/yazi/
@.config/yazi/ # next line we move forward
.config/@yazi/
.config/yazi/@
bash & fish
.config/yazi/@
.config/@yazi/
.@config/yazi/
@.config/yazi/ # next line we move forward
.config@/yazi/
.config/yazi@/
.config/yazi/@
There would be other cases missing from these examples for different characters/different shells. Welcome to provide examples below.
Describe the solution you'd like
Here are some questions we need to resolve:
- Can we formalize these behaviors so that we can simply add these variants to the codebase?
- If not, can we have some ways to customize these behaviors, for example
WORDCHARS
env in zsh?
cc @musjj @Tweoss @sxyazi
I prefer to just keep one "Emacs" implement, having too many implementations would make maintenance more difficult.
It seems like a good idea to exclude /
from word boundaries, as file names generally do not contain the /
character. But we also need to take into account Windows users who use \
instead of /
.
yazi && vanilla zsh
Are you sure you're using vanilla zsh there? In zsh, slashes are not treated as a delimiter unless you use select-word-style bash
.
I think the most widely used line-editing library is readline
(the one used by bash
and many other terminal applications), so it'd make the most sense to follow that, at least by default.
- Can we formalize these behaviors so that we can simply add these variants to the codebase?
It seems that the biggest difference here is how word separators are treated. In the current yazi
implementation, non-space delimiters are treated specially, while in readline
they're treated equally.
For example, with left-to-right word movement:
# readline
hello world hello-world---foobar
^ ^ ^ ^ ^ ^
# yazi
hello world hello-world---foobar
^ ^ ^ ^^ ^ ^ ^
- If not, can we have some ways to customize these behaviors, for example WORDCHARS env in zsh?
I think an option like granular_word_movement
would be useful.
An option to customize the word delimiters would also be useful, but I think the current default is sane enough.
I prefer to just keep one "Emacs" implement, having too many implementations would make maintenance more difficult.
It seems like a good idea to exclude
/
from word boundaries, as file names generally do not contain the/
character.
Perhaps we can have 2-3 implementation if we can have clear definition and well-organized code structure for them? But I agree that excluding /
is a good idea, maybe by default. I don't see any value of having to deal with trailing slash.
But we also need to take into account Windows users who use
\
instead of/
.
Of course. During the discussion we can assume the path delimiter is /
while in real implementation we need to consider \
as well.
Are you sure you're using vanilla zsh there? In zsh, slashes are not treated as a delimiter unless you use
select-word-style bash
.
I tested it in zsh on MacOS, with empty .zshrc and .zshenv. No modification on system-level configuration.
It seems that the biggest difference here is how word separators are treated. In the current
yazi
implementation, non-space delimiters are treated specially, while inreadline
they're treated equally.For example, with left-to-right word movement:
# readline hello world hello-world---foobar ^ ^ ^ ^ ^ ^ # yazi hello world hello-world---foobar ^ ^ ^ ^^ ^ ^ ^
I think the behavior of readline
is more reasonable here, if we consider the default behavior of yazi
.
I think an option like
granular_word_movement
would be useful. An option to customize the word delimiters would also be useful, but I think the current default is sane enough.
This approach looks promising, since it wouldn't be too complex for maintainence. Another approach I recently came across is in https://fishshell.com/docs/current/cmds/bind.html, where some actions like forward-word
, forward-bigword
and backward-kill-path-component
are defined. Perhaps we can pick one of these, combine these two, or try to find some other approaches.
Maybe we could add a config option like whichstop = ' ,.!'
to allow users to specify which characters should as a stop char?
That makes sense. I think we could implement two features separately:
- Better handling of word separator, like
readline
-style word movement and skip '/' under some cases for ergonomic experience. - Config option like
whichstop = ' ,.!'
allowing users to customize their word separator.
Better handling of word separator, like readline-style word movement and skip '/' under some cases for ergonomic experience.
Can't this be covered by whichstop
? i.e. when /
does not exist in whichstop
, it means skipping /
and treating it as part of the character rather than the boundary of the character.
Can't this be covered by
whichstop
? i.e. when/
does not exist inwhichstop
, it means skipping/
and treating it as part of the character rather than the boundary of the character.
I don't think they overlap. First, I don't think ignoring /
makes sense since it's a path separator. I doubt if any user would like to skip /
, but they probably want to customize behaviors around such as .
, _
or -
.
Second, what I mean is we should change our behaviors based on the position of cursor and /
character. For example, we only want to ignore /
when the cursor is exactly after the /
when deleting a word forward, so hello/world/
becomes hello/
instead of hello/world
. But when we want to delete a word forward for hello/wor
, we expect hello/
instead of an empty string.
I think what @sxyazi is saying that whichstop
can be used to change the behavior of certain delimiters. They will still be used as delimiters, but they'll be treated slightly differently from whitespaces.
For example, let's say we have two delimiter groups: hard_delimiters
and soft_delimiters
With hard_delimiters="-/"
and soft_delimiters=" "
:
hello world foobar
^ ^ ^ ^
hello-world-/--/--foobar
^ ^^ ^ ^ ^
With hard_delimiters=""
(empty) and soft_delimiters=" -/"
:
hello world foobar
^ ^ ^ ^
hello-world-/--/--foobar
^ ^ ^ ^
Hi, I've created a PR to make the Emacs keybindings configurable. https://github.com/sxyazi/yazi/pull/382
This allows us to add different commands on top of this and even add different parameters for the same command, similar to what the fish shell does.
I think what @sxyazi is saying that
whichstop
can be used to change the behavior of certain delimiters. They will still be used as delimiters, but they'll be treated slightly differently from whitespaces.
I don't fully understand your meaning. Do you mean that we will only support delimiters in either hard_delimiters
or soft_delimiters
? That would sound bad since users may want to completely treat characters like -
or _
the same as alphas (i.e. they only care about path component, instead of semantic words). I believe at least we should support treating some characters as non-delimiters.
But the concept of hard_delimiters
and soft_delimiters
is interesting. Maybe we can implement these together if we can find a good and straightforward way.