vimneovim

Yanking until (t) doesn't move cursor while (T) does


Given this text: (^ is supposed to be the cursor)

the quick brown fox
           ^

The latter results in:

the quick brown fox
     ^

I'm very confused by this behaviour. Why does T move the cursor and t doesn't?

I searched online and asked several ai models. all of them say that "Any vim operator (y, d, c, etc.) + motion will always leave your cursor at the end of that motion" but that doesn't seem to apply to yt<char>. Did I miss something?


Solution

  • Vim behaves that way because the POSIX vi specification explicitly requests this behavior (emphasis mine):

    Current column:

    1. If the motion was from the current cursor position toward the end of the edit buffer, unchanged.
    2. Otherwise, if the current line is empty, set to column position 1.
    3. Otherwise, set to the last column that displays any part of the first character in the file that is part of the text region specified by the motion command.

    It's a little more readable (although harder to find) in Vim's note after :help blockwise-register:

    Note that after a characterwise yank command, Vim leaves the cursor on the
    first yanked character that is closest to the start of the buffer.  This means
    that "yl" doesn't move the cursor, but "yh" moves the cursor one character
    left.
    Rationale:  In Vi the "y" command followed by a backwards motion would
            sometimes not move the cursor to the first yanked character,
            because redisplaying was skipped.  In Vim it always moves to
            the first character, as specified by Posix.
    With a linewise yank command the cursor is put in the first line, but the
    column is unmodified, thus it may not be on the first yanked character.
    

    Substitute yl and yh with ytx and yTx, respectively, and you have the documentation.

    "Because the standard says so" is certainly a strong argument, but not a real explanation. We might find a little solace in original vi apparently being even more inconsistent. Sometimes it would move the cursor and sometimes it wouldn't.

    How is this consistent?

    Consider the following two examples where ^ denotes the cursor position and ": shows the contents of the unnamed register:

    With t, e.g. ytr:

    the quick brown fox
         ^      
    ":   uick b
         ^
    

    With T, e.g. yTq:

    the quick brown fox
               ^
    ":   uick b
         ^
    

    We can see that the cursor position relative to the yanked text is consistent.

    If you don't think about the cursor position as relative to the text, but relative to the column on the screen, it's also consistent. It doesn't matter whether you did a forward or backward motion, you will end up in the same column. Beautiful consistency.

    Also think about dt/dT and ct/cT where you would almost certainly want the cursor to move to the beginning of the text which you just deleted. This means that - in a script or macro - you can exchange a dT command with yT and the cursor will move the same. It won't break the rest of the script or macro. Consistency for the third time.