Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussing how to handle different variants of emacs keybinding #370

Closed
XOR-op opened this issue Nov 15, 2023 · 12 comments
Closed

Discussing how to handle different variants of emacs keybinding #370

XOR-op opened this issue Nov 15, 2023 · 12 comments
Labels
feature New feature request

Comments

@XOR-op
Copy link
Contributor

XOR-op commented Nov 15, 2023

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 XOR-op added the feature New feature request label Nov 15, 2023
@XOR-op
Copy link
Contributor Author

XOR-op commented Nov 15, 2023

cc @musjj @Tweoss @sxyazi

@sxyazi
Copy link
Owner

sxyazi commented Nov 15, 2023

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

@musjj
Copy link
Contributor

musjj commented Nov 15, 2023

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.

@XOR-op
Copy link
Contributor Author

XOR-op commented Nov 15, 2023

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
Copy link
Contributor Author

XOR-op commented Nov 15, 2023

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.

@sxyazi
Copy link
Owner

sxyazi commented Nov 17, 2023

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

@XOR-op
Copy link
Contributor Author

XOR-op commented Nov 17, 2023

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.

@sxyazi
Copy link
Owner

sxyazi commented Nov 17, 2023

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.

@XOR-op
Copy link
Contributor Author

XOR-op commented Nov 17, 2023

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.

@musjj
Copy link
Contributor

musjj commented Nov 17, 2023

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

@sxyazi
Copy link
Owner

sxyazi commented Nov 20, 2023

Hi, I've created a PR to make the Emacs keybindings configurable. #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.

@XOR-op
Copy link
Contributor Author

XOR-op commented Nov 21, 2023

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.

Repository owner locked and limited conversation to collaborators May 16, 2024
@sxyazi sxyazi converted this issue into discussion #1044 May 16, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
feature New feature request
Projects
None yet
Development

No branches or pull requests

3 participants