Editing multiple lines in VIM with variable indent - vim

I need to edid multiple lines at once. I know how to do it from the begining of the line with ctrl+v - down ... - shift+i, but the problem is that my variables are of different length, so I can't do the same trick to addit the end of each variable. I can do . on each line of course, but I was wondering if there is a faster way to achevie the same result?
EXAMPLE:
I need to change:
parser.add_argument('--name', type=str, help='The name of the experiment')
parser.add_argument('--debug', default=False, action='store_true', help=f'If the run is a debugging run')
parser.add_argument('--gpu_id', type=int, default=0 if torch.cuda.device_count() > 0 else -1, help='The ID of the GPU (if there is any) to run the network on (e.g., --gpu_id 1 will run the network on GPU #1 etc.)')
to:
name =
debug =
gpu_id =
So do do this I:
Go to the first line, hit ctrl+v, down, down, down, shift+i
Then delete everithing till the last -, then esc.
Edit the end of each line I can go to the end of the first line, shift+c, space, =.
Then on each other line repeat this by f+', .
Thanks

What I like to do in cases like that is the following:
Select all lines that you wish to edit in visual line mode (Vjj)
Run a normal mode command on the selection by pressing :. That will automatically set the range to '<,'>, then type norm yi'VpxxA =.
So the whole command becomes :'<,'>norm yi'VpxxA =
That will yank the inner content of the first single quote pair in each line, replace the whole line by the yanked text, delete the first two characters and then append =.

With two simple substitutions (quicker than figuring out a single complex one):
" remove what comes before the variable name
:,+2s/.\{-}--
" substitute what comes after it with an equal sign
:'[,s/'.*/ =
See :help :range, :help :s, :help pattern-multi-items.
There are so many ways to do this…

Related

How to select the similar text in gvim and modify them?

How I can select all the text start with foo_list starting from line 4 (see. below code) and rename them with list_values or any other preferred name ? Please note, I don't want to change in the first line.
Thanks in advance !
foo_list = [5, 2, 3, 1, 4]
def reverse_list_1():
foo_list=[0,10,20,40]
for i in reversed(foo_list):
print i,
foo_list=[0,10,20,40]
print foo_list[::-1]
for i in reversed(foo_list):
print i,
length = len(foo_list)
for i in range(length):
print foo_list[length-i-1],
Preferable solution: key map in the .vimrc or .gvimrc file, don't want to use any plugin.
That's a job for :substitute. You can specify the range with explicit line numbers (here: 4 to end of buffer $, or maybe next empty line /^$/):
:4,$substitute/\<foo_list\>/list_values/g
You can also first move to the first line and use the .,$ range.
Since that's still a lot of typing, you can pull in the current word (assuming you first position the cursor on the foo_list occurrence in line 4) into the command line via <C-R><C-W>.
Or, for a plugin solution, my ChangeGlobally plugin provides a mapping that avoids the use of :s.
:%s/foo_list/list_values/gc
This command says to replace the word foo_list with list_values in the whole document, asking for confirmation each time. Then for the first occurrence of foo_list on line 1, press n (to indicate NO) , and press y (to indicate YES) for all further occurrences to replace them. This solution works when you have to replace a few words. You can read the command as follows:
In the whole document (%), substitute (s) the word foo_list with list_values and do this globally (g), asking for confirmation (c) each time. For more options in the substitute command type :help :s in vim.
Solution 2 :
When there are thousands of words to replace, you surely don't want to type a y/n confirmation each time (which is enabled by the c flag in the end in the above command).
Take your cursor to line 4 and run
:.,$s/foo_list/list_values/g
Read the above command as from here (.) to the end of file (,$)
replace (s) the word foo_list with list_values
globally (g).
For small changes like this I like to use the gn motion. The gn motion visually selects the current search pattern. This makes for a powerful search/replace method when combining the gn motion with the change, c, operator and the repeat command, ..
Basic steps:
Make foo_list your search pattern. e.g. /foo_list or via *
Use c and gn to change the first foo_list. e.g. cgnbar_list<esc>
Now repeat that change on the next search result via .
Use n to advance to the next search results. (Hit n twice to skip an occurrence)
Keep using n and . until done
There is a nice Vimcasts episode on this topic: Operating on search matches using gn
For more information see:
:h gn
:substitute is the 'correct' way, but if you're only making a few changes, and you are not very experienced with ex commands, sometimes it takes longer to think through the command than to bounce through the list of changes you want to make using motions.
If you start with your cursor on the first instance of foo_list, hit '*' to jump to the next occurrence in the file. Hit 'ce' to delete to the end of the word and enter insert mode. Type in your new variable name and return to normal mode. Now you can jump through the rest of the file using 'n' to jump to the next occurrence (or 'N' to go back), and '.' to repeat your last edit action.

delete from end of lines using block select in vim

I'm getting an unusual behavior when I try to delete from end of lines using block selection in vim.
So let's say I have a text as such:
delete this char:x
and this:x
also this:x
and then this:x
lastly this:x
If I want to append y to every line I can:
start block selection with C-v
select all the lines with 4j
go to ends of lines with $
start appending with A
type the desired text y
in order to get:
delete this char:xy
and this:xy
also this:xy
and then this:xy
lastly this:xy
but if I try to delete x in the last step instead of appending I would expect to get:
delete this char:
and this:
also this:
and then this:
lastly this:
although I end up with:
delete this char:
and this:x:
also this:x:
and then this:x:
lastly this:x:
As far as I understand it appends the last char in the first line to all other lines (in this case :) rather than deleting the missing ones (in this case x).
I can do this with macros or substitutes but I don't quite understand the rationale behind such behavior. Is there a way I can do this with block selection?
Have you tried :{range}normal? This should work:
:'<,'>normal $x
(The '<,'> bit is filled in for you when you type :.)
$ C-v 4j x
go to end of line with $
toggle visual block C-v
go down (in your case 4x) 4j
delete that stuff with x
Edit: (reacting on your comment for arbitrary indentation)
That can be done with simple macro. Macros are not so hard as you can think:
start recording a macro, we will name it 'a', so qa
go to the end of line $
delete one character x
go down by one line with j
end our macro q
Now apply our macro: 20#a - will do the same you did while you was recording the macro, 20x.
If I have a small number of lines I typically do Abackspaceesc. Then repeatedly do j. until done. Not the fastest way but easy to remember.
For a large amount of lines I typically visually select the lines via V then do a substitution or a normal command on the range.
:'<,'>s/.$//
:'<,'>norm $x
Note: you do not have to type '<,'>. It will be inserted automatically when you start a command when some text is visually selected.
The substitution command is pretty simple, match the last character (.$) and then replace it with nothing.
The normal command is just how you would delete the last character in normal mode via $x for a single line except it will apply it to each line in the range.
For more help see:
:h range
:h :s
:h :norm
as you said yourself, to achieve your goal, there are other ways, in fact better ways to go. :s or q(macro) or :g/.../norm $x. :s/.$//g is pretty straightforward.
Ctrl-V is not suitable for this job. As for its name: Visual BLOCK. You want to remove the last x, and they (only x) are not in a block.
However if you really want to stick with ctrl-v, you have to do some extra work, to make those 'x' in a block. If you have Align plugin installed, you could :
Select (V) all lines you want to do the trick,
<leader>t:
then your text looks like:
delete this char : x
and this : x
also this : x
and then this : x
lastly this : x
Ctrl-V to remove x, you should know how to do it.
then
:%s/ *:/:/g
to remove padded spaces before ':'
However I don't think it is a good way to go.

How to efficiently concatenate one set of lines with another set of lines in Vim?

Say, I have 10 consecutive lines followed by another 10 lines, e.g.:
1
2
⋮
10
a
b
⋮
j
I want to append the lines from the second range (a…j) to the lines in the first range (1…10), so that the above 20 lines turn into the following 10 lines, instead:
1a
2b
⋮
10j
Which Vim commands can I use to achieve this?
I would start going to line with a, then CTRL-V, 10j$d to blockwise delete everything.
Then :set virtualedit=all, goto first line, move cursor right by 10 characters for example, and press p. Now remove first sequence of spaces in your ten lines.
There is a second way, which is basically the same:
10dd
:call setreg('"', #", '^V') where ^V is typed with CTRL-V CTRL-V
(this will turn the register blockwise)
P
:,+10s/ //g
You can also do that programatically: enter Ex mode with Q, and type this
let i = 1
while i <= 10
call setline(i, getline(i) . getline(11))
11d
let i = i + 1
endwhile
vi
If you intend to reuse it put this into your vimrc :
function PasteLines(startline,numlines)
let i = 0
while i < a:numlines
call setline(a:startline+i, getline(a:startline+i) . getline(a:startline+a:numlines))
exec '' . (a:startline+a:numlines) . 'd'
let i = i + 1
endwhile
endfunction
And call it with :
:call PasteLines(1, 10)
where 1 is the first line, and 10 the number of lines. You need therefore 20 lines.
This would be my way:
qaG"aDdd9-$"apq
9#a
Explanation:
q # Begin recording typed characters...
a # to register 'a'
G # Set cursor in last line.
"aD # Delete the content from the beginning of line till the end and save it in register 'a'.
dd # Previous command deleted the content but left the line in blank, delete the complete line.
9- # Go back 9 lines.
$ # Set cursor at the end of current line (last number in your example).
"ap # Paste content of register 'a' (at end of line without newline character).
q # Stop recording.
---------------
9 # Run nine times.
#a # Commands of register 'a' (all previous commmands).
Register 'a' where I record commands is a different register of where I save content of each line, although they are named the same (letter 'a').
1. Assuming that the cursor is located on the first line of the
twenty-line block, let us consider the following short Ex command:
:,+9g/^/''+10m.|-j!
This is the :global command running on lines that belong to the
range of the next ten lines (starting from the current one). On every
of these lines, two Ex commands, ''+10m. and -j!, are sequentially
executed. The first command takes the tenth line under the line at
which the cursor has been positioned and inserts it just below the
line where the cursor is currently located, using the :move command.
The :join command, -j!, appends the just moved line to the one
just above it (without inserting or deleting whitespace in between,
due to the ! modifier).
There are two considerations that is necessary to take into account in
order to get the idea of that line movement. First, before the command
specified in a :global is executed on yet another line, the cursor
is positioned at the first column of that line. This way, the address
referenced in the aforementioned :move command as ., corresponds
to the latest line on which the command is currently being run.
Second, the number of the line that was the current one just before
a :global command was sent to execution, is added to the jump list.
Therefore, its address can be obtained in ranges through the
' pseudo-mark (see :help :range).
See also my answer to the question “Vim paste -d ' ' behaviour
out of the box?”.
2. The same effect can be achieved by means of Normal mode commands:
qqdd9+PgJ9-q9#q
This sequence of commands implements the same moving scheme, using a
macros to repeat a single-line transferring operation. The commands to
concatenate the first pair of lines, dd9+PgJ9-, are recorded in the
"q register using the q command. Similarly to the Ex command
proposed above, the macros deletes the current line (dd), moves the
cursor nine lines downward (9+), inserts the just cut line above the
new cursor position (P), joins that two lines without adding or
removing any spaces between them (gJ), and moves the cursor nine
lines upward (9-). Finally, these actions are automatically iterated
nine times using the # command to join the remaining nine pairs of
corresponding lines.

What is the best way to refactor a Ruby ‘if’ statement into one-line shorthand form in Vim?

I have the following Ruby code:
if some_cond && another
foo_bar
end
and I want to change it to:
foo_bar if some_cond && another
What are the most idiomatic ways to do that in Vim?
Assuming that the cursor is located at the if-line prior to
refactoring (not necessarily at the beginning of that line),
I would use the following sequence of Normal-mode commands:
ddjVpkJ<<
or this chain of Ex commands:
:m+|-j|<|+d
Here the if-line is first moved down one line by the :move + command.
The :move command cuts a given range of lines (the current line, if
not specified) and pastes it below the line addressed by the argument.
The + address is a shorthand for .+1 referring to the next line
(see :help {address}).
Second, the line containing the body of the conditional statement is
joined with the just moved if-line. The :join command concatenates
a given range of lines into a single line. The - range is a shortened
form of the .-1 address referring to the line just above the cursor
(see :help {address}).
Third, the newly joined line is unindented by one shiftwidth using
the :< command.
Finally, the remaining end-line, which can now be addressed as +,
is removed by the :delete command.
I see few (probably non-optimal) solutions:
cursor in first character in first line:
D - remove if condition but leave cursor in same position (don't delete line)
J - join next line to current
A <Space> <ESC> - append space and exit to Normal mode
p - paste if condition
and then remove remaining end with jdd
cursor in first character in first line, as previously:
j - move to next line
dd - remove this line
k - move back to if condition
P - paste removed line before actual line, cursor should be placed to pasted line
J - join next line to current
== or << - unindent current line
and then remove remaining end with jdd
another solution:
j - move to second line
JD - join line with next, remove what was joined
dd - remove current line
k - step to previous line
PJ<< - paste, join and unshift
It's probably not optimal, but I do it without thinking, because most of this commands are in my muscle memory (you don't think how to move around you, how to yank/delete and paste most of the time, and joining line is also helpful to remember).
If you have virtualedit enabled in config, instead of A <Space> <Esc> you can $ <Space>, but I find $ harder to use than A followed by Ctrl-[ (it's simmilar to ESC).
As an advice: if you use some upper letter commands, try to chain them if it's possible, so you only need to keep Shift pressed and then execute some commands, instead of mixing upper and lower letter commands and pressing two keys at a time (upper letter is 2 key press, one is Shift). Once I found combo helpful for restarting server in console Ctrl+cpj, which sends Ctrl+c, Ctrl+p (previous command) and Ctrl+j (Enter key) with single Ctrl press. Since then I try to find simmilar time-saving combination in Vim too mostly with Shift, as Ctrl is not much used in Vim.
Yet another way:
ddpkJjdd
ddp swap the two lines
kJ move up and join the lines
== re-indent the line
jdd move down and delete the last line
There are probably 30 ways to do this.
Here is one, assuming you are starting from the end of the word end in normal mode:
dd (delete last line)
"aD (delete and copy foo_bar to buffer a)
dd (delete now-empty line 2)
"aP (paste from buffer a before caret)
aSpaceEsc (insert space and return to normal mode)
Again, "properly" rarely applies in Vim because there are so many ways to accomplish something. This is so small a change that even re-typing foo_bar could be justifiable.

Prepending a character followed by the line number to every line

I'm hand-editing CNC Gcode text files and need a way to reference locations in the file and on the toolpath.
I want to modify every line in the text file so that it begins with the the upper case letter N followed by the line number, incremented in tens for each successive line, then a whitespace followed by the original text on that line. How can I do this in Vim?
I'm not sure about vi, but (since you're using the vim tag) Vim allows you to accomplish your task as follows:
Adjust the first line by hand (insert a N10 at the beginning of the line), then put the cursor at the beginning of the next line.
Press qb to start recording a macro (the b names the register used to store the macro; feel free to use a different letter -- and definitely do use a different letter if you've got something useful stashed away in b).
Move the cursor upward to the beginning of the previous line (which you have adjusted by hand). Press v to start visual selection mode, then f to move the cursor to the next space on the line (if you use a single space as your whitespace separator, that is; adjust this step if you're using a tab or multiple spaces).
Press y to yank the selected text. This will also remove the visual selection.
Move the cursor to the beginning of the next line. Press P to insert the previously yanked text before the cursor, that is, on the very beginning of the line.
Move the cursor to the numeric part of the line header. Press 10 C-a (1, 0, control + A) to increment that number by 10.
Move the cursor to the beginning of the next line. Press q to stop recording the macro.
Press 10000000 #b to execute the macro 10000000 times or until it hits the end of the file. This should be enough to take care of all the lines in your file, unless it is really huge, in which case use a bigger number.
...or use Vim to write a simple script to do the job in whichever language you like best, then run it from a terminal (or from withing Vim with something like :!./your-script-name). ;-)
The following command will prepend ‘N<line number * 10>’ to every line:
:g/^/exe 'normal! 0iN' . (line('.')*10) . ' '
You can do it easily in Vim with this:
:%s/^/\=line(".")*10 . " "/
This replaces the start of every line with the result of an expression that gives the line number times ten, followed by a space.
I have not timed it, but I suspect it might be noticeably faster than the other Vim solutions.
Cheating answer:
:%!awk '{print "N" NR "0", $0}'
There are two ways to implement that without resorting to external
tools: via a macro or by using Vimscript. In my opinion, the first way
is a little cumbersome (and probably not as effective as the solution
listed below).
The second way can be implemented like this (put the code into your
.vimrc or source it some other way):
function! NumberLines(format) range
let lfmt = (empty(a:format) ? 'N%04d' : a:format[0]) . ' %s'
for lnum in range(a:firstline, a:lastline)
call setline(lnum, printf(lfmt, lnum, getline(lnum)))
endfor
endfunction
The NumberLines function enumerates all lines of the file in a given
range and prepends to each line its number according to the provided
printf-format (N%04d, by default).
To simplify the usage of this function, it is convenient to create
a command that accepting a range of lines to process (the whole file,
by default) and a optional argument for the line number format:
command! -range=% -nargs=? NumberLines <line1>,<line2>call NumberLines([<f-args>])

Resources