Why does c/_<CR> behave differently from cf_? - vim

Let's say I have the text some_func in my buffer and my cursor is on the s.
If I type c/_<CR> (where <CR> represents the [Enter] key), it removes the text some and enters insertion mode. If I type cf_, it removes the text some_ and enters insertion mode. The difference is that / keeps the _, but f removes it.
However, /_<CR> and f_ both behave identically (both move the cursor to the underscore), so why do they work differently for the c command? Is there a way I can configure Vim to make them consistent?

:help / says:
Search forward for the [count]'th occurrence of
{pattern} |exclusive|.
and :help f says:
To [count]'th occurrence of {char} to the right. The
cursor is placed on {char} |inclusive|.
Pressing <C-]> with the cursor on either |exclusive] or |inclusive| leads to :help exclusive, which says:
A character motion is either inclusive or exclusive. When inclusive, the
start and end position of the motion are included in the operation. When
exclusive, the last character towards the end of the buffer is not included.
Note that this only concerns motions used after an operator, c in this case, not standalone motions used for moving the cursor around. Check out :help operator for a more in-depth look at how operators and motions interact.

Actually, c/_⏎ will change up to the next _ even if the next _ is on a different line, while cf_ will do nothing if there is no _ for the rest of the line. It seems like this is just a quirk of / when used as a motion; in this context, it just works more like t than f.

Related

In vim, why does c_ behave as if cc?

As the title.
Since _ is the movement to the first non-whitespace character in the line, and c + movement generally means to change (aka delete + go into insert mode) the buffer from the cursor to the movement, why does this not work? It seems to delete the entire line, instead of from the cursor to the beginning of line (aka cc or C). Is there an alternative for this?
This is a known issue in vim. See https://github.com/vim/vim/issues/2189#issuecomment-334441965:
_ is a linewise movement (I guess because of VI compatibility). All commands (like d or c) that are combined with a linewise movement
affect whole lines. You can make the movement (in combination with a
command) characterwise by prefixing it with v. So cv_ and dv_ should
do what you want.
Because _ is defined as an up/down motion. From :help up-down-motions:
_ <underscore> [count] - 1 lines downward, on the first non-blank
character |linewise|.
It just defines the position where the cursor will end up once the up/down motion is finished. Thus, c_ operates on lines, just like cj does.
Amadan's answer give's the reason why c_ doesn't work.
Answering the second part of your question
Is there an alternative for this?
Yes, there is. Use c^!
Check out :help left-right-motions and you'll find:
^ To the first non-blank character of the line.
exclusive motion.

What is the difference between s, c and r commands in vi/vim?

I am trying to clarify this from the OReilly book on Vim, but the examples presented aren't clear enough.
Clarification via examples/use-cases instead of direct explanation would be very helpful.
The Sample text could be:
With a
screen editor,
you can
scroll the page, move the cursor.
Assume you have foo in the document, and the cursor is on the f.
Now, pressing rb will change this to boo, and you are back in command mode. Pressing sb will accomplish the same, but you are in insert mode and can insert more characters. Finally, c requires some kind of motion; e.g. you can type cw to remove the whole word and enter insert mode. On the other hand, cl is essentially the same as s.
Descriptions
s (substitute) will delete the current character and place the user in insert mode with the cursor between the two surrounding characters. 3s, for example, will delete the next three characters and place the user in insert mode.
c (change) takes a vi/vim motion (such as w, j, b, etc.). It deletes the characters from the current cursor position up to the end of the movement. Note that this means that s is equivalent to cl (vim documentation itself claims these are synonyms).
r (replace) never enters insert mode at all. Instead, it expects another character, which it will then use to replace the character currently under the cursor.
Examples
Take your sample text, and image the cursor at the beginning of the word 'can' (line 3).
Typing spl<Esc> in vi/vim.
Here, we have s for substitute. pl is the text to insert, and <Esc> will exit insert mode. Put together, it will change can to plan like this:
With a
screen editor,
you plan
scroll the page, move the cursor.
Typing cwcould<Esc> in vi/vim.
c is for change, and w tells c to delete through the next word, can. Next we type the text for insert mode, could. Lastly, we need to type <Esc> again to exit insert mode. The command will change can to could like this:
With a
screen editor,
you could
scroll the page, move the cursor.
Typing rf in vi/vim.
Here we type r for replace, then f as the new character which r uses to replace the original character with. This changes can to fan like this:
With a
screen editor,
you fan
scroll the page, move the cursor.
Notes
There's a lot of hidden uses to the simple commands in vi/vim that highlight more differences between these commands. Since I almost always use vim over vi, these features might be vim-exclusive, I'm not certain.
Maximizing the utility of commands like c, y, and d that take motions requires having a good grasp of text-objects (type help text-objects in vim. These aren't in vi.)
Because r takes a character instead of entering insert mode, you can input characters that would otherwise be difficult to add in. Typing r<C-R> (that's r, then ctrl-r) replaces the current character with a ctrl-r character. This might be surprising since pressing ctrl-r in insert mode awaits another key that is the register to paste.
All three of these commands can be repeated with the . command, substituting, changing, or replacing the same region relative to the cursor with the given text.
When typing a number n before the command, s and c delete n items (characters for s, or movements for c), and then inserts text once. Using a number n before r, however, replaces the next n characters with that many copies of the chosen character. For example, 4r0 could replace 1234 with 0000 all at once.
:help c
:help s
:help r
Easy.
Instead of wasting your time on that book, learn how to use Vim's awesome internal documentation:
:h s
:h :command
:h 'option'
:h function()
:h ctrl-x
:h i_ctrl-x
:h subject
:h foo<Tab>
:helpgrep foo
You may try these commands in visual block mode (press <C-v> to enter), they act a little different.
When you select a block of characters and then type s, it will immediately remove the block and enter insert mode, whatever you input next will be inserted in the same position on each of the lines affected by the visual block selection.
Command c basically does the same thing.
What interesting is command r, when you type ra after you select a block, it will replace every character in that block to a instead of just leave one column of a. I think this can be very useful at some point.

In VIM, why doesn't `db` delete the character under the cursor?

I read that b is like the reverse of e i.e. b does what e does but backwards.
So if I hit de on a word and the cursor is on the first letter of the word, it deletes the whole word. That's great!
But if I hit db on a word and the cursor is on the last letter of the word, it deletes the whole word except for the letter that the cursor was on!
I know I could just move over one character when using b but I would like to keep things consistent. And perhaps someone could enlighten me as to why b behaves like this.
b is not the opposite of e, as b is an exclusive motion, while e is inclusive. From :help e & :help b:
e Forward to the end of word [count] inclusive. Does not stop in an empty line.
b [count] words backward. exclusive motion.
And from :help exclusive:
A character motion is either inclusive or exclusive. When inclusive,
the start and end position of the motion are included in the
operation. When exclusive, the last character towards the end of the
buffer is not included. Linewise motions always include the start and
end position.
the vim cursor is a pretty disgusting thing and poorly designed, to mimic the block cursor of terminals. the cursor position is actually at the start / left hand side of the block, so if the block is "on" k in bark and you db you're actually deleting from r to b.
fortunately, you can change the cursor to something reasonable
You should have a look at :help text-objects : it allows you to apply commands to smart objects, like words, sentences, or paragraphs.
For erasing a word, no matter the position of the cursor, you should use diw.
Delete A Word daw.
Deletes the word under your cursor and the space to the left leaving the cursor at the end of the previous word. This is a repeatable change. In VIM you can repeat with .
$ | Then there was non e
daw | Then there was
. | Then there
Ended up using v to use the visual mode in backward motion.
" Backward motion, inclusive the current cursor.
onoremap b vb
onoremap F vF
onoremap T vT
This is working as intended and good enough for my typical uses. Related article:
https://jdhao.github.io/2019/05/18/nvim_exclusive_inclusive_motion/

Why does "d1j" delete two lines in vim?

The d{motion} command seems to work inconsistently:
d1j " deletes 2 lines to the bottom
d1l " deletes 1 character to the right
Is it expected behaviour?
When you start a motion and you are in operator pending mode, your motion will be either inclusive or exclusive, and either characterwise or linewise (linewise motions are always inclusive).
j is a linewise inclusive motion. Probably you want to try dvj or dgj (the latter one works with screen lines).
See :help operator. You can force motions to be linewise, characterwise or blockwise with V, v or CTRL-V respectively.
It is not actually inconsistent. I guess you were looking for
delete one line: dd (which is a command)
delete one line down: d1j (or short dj). This is an operator with a motion)
The remaining surprising bits are due to vim's notion of linewise, blockwise and characterwise motions, which are really just abstractions to allow Vim to Do The Right Thing or Do What You Expect when operating on selections.
Like #Benoit said, d is operator that takes a motion.
2j being linewise, it will move two lines down, covering 3 lines.
Note how, if you would like some visual clues with that, you can set the rn option
:se relativenumber
This will cause relative linenumbering to be shown in the left 'gutter' of the editor. These numbers can be used as 'addresses' and do what you expect when you do 'd12j' or 'd12k'
I recommend getting acquainted with the text object motions before getting used to this, by the way. Many times, text objects are much more precise (and often work in character mode, only degrading to linewise selections in appropriate conditions)

Cursor positioning when entering insert mode

When I switch to command mode in Vim, the cursor seems to move one character back when it's at the end of or on a word, and when I go to the end-of-line with $, it does not go to the actual end of line but one character before the end of the last word, and l ("el") does not move it forward and I have to use the arrow key to get there.
I haven't been able to find documentation of this behavior, but this seems strange to me. What's the reasoning behind this (for my own curiosity), and how can I get around it (or deal with it)?
it is a little more clear if you use gvim, where the cursor changes.
insert mode in gvim has the cursor as an I-beam, since the next letter you type will be inserted after the |. normal mode has the block cursor, because the next thing you type may just effect the letter that is currently highlighted (like if you use x, s, etc). So insert mode is actually adding text, but normal mode is modifying text in some way.
So in normal mode, jumping to the end of the line really just means the last character, since that is the last thing that is possible to be modified. in insert mode, the cursor goes passed the last character, since it is possible to add things afterwards.
One thing to keep in mind is that you can control which side of the block you end up on going from normal mode to insert mode
([] means that the block cursor is over that h)
Let's say you have t[h]is text
if you pressed i at this point, the cursor would look like this (in gvim)
(| being the insert mode cursor)
Let's say you have t|his text
if you pressed a instead of i, it would look like this
Let's say you have th|is text
Another thing to keep in mind (as pavanlimo mentioned), from normal mode you can go to insert mode with your cursor just before the first character of the line, or just after the last character, with shift-I or shift-A.
I'm not quite sure of the reasoning behind it, but you can work around it by pressing:
Shift + a
You might be interested in the option virtualedit and the following value:
set virtualedit=onemore
With this option you can move the cursor one character over the end of the line and then press i to insert mode after the last character.
This solves the issue in a way but personally I find this behavior a bit odd. Only in a few cases you encounter the problem so it might be worth ignoring it ;-)
That's because all commands you use affect the letter the cursor is over. If wouldn't make sense to press x (delete 1 letter) behind the actual letter.
There's actually no need to move the cursor in command mode behind the last letter, if you want to e.g. append you can press a which puts the cursor behind the letter in insertion mode.
It is implementation-dependent on whether the cursor can move past the end of the line (at least it is an option in my editor's VIM emulation mode). You can press a to enter insert mode and start editing after the current character in this situation (rather than i).
pressing i will enter the insert mode before the cursor
a after the cursor
I before the first non empty character in the line
A at the end of the line.
So, use A to quickly start typing at the end of the line.
I suggest
:set virtualedit=onemore
:inoremap <Esc> <Esc>`^

Resources