Best Vim alternative to VSCode's Ctrl+D - vim

Lets say we are editing this totally made up JSON file:
[{
"id_4f7xg4egb": "<some_random_guid",
// ... other fields
}, {
"id_h34k3": "<another_id>",
// ... different fields than prev object potentially
"nested": {
"id_j3h": "<nested_obj_id>",
// ... nested obj fields
}
},
// ...
]
It contains N objects (including the nested ones), and we'd like to replace the id value with the string coming appended to the field key itself. The result would be something like:
[{
"id": "4f7xg4egb",
// ... other fields
}, {
"id": "h34k3",
// ... different fields than prev object potentially
"nested": {
"id": "j3h",
// ... nested obj fields
}
},
// ...
]
Now, here is what I would do in VSCode using multiple cursors:
With VSCode Ctrl+d
Select first occurrence of "id_ with Shift+→x4
Select (and create a cursor on) every occurrence of the remaining N-1 "id_ with Ctrl+dx(N-1)
Select the id string(s) following "id_ with →,← (to deselect), Ctrl+Shift+→
Cut them with Ctrl+x (yes, each cursor gets its own "clipboard")
Delete the _ with Backspace
Select the portion we want to replace ("<every_diff_id>") with →x4,Shift+End, ←x2
Replace the value with Ctrl+v and Esc to get rid of the extra cursors
So we are talking about 5+N+5+2+1+8+3=N+24=o(N) keystrokes (taking into account the fact that, for example, the Ctrl key is counted only once and then held down for the rest of the N-1 Ctrl+d commands).
With the Power of Vim
And... my question is: How to accomplish the same result using Vim (in =< # of operations, ofc)?! I'm a noob, one week old vimmer, and I'm loving it so far! I've been using . and basic macros for similar tasks, but I'm not sure what would be the most efficient way to tackle this one :(. I'd also prefer a solution NOT using plugins or involving adding some complicated mappings/functions to my .vimrc. After all, the VSCode solution is vanilla VSCode ;).
Thanks!

I would do it in two intuitive passes and not worry about counting keystrokes or beating the wall clock.
First pass:
:g/id/norm f_s": "
Use :global to execute a command on every line matching a pattern.
The pattern is id.
The command is :normal followed by a normal mode macro.
The macro…
moves the cursor to the first _ on the line with f_,
and substitutes it with ": ".
See :help :global, :help :normal, :help f, and :help s.
Second pass:
:g//norm f:;C,
Use :global to execute a command on every line matching a pattern.
The pattern is the same so it can be omitted.
The command is :normal followed by a normal mode macro.
The macro…
moves the cursor to the first : on the line with f:,
then repeats the move to get to the second : with ;,
replaces the rest of the line with a ,.
See :help ; and :help C.

By recording a macro and replaying it N times.
For example:
qa/"id_/e<CR>s": "<Esc>f:dt,q2#a
That's a total of 25 keystrokes.
Breaking it down:
qa: Start recording macro #a.
/"id_/e: Search for pattern id_ and place cursor at the end of the pattern. The <CR> executes the search.
s: Replace the character under the cursor (the _), starting insert mode. Then use ": " as the replacement text (produces "id": "... and the <Esc> leaves insert mode.
f: Move forward to the : after the field. dt,: Delete until the comma (preserving it.)
q: Stop recording the macro.
2#a: Execute macro #a two times. (If there were more id's to replace, execute it more times. Use 999#a to execute up to 999 times, or until there's an error, whatever comes first.)
I agree that multiple cursors are pretty visual and might be easier to grasp... Vim macros can be somewhat abstract and require you to think of what these operations would do to the other positions you apply the macro to...
For this particular situation, perhaps using a :s command would be easier. But that's a good thing of Vim, there are often multiple ways to solve a problem and you can choose the one you prefer.

I would probably just do something like this:
:% s/"id_\([^"]*\)": .*,/"id": "\1"/
That's a global search-and-replace, matching the "id_<something>": <stuff>, and replacing it with "id": "<something>". I think of that as a single "operation", but not sure if that's what you were looking for.

Using global command becomes:
:g/"id/exe 'norm! f_xdt"f<vi"p'
where we find "id
normal mode
f_ .................... jump to underscore
x ...................... cut
dt" .................... deletes until " (goes to unnamed register)
f< .................... jumps to <
vi" .................... visual select inner "
p ...................... paste default register
I have posted this solution but I think filbranden's is better.

Related

Vim: copy a word any number of words away

Just supposing the cursor is at the underscore '_':
line one function Abracadabrabrabra() = {
line two stuff _
line three more longWordIdoNotWantToType
}
And I want to insert Abracadabrabrabra, or maybe longWordIdoNotWantToType, or both, is there some way I can do it like this: execute a magic key command, and then press forward or backwards to cycle through the words that are nearby until I get the word I'm interested in, then press enter to insert that word.
Any other techniques that may be just as fast and flexible would also be appreciated.
Yes, of course, type the two or three first characters and press <C-x><C-n> (completion from the current buffer) or <C-n> (completion from many sources).
See :help ins-completion.

vim: replace block

Let's say I have this text:
something "something else"
something here "just another quoted block"
I want to substitute "something else" with "just another quoted block", so I do:
/quot<enter> (to jump to second quoted block searching for the string "quot")
yi" (to yank inner text for current quoted block)
?else<enter> (to jump back to the first quoted block wich contains "else")
vi" (to visually select the quoted block)
p (to paste yanked text)
This works, but I would like to know if the two last steps can be replaced by a single one, to avoid visual mode. I know it's not a huge gain keystroke-wise, but I think that the Vim philosophy would encourage what I'm trying to do, and every time I do this my mind keeps asking for this command. :-P
What I tried so far:
r (replace) replaces just one character
c (change) throws me into Insert mode and does not let me paste the text.
"_di"P
Delete inside quotes to the blackhole register; paste last yanked before cursor.
Or
ci"<Ctrl-R>0<ESC>
Change inside quotes to retrieve last yank; leave insert mode.
With my ReplaceWithRegister plugin, the last two steps would be gri". It also offers grr (replace current / [count] lines); though it only saves a little typing, I find this indispensable.
Key stroke wise, j$yi"k then vi"p is actually probably the fastest. However, if you absolutely must go into insert mode you can j$yi"k then "_ci"<C-r>" or ci"<C-r>0. The :help i_CTRL-R operator allows you to put the contents of a register into insert mode.
I usually try to keep it simple, using what I feel is more intuitive with every day commands:
j
yi"
k
ci"
<ESC>
p

How to delete a paragraph as quickly as possible

I want to delete the following codes in my config file:
server {
listen 80;
server_name xxx;
location / {
try_files xx;
}
}
I know I can use 7dd, but this is not handy enough- if the section is too long, counting the rows would be inconvenient.
Is there a better way to do this?
Sometimes, I have to delete a whole function, any ideas for that?
As in common in Vim, there are a bunch of ways!
Note that the first two solutions depend on an absence of blank lines within the block.
If your cursor is on the server line, try d}. It will delete everything to the next block.
Within the entry itself, dap will delete the 'paragraph'.
You can delete a curly brack block with da}. (If you like this syntax, I recommend Tim Pope's fantastic surround.vim, which adds more features with a similar feel).
You could also try using regular expressions to delete until the next far left-indented closing curly brace: d/^}Enter
]] and [[ move to the next/previous first-column curly brace (equivalent to using / and ? with that regex I mentioned above. Combine with the d motion, and you acheive the same effect.
If you're below a block, you can also make use of the handy 'offset' feature of a Vim search. d?^{?-1 will delete backwards to one line before the first occurrence of a first-column opening curly brace. This command's a bit tricky to type. Maybe you could make a <leader> shortcut out of it.
Note that much of this answer is taken from previous answer I gave on a similar topic.
If there is a blank line immediately after these lines, Vim will identify it as a paragraph, so you can simply use d} to delete it (assuming the cursor is on the first line).
Try using visual mode with %.
For example, say your cursor is at the beginning of the "server" line.
Press v to go into visual mode. Press % to highlight to the end of the block. Press d to delete it.
As David said their are many ways. Here are a few that I like:
Vf{%d This assumes you are on the line w/ server and you do a visual line selection, find the { and then find the matching } via the % command.
set relativenumber or set rnu for short. This turns on line numbering relative to your cursor. So you do not have to count the lines just look and 7dd away to deleting your block.
Use VaB to visually select a block i.e. { to }, line-wise. While still in visual mode continue doing aB until the proper block is selected then execute d. This means you can select a block from inside the block instead of at the start or end.
d} will delete a paragraph. This works in your current scenario because there is no blank line inside the block. If there is one then all bets are off. Although you can continue pressing . until the block is properly deleted.
If inside a block you can jump to the current block via [{ then continue executing [{ until you are at the correct block then V%d or d%dd to delete the block
Using the techniques above you can delete a function as well, but you can also use the [M and friends ([m, ]m, ]M) to jump to the start and endings of methods but they commonly work for functions as well.
For more information
:h %
:h 'rnu'
:h aB
:h }
:h .
:h [{
:h [m
Given matching parens, or braces, you can use % with d to delete, spanning lines.
So, assuming that you're on the server { line, you can do d%. This is generally useful for all code blocks, e.g. function blocks, loop blocks, try blocks.
Similarly, dip works, but d} is shorter. However, both will only delete to the next empty line.

How do I get fine-grained undo in Vim

I find Vim's undo to be a bit too coarse. E.g. if I type something like this:
a // to go into edit mode
to be or not to ve
<esc> // to exit insert mode
Oops! I made a typo. I want to start undoing so I press u, but then it clears the whole line. Is there a way to undo word-by-word or character-by-character?
You can break undos via :help i_ctrl-g_u. You can map this if you want for every character, but that might a little bit complicated. Mapping this to space button is a way.
:inoremap <Space> <Space><C-g>u
After that every word can be undo via u
So as you see from the others what you are asking for doesn't exist in Vi (AFAIK).
Undo undoes what your last action was. If your last action was to enter insert mode and then add a line and then exit insert mode. That will be undone, however if from the default mode you hit the "x" key then you will delete 1 character or if in visual mode with text selected the text will be deleted. If you hit undo then you will restore that one character or the text that was selected.
...You should think of this as an action, and actions can be atomically undone or restored
As mentioned previously if you wish to delete the previous word then you should be able to hit Ctrl + w and delete the previous word while remaining in insert mode.
If you exit insert mode you can navigate (motion) back a word with "b" forward a word with "w" to the end of a word with "e", and can cut (which leaves you in insert mode) with "c" or delete with "d". Both actions cut and delete can accept a motion following them so you can delete the current word / up to the next word with "dw" or cut the previous word with "cb"
This concept becomes more useful when you remember to use the "." command (in normal mode). This command is to repeat the last action. I have used this many times to find and replace a small group of words in a file (It is especially useful if you are paranoid about changing too much). The scenario would be the following:
File:
This is my post
really it is a test
this is the middle
This is the end
if I wanted to replace "is" with "was" I could write:
%s/\<is\>/was/g
however if I wanted to change the first line and the third line "is" to "was" (and I didn't know their line numbers, and I wanted to see if there were any other places I wanted to change is to was I could type
"/is"
hit "n" until I reach the place I want substituted, and then hit "cw" and type "was"
I can now hit "n" until I reach another place I want substituted and hit ".", and that will replace "is" with "was" (Note: my search string didn't limit to the word "is", just the two characters "is" so "This" & "this" will match in this case)
No, it is not possible and is actually not necessary either. Vim has a million ways of dealing with that. Try cb for example. Or bC. Or brb. Or Tspace to jump back instead of b. Or ciw.
You can, of course use most of these solutions in insert mode (by pressing CTRLo first), or bind one to your favorite key combination (:help map and :help imap).
On Linux, using control-w while in input mode deletes the last 'word'.

Is there some pattern behind the many VIM commands?

I have to add a VIM personality to an IDE. I never used VIM for more than the most basic edits and i'm now overwhelmed by the complexity of the command structure.
Is there any overall structure for the combination of counts moves and insert/delete commands?
I just can't see the wood for the trees.
Well, there is obviously a finger position pattern behind h, j, k, l.
The fact that ^ goes to the beginning of a line and $ goes to the end is patterned on common regular expression syntax.
Ctrl-F and Ctrl-B page forward and back, and that's fairly intuitive.
i inserts (before) and a appends (after the cursor). Similarly,
I inserts at the beginning of the line, and A appends at the very end.
> and < indent and outdent, respectively. That's also kind of intuitive.
But on the whole, many of the other commands are on whatever keys were left – it's hard to find an intuitive mapping between the letters of the alphabet and an editor's commands.
Repeat counts are always entered before a command, and mostly repeat the command that many times, but in some cases do something clever but analogous.
I think the secret to not going crazy over vi is to start out with only a small handful of commands. I have a lot of colleagues who don't know to do anything other than
move the cursor around using the arrow keys (you don't have to use h, j, k, l);
insert with i, delete with Del (you don't have to use x);
delete a line with dd
get out of input mode with Esc
get out of vi with :x (exit) or q! (quit, and throw away my changes!)
Because I'm much smarter, the additional commands I know and use are:
go to the top of the file with gg, the bottom with G.
I can go to a specified line number with (line-number)G.
copy a line with y (yank), paste it with p
change a word with cw, the rest of the line with C
delete a word with dw, the rest of the line with D
I sometimes use . to repeat the last command, or u (undo) if I messed up.
When you have occasion to use other commands, you can teach them to yourself one by one as needed.
This is a good article for explaining the VIM philosophy.
I think the characteristic that better defines VIM in respect to other editors is its wide array of motion commands. The first thing to learn to fully use VIM is hitting the arrow keys as little as possible, and think at the text in terms of "blocks" like "a sentence" "a tag" "a word" "a group of brackets".
Say you have function foo($bar, $fooz) you can change the parameters by simply positioning your cursor anywhere inside the brackets and pressing ci) (mnemonic: change inner bracket). The same pattern applies to other commands: yank (y), delete (d) and so on.
I know this doesn't explain the whole "VIM philosophy" but combining normal mode commands with the vast amount of motion modifiers is what really made me see the light.
There are plenty of nice and interesting tutorials. One example is
http://blog.interlinked.org/tutorials/vim_tutorial.html
But the broad structure that most of them would give you is
There are two main modes for editing - Command mode and insert mode. You can move from insert mode to command mode using the key.
You can execute commands in the command mode by typing a single key or a sequence of keys.
Commands can help you achieve a wide variety of things
deletion of lines - dd
yanking (copying of lines ) - yy
pasting lines below the current line - p
pasting lines above the current line - P ( and so on)
Most commands in the command mode can be pre-fixed by a "count" to indicate the number of times the command has to be executed. For example, 3dd would delete three lines.
One set of commands in the command mode lets you move to the insert mode. That is explained below.
There are different ways of entering the insert mode from the command mode. Prominent among them are (i-insert at cursor, I-insert at beginning of line, o-insert a line below, O-insert a line above, a-append, A-append at end of line.
The quick reference at
http://www.andy-roberts.net/misc/vim/vim.pdf
Will help you understand the relevance of "count"

Resources