How to paste contents of a register specifying a line number? - vim

Assume a text file with 5 lines, each having a line break. This text file is opened in gvim. While in command mode and cursor in line number 1, the content of current line is yanked into default register.
How to paste/put yanked to different line number without moving the cursor? For example, cursor is in line number 1. Yanked content should be put in line number 4. How to do this in gvim without moving the cursor to line number 4?

The cursor position is an important part of command addressing; for interactive editing, it does not make sense to have commands that work "at a distance". (Inside Vimscript you can use low-level functions like setline() to modify arbitrary places, but that should not be part of normal use, and here I disagree with #SergioAraujo's answer, which presents this command as suitable for interactive use.)
Instead, Vim makes it easy to temporarily go to a location and then return back to where you came from. The :help jumplist and especially CTRL-O are for that. Your example paste you be done via 4Gp<C-O> or 4Gp``; to use Ex commands, you have to explicitly set the jump point: m':4put<CR>``. The advantage of working with the jumplist is that the paste target now also became part of it, so you can easily go forward (with <C-I>) to it, too.

Here an example:
:call setline(4, getline(4) . " " . #")
We are setting line 4 to line 4 itself plus space " "
plus default register #" .
Just In case you want just put the content
of the line 1 on the line 4 type:
:call setline(4, getline(1))

Use the :t command (a synonym for :copy). Examples:
:1t 4
:.t 4

Related

How do I re-select a range in vim? [duplicate]

Is it possible to reuse the range of ex commands in VIM?
As an example, I can write (copy) lines 4 to 10 from my current file to a new file using the following command:
:4,10w foo/bar.txt
But what I really want to do is move the lines to a new file. I can do this like so:
:4,10w foo/bar.txt
:4,10d
But it's a bit annoying to have to type 4,10 both times.
So I have two questions:
Generally, Is there a way to reference the previously used range in ex commands?
Specifically, if there is not a way to do (1), is there any easier way to cut and paste a number of lines from one file into a new one.
I usually use cat to do this:
:4,10!cat > foo/bar.txt
This works because you're piping the lines through cat and replacing them with the resulting output, which is nothing. And of course you can append to the end of an existing file by doing >> instead of >.
I am unaware of an answer to (1), but to answer (2), there are a number of different ways of doing it that don't require reselecting the lines by hand. In visual mode this will work:
4GV10G
:w foo/bar.txt
gvd
because gv reselects the previous selection, which is almost what you want, without using an ex range.
But you could just turn the problem on its head, and try:
:4,10d
:sp foo/bar.txt
pZZ
to cut, then paste into a new file, then close it.
Other than using the Vim history (:Cursor Up, q:) and removing the previous command so that just the range is kept, there's no way to re-use the last range, no magic variable.
If I used this move lines combination more often, I would write a custom command for it:
command! -bang -range -nargs=1 -complete=file MoveWrite <line1>,<line2>write<bang> <args> | <line1>,<line2>delete _
You need to specify the range only once and save typing.
You can write something like this for other combinations, too. The main challenge is specifying all the command attributes (bang, range, completion), and, later, remembering the custom command name.
Generally, what I do is delete the lines from the one file, switch to the other file, and paste.
Also, I generally use marks. Instead of typing the actual numbers, I hit mb to mark the beginning line, then go to the end line and hit d'b to delete back to the line marked as b. But you can use mb to mark a begin line, and me to mark an end line, then run an ex command:
:'b,'e w somefile.txt<Enter>
Of course you can use any letters from a through z for your marks; I usually use b and e but you can use what you like.
How I would move the lines:
m'b
<navigate to end line>
d'b
:n somefile.txt<Enter>
p
Ctrl+^
Ctrl+^ switches from the current open file to the previous open file. (You could also just open a pane and switch panes, if you prefer. Panes don't work in plain vi but do work in vim.)
The above assumes that you have set the autowrite option on. With autowrite, the :n command and Ctrl+^ both just write the current file and then switch files, instead of complaining that the file has been changed without you saving it. You can also do the above and just explicitly write the file before using :n or Ctrl+^.
By the way, I use Ctrl+^ so much that I mapped it onto K. Easier to type, but I got in that habit long ago when I used to have to sometimes use a dumb terminal that couldn't type Ctrl+^.
By the way, when you delete lines, they go into the "unnamed buffer". In vim, the unnamed buffer is preserved when you switch files. In original vi, the unnamed buffer is cleared. So the above won't work with old vi. You can make it work by deleting into a named buffer, then pasting from the named buffer; that works in any version of vi.
m'b
<navigate to end line>
"ad'b
:n somefile.txt<Enter>
"ap
Ctrl+^
The above deletes into the buffer named a, then pastes from a in the other file. This does work in vim of course; it's just that you don't need it.
Here's a command-line mapping that achieves this. I've bound it to CTRL-G CTRL-U, since it performs a similar action as CTRL-U. (But you can change that, of course!)
" c_CTRL-G_CTRL-U Remove all characters between the cursor position and
" the closest previous |:range| given to a command. When
" directly after a range, remove it.
" Useful to repeat a recalled command line with the same
" range, but a different command.
let s:singleRangeExpr = '\%(\d\+\|[.$%]\|''\S\|\\[/?&]\|/[^/]*/\|?[^?]*?\)\%([+-]\d*\)\?'
let s:rangeExpr = s:singleRangeExpr.'\%([,;]'.s:singleRangeExpr.'\)\?'
let s:upToRangeExpr = '^\%(.*\\\#<!|\)\?\s*' . s:rangeExpr . '\ze\s*\h'
" Note: I didn't take over the handling of command prefixes (:verbose, :silent,
" etc.) to avoid making this overly complex.
function! s:RemoveAllButRange()
let l:cmdlineBeforeCursor = strpart(getcmdline(), 0, getcmdpos() - 1)
let l:cmdlineAfterCursor = strpart(getcmdline(), getcmdpos() - 1)
let l:upToRange = matchstr(l:cmdlineBeforeCursor, s:upToRangeExpr)
if empty(l:upToRange)
return getcmdline()
else
call setcmdpos(strlen(l:upToRange) + 1)
return l:upToRange . l:cmdlineAfterCursor
endif
endfunction
cnoremap <C-g><C-u> <C-\>e(<SID>RemoveAllButRange())<CR>
as a plugin
My CmdlineSpecialEdits plugin has (among many others) this mapping as well.
You can also do something like this to write the contents of the anonymous register to file2.txt
:4,10d | :call writefile(split(##, "\n", 1), 'file2.txt')
You can do the deleting first, and then open a new tab and paste the contents - so :4,10d, then :tabe foo/bar.txt, followed by p... does that sound better?
In Vim 8 and NVIM 0.3.7 as of writing, you can actually edit your command list and hit enter to execute.
:4,10w foo/bar.txt
q:
q: is to enter interactive ex command
Once you open the interactive command list, you can then edit it and press enter to execute.
I love moopet's answer though, it's efficient.

How to know in VI which line the cursor is on?

I want to copy paste some lines in vi.
I have a text like
python class1 def:
code code code
...
code code code
last line class1
python class2 def:
code code code
...
code code code
I want to copy the whole class1. I was trying to do it with yNy, so I needed to get N, that is, to count the number of lines the class has.
Then I thought it would be good to get the line number of python class1 def: (let's say X) and the last line class1 (Y), calculate N=Y-X, go to the first line of the class and do the yNy. However, I could not figure out how I can get the line numbers.
So, is there any way to know which line I am in? And in general, is there any other way to copy paste a whole block like the one I indicated?
This is my vi version:
VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Oct 26 2012 16:44:45)
Included patches: 1-547
The current line number can be obtained by :.=. Ctrl-g gives more details including filename, column information, ...
In order to copy a block, go to the start of the line to be copied 0. Hitting v would start the visual mode. Navigate to the last line to be copied. Yank y. (Visual selection is now in buffer.)
Using only normal mode commands:
You can do y} to yank everything from the current line to and including the next empty line, delimiting what Vim considers to be a "paragraph". This may or may not work depending on your coding style.
Still using the notion of "paragraph", you can do yip or yap from anywhere in a "paragraph".
You can set the number option which allows you to see absolute line numbers and therefore be able to do y10G, "yank everything from here to line 10".
You can set the relativenumber option which allows you to see relative line numbers and therefore be able to do y5j, "yank everything from here to 5 lines below".
You can do V/foo<CR>y to yank everything from here to foo linewise.
More generally, you can simply use visual mode to select what you want and yank it.
You can also set a mark on the first line of the class with ma, move the cursor to its last line and do y'a (which sounds like the name of a Lovecraftian deity).
Using Ex commands:
Because the aforementioned number option shows absolute line numbers, you can see that the class ends at line 10 and do :.,10y.
Because the aforementioned relativenumber option shows relative line numbers, you can see that the class ends 5 line below and do :,+5y (dropping the implied .).
Using your statusline (or not):
You can :set ruler to have the current line number displayed on the right side of your statusbar if you have one or on the right side of your command line if you don't have a statusline.
Using Vimscript:
You can use line('.') to retrieve the number of the current line.
Using custom text-objects:
There are a number of custom text-objects available on vim.org for indented blocks, function arguments and many other things. Maybe there is one for Python classes.
More generally, I'd advise you to set either ruler, number or relativenumber permanently in your ~/.vimrc and get used to it.
ruler is the least invasive of the bunch but it's also the most limited: you know where you are but it doesn't help at all when you want to define a target.
number is the most classical and can be used to easily target a specific line.
relativenumber is a bit weird at first and, like number, can be used easily to target a specific line.
Choosing number or relativenumber is, as far as I'm concerned, a matter of taste. I find relativenumber very intuitive, YMMV.
Try the following in command mode
:.= returns line number of current line at bottom of screen
yNy or Nyy copies the next N lines, including the current line
p pastes the copied text after the current line
Additionally,
:set nu! in command mode will turn on/off the line number at the beginning of each line.
let the vim registers do this task. why bother calculating lines
for example if you want to copy line X to line y
1) move your cursor to 1st character of line X.
2) type "ma" . this will save current cursor position in register "a".
3) move cursor to last char of line Y.
4) type "y`a". copy is done
5) p pastes the copied text
This method can work not only lines but block ,words even on characters.

Reusing the previous range in ex commands in VIM

Is it possible to reuse the range of ex commands in VIM?
As an example, I can write (copy) lines 4 to 10 from my current file to a new file using the following command:
:4,10w foo/bar.txt
But what I really want to do is move the lines to a new file. I can do this like so:
:4,10w foo/bar.txt
:4,10d
But it's a bit annoying to have to type 4,10 both times.
So I have two questions:
Generally, Is there a way to reference the previously used range in ex commands?
Specifically, if there is not a way to do (1), is there any easier way to cut and paste a number of lines from one file into a new one.
I usually use cat to do this:
:4,10!cat > foo/bar.txt
This works because you're piping the lines through cat and replacing them with the resulting output, which is nothing. And of course you can append to the end of an existing file by doing >> instead of >.
I am unaware of an answer to (1), but to answer (2), there are a number of different ways of doing it that don't require reselecting the lines by hand. In visual mode this will work:
4GV10G
:w foo/bar.txt
gvd
because gv reselects the previous selection, which is almost what you want, without using an ex range.
But you could just turn the problem on its head, and try:
:4,10d
:sp foo/bar.txt
pZZ
to cut, then paste into a new file, then close it.
Other than using the Vim history (:Cursor Up, q:) and removing the previous command so that just the range is kept, there's no way to re-use the last range, no magic variable.
If I used this move lines combination more often, I would write a custom command for it:
command! -bang -range -nargs=1 -complete=file MoveWrite <line1>,<line2>write<bang> <args> | <line1>,<line2>delete _
You need to specify the range only once and save typing.
You can write something like this for other combinations, too. The main challenge is specifying all the command attributes (bang, range, completion), and, later, remembering the custom command name.
Generally, what I do is delete the lines from the one file, switch to the other file, and paste.
Also, I generally use marks. Instead of typing the actual numbers, I hit mb to mark the beginning line, then go to the end line and hit d'b to delete back to the line marked as b. But you can use mb to mark a begin line, and me to mark an end line, then run an ex command:
:'b,'e w somefile.txt<Enter>
Of course you can use any letters from a through z for your marks; I usually use b and e but you can use what you like.
How I would move the lines:
m'b
<navigate to end line>
d'b
:n somefile.txt<Enter>
p
Ctrl+^
Ctrl+^ switches from the current open file to the previous open file. (You could also just open a pane and switch panes, if you prefer. Panes don't work in plain vi but do work in vim.)
The above assumes that you have set the autowrite option on. With autowrite, the :n command and Ctrl+^ both just write the current file and then switch files, instead of complaining that the file has been changed without you saving it. You can also do the above and just explicitly write the file before using :n or Ctrl+^.
By the way, I use Ctrl+^ so much that I mapped it onto K. Easier to type, but I got in that habit long ago when I used to have to sometimes use a dumb terminal that couldn't type Ctrl+^.
By the way, when you delete lines, they go into the "unnamed buffer". In vim, the unnamed buffer is preserved when you switch files. In original vi, the unnamed buffer is cleared. So the above won't work with old vi. You can make it work by deleting into a named buffer, then pasting from the named buffer; that works in any version of vi.
m'b
<navigate to end line>
"ad'b
:n somefile.txt<Enter>
"ap
Ctrl+^
The above deletes into the buffer named a, then pastes from a in the other file. This does work in vim of course; it's just that you don't need it.
Here's a command-line mapping that achieves this. I've bound it to CTRL-G CTRL-U, since it performs a similar action as CTRL-U. (But you can change that, of course!)
" c_CTRL-G_CTRL-U Remove all characters between the cursor position and
" the closest previous |:range| given to a command. When
" directly after a range, remove it.
" Useful to repeat a recalled command line with the same
" range, but a different command.
let s:singleRangeExpr = '\%(\d\+\|[.$%]\|''\S\|\\[/?&]\|/[^/]*/\|?[^?]*?\)\%([+-]\d*\)\?'
let s:rangeExpr = s:singleRangeExpr.'\%([,;]'.s:singleRangeExpr.'\)\?'
let s:upToRangeExpr = '^\%(.*\\\#<!|\)\?\s*' . s:rangeExpr . '\ze\s*\h'
" Note: I didn't take over the handling of command prefixes (:verbose, :silent,
" etc.) to avoid making this overly complex.
function! s:RemoveAllButRange()
let l:cmdlineBeforeCursor = strpart(getcmdline(), 0, getcmdpos() - 1)
let l:cmdlineAfterCursor = strpart(getcmdline(), getcmdpos() - 1)
let l:upToRange = matchstr(l:cmdlineBeforeCursor, s:upToRangeExpr)
if empty(l:upToRange)
return getcmdline()
else
call setcmdpos(strlen(l:upToRange) + 1)
return l:upToRange . l:cmdlineAfterCursor
endif
endfunction
cnoremap <C-g><C-u> <C-\>e(<SID>RemoveAllButRange())<CR>
as a plugin
My CmdlineSpecialEdits plugin has (among many others) this mapping as well.
You can also do something like this to write the contents of the anonymous register to file2.txt
:4,10d | :call writefile(split(##, "\n", 1), 'file2.txt')
You can do the deleting first, and then open a new tab and paste the contents - so :4,10d, then :tabe foo/bar.txt, followed by p... does that sound better?
In Vim 8 and NVIM 0.3.7 as of writing, you can actually edit your command list and hit enter to execute.
:4,10w foo/bar.txt
q:
q: is to enter interactive ex command
Once you open the interactive command list, you can then edit it and press enter to execute.
I love moopet's answer though, it's efficient.

Vim: insert text from a file at current cursor position

To insert text from a file in the current Vim buffer I use :r filename to insert the text below the cursor or :0r filename to insert in the first line.
How do you insert the contents of a file where [Cursor] is located?
Actual line with some coding [Cursor] // TODO for later version
Line below actual line ...
This inserts the contents of the file whose path is at the cursor position:
:r <cfile>
Insert a line break, read the file, and then take out the line break...
I propose Ctrl-R Ctrl-O = join(readfile('filename','b'), "\n")
Other solution:
Possibly open the other file in another window, use :%yh (h is a register name) and in your original file, in normal mode use "hp or "hP or in insert mode, Ctrl-R Ctrl-O h
To expand on the accepted answer with actual code, since I tried the suggestion and it worked nicely;
Here is an example of it working to insert a little php snippet:
`nnoremap <leader>php a<CR><ESC>:.-1read $SNIPPETS/php<CR>I<BS><ESC>j0i<BS><ESC>l`
In general the syntax is
`nnoremap [KEY SEQUENCE] a<CR><ESC>:.-1read [FILE PATH]<CR>I<BS><ESC>j0i<BS><ESC>l`
Just to break it down:
nnoremap : I'm about to define a new key mapping for normal mode
<leader>php : I would like to trigger the command sequence when this key combination is pressed. In my case <leader> is , so I type ,php to trigger the command.
Now for the command, bit by bit:
a<CR><ESC> : go into insert mode (after the cursor), insert a line break, go back into normal mode.
:.-1read <file><CR> : this enters the desired file on the line above the current line.
I<BS><ESC> : jump to the start of the line, delete line break, return to normal mode.
j0i<BS><ESC>l : Go down one line (to the remainder of the line that you were initially on), jump to the start, enter insert mode, delete the line break, return to normal mode.
l : position the cursor to the right of the pasted file (this just made the command work more like how you expect it to).
note
You have a choice of whether to paste before or after the cursor. I have chosen in this example to paste after the cursor because that is how the p command usually works to paste yanked text. Alternately, to paste before the cursor you should change the a at the start of the command to an i. You could use one of these exclusively, or you could bind them both to different but related key sequences. For example:
`nnoremap <leader>php i<CR><ESC>:.-1read $SNIPPETS/php<CR>I<BS><ESC>j0i<BS><ESC>l`
Could paste text before the cursor,
and :
`nnoremap <leader><leader>php a<CR><ESC>:.-1read $SNIPPETS/php<CR>I<BS><ESC>j0i<BS><ESC>l`
could paste text after the cursor. Or vice versa. I have made 'before the cursor' easier to trigger because I use it more often when pasting in-line.
other note
This solution is mainly useful if you're reading from a file that you expect to use often enough that it's worthwhile setting up a key mapping for it (ie reading a snippet from a file). This doesn't really help if you just want to read in a random files whenever since you won't have the key mapping ready.
Since it is very formulaic, I'm sure it would be possible to define a function that would take the file name as an argument and do the same thing though. I've never written a function in vimscript though so if someone who knows more feels like editing this answer to contain that solution - I urge them to do so!
" put this in your ~/.vimrc
" in insert mode press ,n
"
imap ,n <c-r>=expand("%:p")<cr>
Read more in wikia.

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