helix-editor / helix

A post-modern modal text editor.
https://helix-editor.com
Mozilla Public License 2.0
33.31k stars 2.47k forks source link

Remove yank behavior of `c` and `d` #3001

Closed samholmes closed 6 months ago

samholmes commented 2 years ago

I think Vim made a mistake by letting c and d pollute the default register by "yanking" the contents of the change or deletion. I'll make a short case as to why I think Helix should make a bold decision to not do this like vim (and maybe even kak).

Why not?

The reason why d and c should not also yank is simple, because you can yank before d and c easily with y. You cannot as easily "prevent yank" without knowing about registers and particularly the black-hole register "_; so you're left with "_d or "_c which is too often a use-case to make it not the default behavior. The times when you want to yank before a modification is easily opt-in with yd and yc. Simply put, the answer why the default behavior should be inverted: it's the correct UX.

Note, in View mode, y does not remove the selection like it does in vim. This positions Helix to be able to even consider the correct behavior.

With the correct default behavior in place, users can edit happily knowing that when they copy something to their default register many modifications back in their history, they will not accidentally overwrite the default register with d and c. The foot-gun has been holstered.

How do we migrate?

Some users (vim and kak alike) will not expect this change and will perhaps not like to modify their habit. However, this could easily be mitigated with a config to change the default behavior back (which is what user's do already). If a user doesn't like the position Helix makes, they can modify the behavior or change their habits.

Please.

the-mikedavis commented 2 years ago

This is currently implemented in the delete_selection_noyank (A-d) and change_selection_noyank (A-c). You can switch the keybinds in your config.toml:

# ~/.config/helix/config.toml
[keys.normal]
A-d = "delete_selection"
d = "delete_selection_noyank"
A-c = "change_selection"
c = "change_selection_noyank"

I'm not sure it makes sense for this to be the default keymap though - yd or yc is the same number of keystrokes as A-d or A-c so if anything it would make sense to remove the A-d and A-c keybinds. Without anything to replace those keybinds I'm not sure it makes much sense to reclaim them. Plus in my experience, if you're running around a code-base selecting lines/blocks/etc and deleting them with d and then placing something new with y, you might be better off using R which doesn't affect the yank register.

On the other hand I agree with your reasoning:

you can yank before d and c easily with y.

samholmes commented 2 years ago

@the-mikedavis A-d being Alt+d or Option+d on macOS, does not appear to delete without yank.

To the point about R: Yes, R is best for replacing text with the last yanked text. But the point isn't about using d or c to replace text; rather the point is that if you're doing d or c to insert some text, and then later you chose to use R or p to replace/paste what was yanked, then you'd be remise to find what you thought was in the register has been hijacked by your insert operation. If we instead make yank more explicit with y, then you no longer have to feel uneasy about accidentally overwriting your register as you operate on your file. The reason why I think yank should always be explicit, is because registers have no history and are subject volatile. From a UX perspective, we must treat yank with respect, we must make it an explicit action to yank with y.

As for keystroke count: yd is more than d because it does more than d. There is no need for A-d anymore. If one really wished for less keystrokes, then x is suitable for the job, but then we'd need a key for line-selection. By introducing x as a shorter version of yd we open a can of worms because now why not a shorter yc? Maybe it's best to be conservative here and let yd remain yd for the sake of all that is good.

EpocSquadron commented 2 years ago

I for one would actually be in favor of this change. One thing I think helix does well is being discoverable and obvious, and the more I think on things the more I realize that key bindings that do conditional logic or do more than one thing make things a little less obvious and discoverable.

samholmes commented 2 years ago

I for one would actually be in favor of this change. One thing I think helix does well is being discoverable and obvious, and the more I think on things the more I realize that key bindings that do conditional logic or do more than one thing make things a little less obvious and discoverable.

I couldn't agree more. I think the challenge with modal editing has always been a UX challenge. The primary UX mis-step has not been communicating the mode to the user. The mode that you're in should be obvious and not require any user memory (I shouldn't need to remember that I entered insert mode before leaving my desk to go to the restroom, for example). Furthermore, learning curve should compound over-time. If I learn that y yanks text, and d deletes text, I should be able to use that knowledge to cleverly compose those actions into "cut". I argue that any action that is composable should remain a composition, and the atomic actions (those which cannot be composed) deserve their own single-key binding. This is a loose goal with maybe some exceptions.

danyspin97 commented 2 years ago

@the-mikedavis A-d being Alt+d or Option+d on macOS, does not appear to delete without yank.

It works for me. If they don't work for you (via the the keybindings or the command palette) please open a bug. I'd consider them functional to continue the discussion forward.

Why not swapping A-c and A-d with c and d respectively? So users can still have a command that yanks and can use it when they need it. Also it makes sense to have the keybind with less keystrokes to do the most simple action.

the-mikedavis commented 2 years ago

It seems like that's a macos specific problem you could fix in terminal emulator: https://github.com/helix-editor/helix/issues/2280

Swapping A-c and A-d could work but then it's the same number of key-presses as yc and yd, so we might as well just remove the current c and d mappings.

I'd like to try this out in my config for a bit but it seems like a reasonable change to me in theory

samholmes commented 2 years ago

I'm using Kitty (terminal app) and it doesn't register the Option key as Alt

I set max_os_opt_as_alt yes in my kitty.conf but still no fix.

EDIT: Never mind, it seems to have worked.

danyspin97 commented 2 years ago

Swapping A-c and A-d could work but then it's the same number of key-presses as yc and yd, so we might as well just remove the current c and d mappings.

Except that now there is a delay for simple yank (y) and new composable commands unfamiliar to anyone. If they take the same keystrokes, then A-c and A-d seems better to me.

EpocSquadron commented 2 years ago

There would be no delay for yank, it would simply be sequential.

danyspin97 commented 2 years ago

There would be no delay for yank, it would simply be sequential.

There would be a small timer after y to let you type the next character of the command (either c or d) and in case you don't, it will assume that it's just yank action. It's a small delay it's not the end of the world, but it's something to consider.

EpocSquadron commented 2 years ago

It wouldn't need to care what comes next, any which way it yanks, what comes after is entirely separate.

the-mikedavis commented 2 years ago

No the yank and delete/change would be separate commands executed independently but in sequence - there wouldn't be any need to have a delay. So if you would want to delete/change without yank, you would form a selection and then hit d/c. If you wanted to yank first, you would just hit y in between making the selection and deleting/changing which is the current behavior of y

amfaber commented 1 year ago

As someone trying to get into an editor like vim or helix, the fact that deleting a section overwrites my buffer was one of the worst design design decisions about vim to me. I am happy that there is an explicit command for not copying in helix and that I can simply swap the keybindings. I fully agree that this should be the default behavior.

Another approach could be to keep the copying behavior of c and d, but to yank into some designated register which isn't the default one?

lukasjuhrich commented 1 year ago

Why not implement a yank-stack? I see this as the more powerful solution in contrast to the mentioned compromises… unless I am totally blind to some disadvantages.

Basic idea

Benefits

Editing Performance

For the sake of discussion, let A-p be the chosen keybinding for the cycle operation.

  1. Typing something like p A-p A-p … is fairly primitive and after a few times of doing that, does not require any mental effort.
  2. With everything pushed and nothing lost, an implicit advantage is that at no point in editing I have to make a conscious choice of whether to use a yanking or non-yanking delete command; neither do I have to „plan“ a bunch of editing actions ahead with a specific thing I want to paste in mind.

I'm not sure what the best keybinding for the cycling command would be. Perhaps A-p/A-P? Perhaps p/P? This would add context-dependence, but could work, because pasting twice could be done via 2p anyway; however in that context it might be a good idea to let ESC exit the „cycling-mode“.

Learnability

The concept is not trivial, but completely clear once you understood it. So:

  1. Teaching this is just a matter of putting this into the tutorial.
  2. One could think about making this discoverable on one's own by giving the user some sort of hint (say greyed out in the command line) that, directly after pasting, they're in the “past-cyclable mode“ and have the opportunity to paste „earlier“ items from their stack. However, that might get annoying fast since you only need to see this hint once and then never again because you know of the existence of the mechanic.

Implementation complexity

After pasting, helix has to remember the selection and the position in the stack as part of the „cycling state”. This has to be cleaned up after „leaving“ that mode, i.e. doing something other than pasting.

Anecdotal testimony

As someone having used emacs for quite a few years – first vanilla, then with vim keybindings – and various IDEs with vim keybindings, this is the one big feature which for me was painfully missing in vim. Rectifying this in helix could be a great opportunity.

casperin commented 9 months ago

Fwiw I took the (on July 7 2022) suggested keybindings and stuck it in my config. Re-found it today (after a year and something) and thought I'd chip in to say that it just feels less surprising. I have one machine that I rarely ssh into where I forgot to add it, and every time I get hit by suddenly having junk in my register. And every time I have a moment of confusion.

I'm a big fan of this editor and I hardly change any default settings. But this change is staying in my config if this change request doesn't make it.

gyreas commented 9 months ago

Tldr: 100% in favour.

Well, I just discovered that behaviour even exist, which is fuelled by my recent coding spree. I find yank before change/delete more natural since their function is in their names. Also, as mentioned above, the contents of registers are volatile, making them even more so by yank during change/delete is not user friendly per se.

Alt-d is a very obscure thing, I've probably only ever used it handful of times (or at all) throughout 2023.

cotneit commented 7 months ago

There's an issue https://github.com/helix-editor/helix/issues/6900 for emacs kill-ring proposed in https://github.com/helix-editor/helix/issues/3001#issuecomment-1515290651

I think it would definitely make yanking c and d less frustrating, especially if it's implemented like https://github.com/helix-editor/helix/issues/6900#issuecomment-1704983746 suggests (with search).

I don't have a strong opinion here since I'm still getting used to modal editing, but one thing I miss from VS Code is how easy it is to move lines up and down https://github.com/helix-editor/helix/issues/2245. Currently xdp is the best alternative to that, removing yank from d would turn it into xydp, which isn't a huge deal but does add up for his particular usecase since it already feels more involved than it needs to be.