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>])
Related
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.
How do you select a single line in VIM, when your cursor as at some random point along that line?
I know you can do (v, $) to get to the end of the line, or (v, ^) to get to the start, but when you do (v,$,^) it logically doesn't select the whole line, it selects from cursor, until end, then switches it to cursor until beginning... So this approach fails of course.
Capital V selects the current line in one key stroke; two, if you include the "shift" in shift+v.
V would be direct answer. However, I rarely need to do this because "selecting the current line" is generally part of a larger task. Example of such tasks includes copying the line and deleting the line. There's generally a better way to accomplish the task as a whole. The following are some of the tasks I can think of:
copy the line: yy
delete the line: dd
indent the line: >> or <<
select the current paragraph: vap or vip
delete from the current line to the end of the file 0dG
highlight the current line to see where my cursor is: use :set cursorline in .vimrc file
One case in which I do use V is to select multiple lines that are not a paragraph or some other text object. In this case, there's a tip that might be useful for you: once in the selection mode, you can use o to jump the cursor between the start and the end of the selection.
While this might be more keystrokes.
If you are already in visual mode you can use o to go to the other end of the visual selection.
So you can type
v0o$
To select the whole line. Take a look at :h visual-change
However from the comments it seems you just want to copy the whole line.
Which would just be yy
Just change your order of operations. You almost have it.
^,v,$
Or as suggested by #Kent: because ^ goes to the first non-empty char, if the line has leading spaces:
0,v,$
I know this thread is super old, but I just had the same question. This thread came up first, but I found a different answer than any found here. Use 'V' to select whole lines. That easy. One character to select the whole current line.
Suppose I'm editing the following document (* = cursor):
Lions
Tigers
Kittens
Puppies
*
Humans
What sequence can I use to delete the surrounding white space so that I'm left with:
Lions
Tigers
Kittens
Puppies
*
Humans
Note: I'm looking for an answer that handles any number of empty lines, not just this exact case.
EDIT 1: Line numbers are unknown and I only want to effect the span my cursor is in.
EDIT 2: Edited example to show I need to preserve leading whitespace on edges
Thanks
Easy. In normal mode, dipO<Esc> should do it.
Explanation:
dip on a blank line deletes it and all adjacent blank lines.
O<Esc> opens a new empty line, then goes back to normal mode.
Even more concise, cip<Esc> would roll these two steps into one, as suggested by #Lorkenpeist.
A possible solution is to use the :join command with a range:
:?.?+1,/./-1join!
Explanation:
[range]join! will join together a [range] of lines. The ! means with out inserting any extra space.
The starting point is to search backwards to the first character then down 1 line, ?.?+1
As the 1 in +1 can be assumed this can be abbreviated ?.?+
The ending point is to search forwards to the next character then up 1 line, /./-1
Same as before the 1 can be assumed so, /./-
As we are using the same pattern only searching forward the pattern can be omitted. //-
The command :join can be shorted to just :j
Final shortened command:
:?.?+,//-j!
Here are some related commands that might be handy:
1) to delete all empty lines:
:g/^$/d
:v/./d
2) Squeeze all empty lines into just 1 empty line:
:v/./,//-j
For more help see:
:h :j
:h [range]
:h :g
:h :v
Short Answer: ()V)kc<esc>
In normal mode, if you type () your cursor will move to the first blank line. ( moves the cursor to the beginning of the previous block of non-blank lines, and ) moves the cursor to the end (specifically, to the first blank line after said block). Then a simple d) will delete all text until the beginning of the next non-blank line. So the complete sequence is ()d).
EDIT: You're right, that deletes the whitespace at the beginning of the next non-blank line. Instead of d) try V)kd. V puts you in visual line mode, ) jumps to the first non-blank line (skipping the whitespace at the beginning of the line), k moves the cursor up one line. At this point you've selected all the blank lines, so d deletes the selection.
Finally, type O (capital O) followed by escape to crate a new blank line to replace the ones you deleted. Alternatively, replacing dO<Escape> with c<Escape> does the same thing with one less keystroke, so the entire sequence would be ()V)kc<Esc>.
These answers are irrelevant after the updated question:
This may not be the answer you want to hear, but I would make use of ranges. Take a look at the line number for the first empty line (let's say 55 for example) and the second to last empty line (perhaps 67). Then just do :55,67d.
Or, perhaps you only want there to ever be one empty line in your whole file. In that case you can match any occurrence of one or more empty lines and replace them with one empty line.
:%s/\(^$\n\)\+/\r/
This answer works:
If you just want to use normal mode you could search for the last line with something on it. For instance,
/.<Enter>kkVNjd
I didn't test so much, but it should work for your examples. There maybe more elegant solutions.
function! DelWrapLines()
while match(getline('.'),'^\s*$')>=0
exe 'normal kJ'
endwhile
exe 'silent +|+,/./-1d|noh'
exe 'normal k'
endfunction
source it and try :call DelWrapLines()
I know this question has already been resolved, but I just found a great solution in "sed & awk, 2nd Ed." (O'Reilly) that I thought was worth sharing. It does not use vim at all, but instead uses sed. This script will replace all instances of one or more blank lines (assuming there is no whitespace in those lines) with a single blank line. On the command line:
sed '/ˆ$/{
N
/ˆ\n$/D
}' myfile
Keep in mind that sed does not actually edit the file, but instead prints the edited lines to standard output. You can redirect this input to a file:
sed '/ˆ$/{
N
/ˆ\n$/D
}' myfile > tempfile
Be careful though, if you try to write it directly to myfile, it will just delete the entire contents of the file, which is clearly not what you want! After you write the output to tempfile, you can just mv tempfile myfile and tada! All instances of multiple blank lines are replaced by a single blank line.
Even better:
cat -s myfile > temp
mv temp myfile
cat is awesome, yes?
Bestest:
If you want to do it inside vim, you can replace all instances of multiple blank lines with a single blank line by using vim's handy feature of executing shell commands on a range of lines within vim.
:%!cat -s
That's all it takes, and your entire file is reformatted all nice!
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.
In vim I filter, say the current single line, using !! through a Unix command. To achieve this I defined the following shortcut in .vimrc
:map <Enter> !!mycommand<CR>:,+1<CR>
Pressing <Enter> this takes me to the line below the current if mycommand replaces my single input line be exactly one output line. If the output has more lines (number of lines unknown before command execution) it will still take me to the line below the current.
Now, I would like to know how I can always get to the first line below the inserted output of mycommand.
The modified shortcut would then allow me to 'execute' the text file line by line using just <Enter> displaying the output each time.
If there is no way to do this without any previous knowledge of the output of mycommand, maybe there is one knowing say the first character of each output line.
Thanks a lot!
You can use the special marks '[ and '], which mark the start and end of the last changed (or yanked) text. Change your map to:
:map <Enter> !!mycommand<CR>']+
Note that I'm using + in place of your ex command. This will jump to the first non-blank character in next line. If that's not what you want, you may try simply j or, use a shorter version of your original map:
:map <Enter> !!mycommand<CR>']:+1<CR>
You don't really need the comma, to make this a range. This command is just a simplified :#, where # is a line number to jump. Here you can use . meaning "current line", and then :.+1 moves to the next line. But you can omit the dot, and that's why :+1 does the same.