yazi icon indicating copy to clipboard operation
yazi copied to clipboard

Discussing how to handle different variants of emacs keybinding

Open XOR-op opened this issue 1 year ago • 12 comments

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:

  1. Can we formalize these behaviors so that we can simply add these variants to the codebase?
  2. If not, can we have some ways to customize these behaviors, for example WORDCHARS env in zsh?

XOR-op avatar Nov 15 '23 01:11 XOR-op

cc @musjj @Tweoss @sxyazi

XOR-op avatar Nov 15 '23 01:11 XOR-op

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 /.

sxyazi avatar Nov 15 '23 14:11 sxyazi

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.

  1. 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
^    ^     ^       ^^    ^  ^     ^
  1. 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.

musjj avatar Nov 15 '23 17:11 musjj

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.

XOR-op avatar Nov 15 '23 18:11 XOR-op

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 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
^    ^     ^       ^^    ^  ^     ^

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.

XOR-op avatar Nov 15 '23 18:11 XOR-op

Maybe we could add a config option like whichstop = ' ,.!' to allow users to specify which characters should as a stop char?

sxyazi avatar Nov 17 '23 06:11 sxyazi

That makes sense. I think we could implement two features separately:

  1. Better handling of word separator, like readline-style word movement and skip '/' under some cases for ergonomic experience.
  2. Config option like whichstop = ' ,.!' allowing users to customize their word separator.

XOR-op avatar Nov 17 '23 15:11 XOR-op

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.

sxyazi avatar Nov 17 '23 18:11 sxyazi

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.

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.

XOR-op avatar Nov 17 '23 18:11 XOR-op

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
^    ^     ^            ^

musjj avatar Nov 17 '23 18:11 musjj

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.

sxyazi avatar Nov 20 '23 07:11 sxyazi

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.

XOR-op avatar Nov 21 '23 04:11 XOR-op