Skip to content
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

Change slightly how x/X works #2590

Closed
dpc opened this issue Nov 23, 2018 · 74 comments
Closed

Change slightly how x/X works #2590

dpc opened this issue Nov 23, 2018 · 74 comments

Comments

@dpc
Copy link
Contributor

dpc commented Nov 23, 2018

Right now: https://github.com/mawww/kakoune/wiki/Migrating-from-Vim states, that to "copy a line", one has to do xy.

That's however is technically not true. Because if you're on an empty line, you are going to copy the next line. My point is not that Wiki is wrong, but that to reliably select a current line, one has to use <a-x>, so that reliable "delete current line" is <a-x>d which is way less convenient, especially comparing to very handy dd in Vim.

While I really appreciate the "visual feedback" of Kakoune, and I think it's amazing when doing anything non-trivial, having to rely on the visual output, while doing trivial and mechanical operations like deleting lines is terrible. The fact that I have to pay attention to exact content of everything just so I don't delete a different line is just slowing me down.

Edit: Also, not that x is practically unusable for scripting. If your script needs to select a line somewhere, it has to go with <a-x> as the only reliable one.

I am aware that x/X needs to extend down and I also believe it's undesirable to loose that property.

So I was wondering if instead of the rule "x/X goes to the next line if the cursor is on LF and already selects the whole line" the new one: "x/X goes to the next line if it previous action was also x/X`" wouldn't be better?

This way first x reliably selects the current line, and following ones move downwards. Everything becomes trivially predictable. The behavior of the movement would be conditional only on the keystrokes of the user.

This change should be quite easy to get used to for the existing users. In case they press x/X while having a whole line selected somehow without using any x command (typically because of being on an empty line), they will just have to press x/X again. It would be awesome if some form of visual change was there, but it's not terrible if it isn't. Maybe there could be a special color or some other visual designation for the "full-line quasi-mode" that one enters after first x and leaves after first non-line command, which would make the whole thing obvious.

Note: Any line-related command (xX<a-x><a-X>) should work for any line-related command for the purpose of "consecutiveness".

@dpc
Copy link
Contributor Author

dpc commented Nov 23, 2018

BTW. If the above idea/paradigm of actions having a tiny bit (actually precisely one bit!) of memory is accepted, then it would make sense to improve <a-x> and <a-X> slightly. Right now they are idempotent, which is not very useful. But they could work somewhat analogous and subsequent <a-x> could extend selection by one line on both top and bottom of the current selection(s), and subsequent <a-X> could shrink it. This could be used for "select full-lines of the current selection and add/remove N before, and N after it".

This way all the x key variants become quite nifty tools for selecting many lines visually, which is awesome because line-based operations are bread and butter of code editing. For perfection, one would need just some way to extend only upwards or move the whole selection one line in a given direction. That would allow selecting any range of lines by staring just somewhere in the middle and extending up/down by pressing x-variants. I guess, due o running out of x-key combinations, some other key could be used.

Hmm... crazy idea... maybe if we follow the logical model of "full-line quasi-mode" that one enters after x-and-alikes, then even some existing keys/movements could do something similar to what they do right now, but in a line-oriented way. Eg. J effectively extends selection downwards, so maybe it could extend or move full-line selections downward, and K could extend or move full-line selections upward. Just thinking aloud.

@dpc
Copy link
Contributor Author

dpc commented Nov 23, 2018

I'm trying to implement it here: https://github.com/dpc/kakoune/tree/line_editing

@maximbaz
Copy link
Contributor

Cross-linking threads: #2206

@dpc
Copy link
Contributor Author

dpc commented Nov 23, 2018

I think I've succeeded. https://github.com/dpc/kakoune/tree/line_editing

This does not add any visual feedback, or implement any of the "extension" ideas. If I mange, I will try to implement it.

dpc added a commit to dpc/kakoune-dpc that referenced this issue Nov 23, 2018
@dpc
Copy link
Contributor Author

dpc commented Nov 23, 2018

I probably do a terrible job at it, but I'm trying out the more extreme ideas here: https://github.com/dpc/kakoune/tree/line_editing_bells_and_whistles . Please read commit logs for instructions.

@maximbaz
Copy link
Contributor

I tried line_editing branch and it's quite interesting experience, I think I'm gonna use it at least for another few days.

I definitely recommend others who are reading this thread to try it for yourself.

@dpc
Copy link
Contributor Author

dpc commented Nov 24, 2018

My "bells and whistles" is almost ready.

I wish I could get the selection to change color after one enters "line selection mode" for both the "conservatie" and "bells and whistles" branch. Then it would all be obvious, and quite pretty IMO.

@maximbaz
Copy link
Contributor

I'm thinking about re-implementing your line_editing branch in kakscript, I like your approach and personally I'd like to continue using it, but I'm concerned it will be difficult to convince everyone to change the default behavior, and maintaining a fork is kinda annoying.

@dpc
Copy link
Contributor Author

dpc commented Nov 24, 2018

@maximbaz Better than nothing. :) . I still hope that after trying it out, more people will be convinced.

@Screwtapello
Copy link
Contributor

Screwtapello commented Nov 25, 2018 via email

@maximbaz
Copy link
Contributor

Maybe because I wasn't using kakoune for very long yet, but with vanilla kakoune even though I understand the logic in how x behaves on empty lines, very often I still hit XXX one too many times and then adjust my selection with K. It has even become a habit that I didn't think about until yesterday, but it's sad to have such habit in the first place. With line_editing branch my selection is always precise, because the number of times X needs to be pressed is predictable.

@dpc
Copy link
Contributor Author

dpc commented Nov 25, 2018

I would be guessing that long time users have internalized the current rules, and they just avoid pressing x on empty lines altogether, so they won't really feel a difference much if at all. New users who haven't internalized "working around" the current logic should feel an improvement.

After a day with this change, the biggest problem with my Kakoune experience is totally gone, and line-wise I am always precise. I still feel that yy, dd are slightly more convenient because they repeat the same key, but obviously it's impossible to get it in Kakoune's model, and I think Kakoune model is better overall.

Note: I'm using my line_editing_bells_and_whistles branch, where <a-X> and <a-x> are no longer idempotent, but start shrinking/extending the selection, and HJKL in "full-line selection" was modified to allow nice control. I don't have strong feelings about these additional changes. They not as "life-changing" as being able to select lines precisely without paying too much attention.

Now the last problem with getting used to kakoune is anchor vs cursor ordering. And I think I just need to modify the colorscheme to make the cursor more distinct vs the selection.

That's also one part that I don't understand how to handle in line_editing. When doing consecutive x-s, must the cursor always be below anchor, and should command like <a-;> be allowed to preserve the x-s "consecutivness"? It seems to me that in the current rules: the fact that cursor has to be below anchor was artifact of how the logic worked. With my change, this limitation is no longer neccessary, but as a noob I don't fully realize if it makes any difference.

@eraserhd
Copy link
Contributor

I picked up using <a-x>XXX... somewhere. This works, but is awkward. I think I'll try this out a bit later.

@lenormf
Copy link
Contributor

lenormf commented Nov 25, 2018

Looks like a glass half-empty/half-full issue, you'll prefer x or a-x depending on which you already use the most.

The easiest thing to do is revert the two mappings in your user configuration, that works for everybody.

@dpc
Copy link
Contributor Author

dpc commented Nov 25, 2018

@lenormf I thought like this at the beginning, but it doesn't solve anything. <a-x> doesn't go downwards. So you loose ability to xxxx to go down with line selected. And you have to do xXXXX for predictable extending lines.

With my change you can predictably do xxxxxx or XXXXXX, depending on what you want, because it fixes the fundamental problem that in Kakoune there's no way to differentiate "being on an empty line" and "having a whole line selected". They are just conflated, and rationalized away.

You can think of my change as introducing a difference between "selecting all characters in a line" and "selecting a whole line". The first time you hit x or X you select the line as a whole. So if you were on an empty line, it looks the same, but it is different. If I had more time or kakoune knowledge I would actually implement some visual difference to make it clear.

@dpc
Copy link
Contributor Author

dpc commented Nov 25, 2018

I'm thinking, right now when you select lines it looks like this

foo.
.
bar.

Where . are invisible, but highlighted as all other characters.

What if it looked like this:

foo........................................
...........................................
bar........................................

This way when you first hit x or X on a line, you can see that you selected and highlighted the whole concept of a line - going to infinity (to the right edge of your window).

This way x/X on an empty line has a visual feedback, and makes it immediately obvious when your movement looses the line selection, and goes back to selecting bunch of characters.

@Screwtapello
Copy link
Contributor

@dpc See also #1909

@dpc
Copy link
Contributor Author

dpc commented Nov 25, 2018

@Screwtapello Thank you!

@dpc
Copy link
Contributor Author

dpc commented Nov 25, 2018

I've implemented it and pushed in line_editing_with_highlight branch. I think it looks and works OK. It doesn't even seems that noisy. I've also rebased line_editing_bells_and_whistles on top of it now, where it makes even more sense.

@dpc
Copy link
Contributor Author

dpc commented Nov 25, 2018

Couple of things to polish:

  • what should the rules of anchor vs cursor be; should anchor always < cursor when working on full lines, or should we just preserve it? In case it's fixed, is there any point in displaying cursor highlight? And if yes, should be at the end of the "virtual line"?
  • Which operations should preserve the line-mode? I've just noticed, that I would like select to potentially select all lines matching given regex, and keep the line-mode.

Just to repeat : "line editing quasi-mode" is an extension to the core idea of changing how x/X works. One could always explain the new behavior with just "goes down only if consecutive" and call it a day. I'm just experimenting here. :)

@maximbaz
Copy link
Contributor

line_editing_with_highlight feels weird to me because it's specialized for x/X, for example here's how XXXK behaves:

peek 2018-11-25 13-12

@maximbaz
Copy link
Contributor

@dpc found a serious downside of this approach, with your branch it is impossible to make the following binding work (to make X always extend down), because <a-:> is resetting the "previous action was also X":

define-command -hidden -params 1 extend-line-down %{ execute-keys "<a-:>%arg{1}X" }
map global normal X ': extend-line-down %val{count}<ret>'

Pressing XXX does not select multiple lines.

@dpc
Copy link
Contributor Author

dpc commented Nov 25, 2018

@maximbaz : line_editing_bells_and_whistles implements K and friends (though I'm not sure if in a way you would like it - but it can be changed). It feels much more natural there, but as I said - if this was to take approach of a full conceptual "line editing mode", it might need even more commands that make sense in line-only mode implemented. Give it a try and tell me what you think.

@dpc
Copy link
Contributor Author

dpc commented Nov 25, 2018

@maximbaz I don't understand why would you need this mapping now, as XXXX just does what you want by default. Maybe you just need to disable this workaround macro that is no longer need and is getting in a way.

But I've mentioned before that I think I <a-;> and <a-:> should probably be added to commands that don't enter line editing, but do preserve it. Similarly selection on line granularity makes sense now.

@dpc
Copy link
Contributor Author

dpc commented Nov 25, 2018

I made a recording of how it looks: https://asciinema.org/a/213645

@maximbaz
Copy link
Contributor

I'll reply in full later, but to explain you the need for this macro, try selecting with KKKK and then pressing X - I want this to continue expanding selection.

@dpc
Copy link
Contributor Author

dpc commented Nov 25, 2018

@maximbaz If you did JJJ and then XXX that would work. KKK and JJJ is kind of a bug, which could be addressed by thinking through the previous question I posted above:

what should the rules of anchor vs cursor be; should anchor always < cursor when working on full lines, or should we just preserve it? In case it's fixed, is there any point in displaying cursor highlight? And if yes, should be at the end of the "virtual line"?

So: I agree with your desired behavior, and this can be easily fixed.

@dpc
Copy link
Contributor Author

dpc commented Nov 25, 2018

@maximbaz Like this? https://asciinema.org/a/213654 . I've pushed it in line_editing_with_highlight and line_editing_bells_and_whistles, though I tested more in the later.

@dpc
Copy link
Contributor Author

dpc commented Nov 30, 2018

I decide I want to delete that line, and usually the content of the line is pretty important to make that decision.

Maybe I'm from a different world, but I hack and slash my code left and right, with a speed of a machine gun, selecting, deleting, yanking, pasting stuff all the time, jumping between buffers left and right. There's always undo, or even git reset --hard HEAD. I don't meditate at every line.

In my view x is not really inconsistent, its logic is relatively simple "select fully the line on which the cursor lies,

It doesn't matter there's a consistent explanation for it. What matters is if that's what the user naturally expects. Otherwise one could have any arbitrary rule like "selects full line if it's Saturday". Consistent? Consistent! Easy to understand? Yes! Can users keep track if it's Saturday? Of course they can: it's even easier, because days don't change every time you change the line! Does it give a great UX? Not really.

@dpc
Copy link
Contributor Author

dpc commented Nov 30, 2018

Just had an another out of the box idea.

Maybe there should be a -1 column? Yeah, you heard me right. A column that is left of what everybody consider the first column.

Whaaaaa? you ask?

Here's how it works. Column -1 is magical place where the cursor lands when the \n of the previous line's \n was selected (in which case previous \n is highlighted), or when the line itself is empty (in which case previous \n is not highlighted).

To visualize.

Legend first:

  • first column is the -1 one, and is . if empty (just for visualization)
  • X is cursor.
  • UPPERCASE is selection.
  • _ is highlighted newline (just for visualization)

Staring position:

.abXd
.
.abcd

After moving with j to a an empty line:

.abcd
X
.abcd

After first X:

.abcd
._
Xabcd
.

after second X:

.abcd
._
.ABCD_
X

Benefits: expected behavior. the additional "state" (empty vs full line selected) is now visible and part of positioning system.

The column does not have be really "wasted". If cursor shapes are available cursor on line -1 could be visualized as | on column 0. If cursor shapes are not available, the cursor could be on whatever is on the left side of column 0. It doesn't hurt anything.

Variations of this idea exist. Column -1 could be used eg. to place the cursor, only if the cursor lands on an empty line, but haven't selected the \n on that line yet. In every case the core idea is : this additional bit of information (empty line vs fully selected one) has to go somewhere. If it can't go into invisible sub-mode state, it has to go into the editing buffer itself, in form of an additional column.

@mawww
Copy link
Owner

mawww commented Nov 30, 2018

My point is mostly that I dont see how the proposed change is a clear improvement on the current behaviour. In both cases we need to track an additional state ("is the line already fully selected" or "was the last command hitten in this client x), one has the advantage of being visible, the other has the advantage of being something you just did (most of the time). My main concern with the new change is that it seems to break scripting in subtler ways because that state is an additional bit, instead of being derivable from the current selection and buffer state.

(Just saw that you posted a new proposal, I'll go through it when I get time, gotta go now).

@dpc
Copy link
Contributor Author

dpc commented Nov 30, 2018

My main concern with the new change is that it seems to break scripting in subtler ways because that state is an additional bit, instead of being derivable from the current selection and buffer state.

This state could be tracked per-client/script. I also thought about keeping that state only for interactive input from the terminal. This is especially the route I will take if there will be no consensus on making x work in a way I would be happy with. I'll just keep rebasing my patches, and scripts and plugins will not know that I've implemented any change in a behavior.

@dpc
Copy link
Contributor Author

dpc commented Jan 15, 2019

@mawww So, as we've talked on IRC - I just went ahead and implemented it in a text editor I hacked together in Rust: https://github.com/dpc/breeze . Obviously toons of stuff is missing there and there are probably many bugs in whatever is implemented, but I did implement x and X in a "out of the box idea" from couple of posts before.

@dgrisham
Copy link

dgrisham commented Apr 17, 2019

Any progress here? Think alexherbo2/auto-pairs.kak#9 is waiting on this, would love to see resolution but I know this was somewhat contentious.

Edit: Never mind, I was wrong about the dependency. Ignore this.

dpc added a commit to dpc/kakoune-dpc that referenced this issue May 7, 2019
@aldanor
Copy link

aldanor commented May 15, 2019

Late to chime in, but I also wanted to say I love @dpc's suggestion.

Regarding the discussion about "stateful vs stateless" behaviour - think of it this way - either it's implemented in a stateful way which may be less elegant etc, or the user has maintain state in their head ("I'm on an empty line, hold on, I shouldn't be pressing x, I need to press a-x to select it"). The more stateless the end-result is from user's perspective, the easier it is to train the muscle memory (e.g., "xd to delete a line, wherever you are located and whether it's empty or not"), and computers are arguably better at maintaining state than users' heads.

dpc added a commit to dpc/kakoune-dpc that referenced this issue May 25, 2019
dpc added a commit to dpc/kakoune-dpc that referenced this issue Jun 15, 2019
dpc added a commit to dpc/kakoune-dpc that referenced this issue Jul 21, 2019
dpc added a commit to dpc/kakoune-dpc that referenced this issue May 4, 2020
@pickfire
Copy link
Contributor

pickfire commented May 21, 2020

I find it quite hard to do something like C or S or D in vim without doing scripting. Comparing to vim, one needs to do giGlc in kakoune but only S in vim.

@greneholt
Copy link
Contributor

greneholt commented Dec 17, 2020

Possibly foolish question: why does the current behavior of x exist at all? As I understand it, x has the following purposes:

  1. When the selection is part of a single line, extend the selection to cover the whole line (unless the cursor was on an empty line, in which case it selects the next line). This is the same as <a-x>, except without the inconsistency on blank lines.
  2. Select multiple full lines by hitting X repeatedly. For example, XX will select two lines (unless the cursor was on an empty line, in which case three lines will be selected). This can be accomplished with the same number of keystrokes with J<a-x>, except without the inconsistency when starting on a blank line.
  3. When a selection contains parts of multiple lines, extend the selection to the end of the last line. This can also be accomplished with <a-L>. (However this doesn't select the newline, if any.)
  4. When the selection contains parts of multiple lines, change the selection to only the the last line. The same can be done with ;<a-x>. (This does require more keystrokes, but I would guess this is a very rare use case.)

As a new user to Kakoune, I found the distinction between x and <a-x> very confusing, and now I know why. x seems to be redundant, with all of its uses accomplishable in other ways with no more keystrokes (and more consistent behavior on 1. and 2.).

I'm going to be trying out the following mappings for my personal use:

map global normal x <a-x>   
map global normal X giGl    
map global normal <a-x> ghGl

@mawww
Copy link
Owner

mawww commented Dec 18, 2020

@greneholt I have actually been thinking about the same thing recently, that we ought to be able to get rid of x altogether (making it behave as <a-x> currently does). I have been experimenting a bit with it and its currently quite painful due to muscle memory, its also weird to do to hold shift+j to select a bunch of lines then have to release shift to hit x.

Still trying, I think Kakoune has too many normal mode commands at the moment so I'd be really happy to remove some redundant ones.

@forbesmyester
Copy link

forbesmyester commented Apr 30, 2021

As an observer with limited understanding (which has grown reading this).

I did <s-c> as it's quite prominent in the docs.
I also did <a-s-c> as it was easy to find, which was neat.

I did x which was interesting.
I saw that <s-x> did the downwards selection.
But <a-s-x> didn't do upwards selection, which my brain just expected to work (doing <s-x> in the opposite direction.

I wonder if it is (somewhat) safe to remap x to <a-x>.

BTW <a-x>J does something like linewise selection, but <a-x>K doesn't as the cursor stays on the EOL, where I'd like it to stay at the start of the line.

I realize that I have a broken mental model and it doesn't match up with Kakoune. I don't think Kakoune should come pre-bent to my mental model, but at the moment I don't understand how to safely bend it.

@Delapouite
Copy link
Contributor

A new experimental branch has been started on the topic: 850c88c

dpc added a commit to dpc/kakoune-dpc that referenced this issue Jul 29, 2022
@arrufat
Copy link
Contributor

arrufat commented Nov 14, 2022

I think this can be closed now :)

@dpc
Copy link
Contributor Author

dpc commented Nov 14, 2022

I'm waiting for NixOS to bring the latest version, but after reading the latest release notes, I think this one is indeed done.

@dpc dpc closed this as completed Nov 14, 2022
@dpc
Copy link
Contributor Author

dpc commented Nov 21, 2022

Correct me if I'm wrong but after using new vanilla version for a while it seems that no new state was actually introduced, so there's no way to differentiate between unselected empty line and selected empty line. That's why x does not advance at all anymore, and people on forums can't make X behave to consistently extend selection by a line.

I guess back to my fork it is for me.

@krobelus
Copy link
Contributor

it seems that no new state was actually introduced, so there's no way to differentiate between unselected empty line and selected empty line

technically there is, the state is the "target column" but it's not exposed explicitly

given

abc

def

where the selection is on a.

  • jj will move the cursor to d
  • jxj will move the cursor to the EOL character after def (selected empty line)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests