How to get contents of whole screen in Vim? - vim

I need to get a copy of screen in Vim. Not a buffer, window, tab, but a whole screen, i.e. if terminal size is 80x25, then I need a string of 2000 characters. Colors do not matter (though, if it is possible, I'd be happy to have this data too). Portable solution (i.e. vimscript only, or maybe some python trick) is welcome.

The ScreenShot plugin allows you to make a copy of the current Vim view (all window splits, statuslines, etc., including syntax highlighting) as an HTML file.
You can probably "downgrade" the implementation to yield simple text, or postprocess the generated HTML into text.

I'm not sure if this is quite what you want, and I'm not sure what you want to do with the string once you have it.
But here is a function that returns the visible text from each window in a list of strings. It uses the w0 and w$ line marks to extract the visible range, yanks the range to register a, and dumps the register in the returned list. (The existing a register is saved and restored at the end.)
function! WindowStrings()
let areg = getreg('a')
let atype = getregtype('a')
let strings = []
windo exe ':' . line("w0") . ',' . line("w$") . 'yank a' |
\ call add(strings,getreg('a'))
call setreg('a',areg,atype)
return strings
endfunction

Related

NeoVim yank inside pattern

I have a code like:
$class->function('passedValue');
// some code...
$myVar = $class->function('anotherPassedValue');
// more code...
$anotherVar = $class->function('yetAnotherPassedValue');
And I want to YANK to system memory all the values $class->function('<HERE>');
So, my goal is to get this in the default system clipboard:
passedValue
anotherPassedValue
yetAnotherPassedValue
I don't want to change them in any way (delete/change/replace), just copy to default system clipboard.
The Visual Block selection won't work, as they're all over the place (not aligned).
Does Nvim (or Vim) have something native for that?
I'm using 0.8 version in terminal (not GUI).
I suppose it's possible with some regex, but I can't figure out how...
Thank you all.
Here's how to copy matches from :g to register *:
:let #*='' | g/function/let #* = #* . "\n" . getline('.')
You need to do some further adjustments:
change #* to whatever your clipboard register is
since that command copies entire lines (with one or more matches), you'd need to substitute the result of getline() or search+replace in a register.

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.

Easy motion vim plugin mapping issue

I have the vim EasyMotion plugin installed.
Pressing,
<Leader><Leader>f searches forward from the current line.
<Leader><Leader>F searches backward from the current line.
Is there a way to search the entire visible part of the buffer only using 'f'? I would ideally not want to use two different bindings for searching forwards and backward. One single binding to search the entire visible portion of the buffer would be most ideal.
You can try a mapping like this:
nnoremap <leader><leader>f :execute "/\\%>" . line('w0') . "1\\%<" . line('w$') . "l"<left>
That's a confusing syntax, so I'll unpack it.
line('w0') and line('w$') return the line numbers of the first and last visible lines in the buffer, respectively, so you use them to find the range for the visible part.
The / search command allows a range to be specified, but with an odd syntax. The format is /\%>Xl\%<Yl where X is the line to start from and Y is the line to end at.
It's not possible to just drop the results from line() into a normal / invocation, but we can build a string, using . to join segments together, and once the command is built, pass it in to :exec to make it happen.
Finally, there's the <left>. That's for cursor positioning. When you execute <leader><leader>f, the whole mapping fires as though you were typing it, so you end up with the full :exec command on the line, and it ends with a ", but you want to type inside those quotes. Alternatively, you could remove "<left> from the end of the mapping, but then you'll have to remember to close the quote after typing your search term.
I'm not familiar with EasyMotion, so this may not give you exactly what you were asking for (I realized this after I typed up the answer), but it will let you run a search in the currently visible part of a buffer only, and you can probably adapt it to EasyMotion's purposes without too much difficulty.

How to diff two lines in an open file in vim?

I occasionally see very long lines in my code that I need to check if they are the same. Is there a way in vim to select two lines and diff them to show any differences between the two?
For example, given the two lines in vim:
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(That *is, Overloaded *with, Multiple *different, Parameter *lists);
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(That *is, Overloaded *with, Multiple *different, Parameter *1ists);
I would like vim to tell me that the two lines are in fact different because each spells "lists" differently. Is this possible, and if so, how do I do it?
A quick and dirty solution is to just select both lines and sort them while removing duplicates:
select lines
":sort u"
if only one line remains, both were equal
if both remain, there most be some difference
An undo recovers everything again.
An alternative to #sehe's approach would not require the use of temp files:
funct! DiffTwoTexts(text1, text2)
new
put =a:text1
normal ggdd
diffthis
new
put =a:text2
normal ggdd
diffthis
endfunct
funct! DiffTwoLines(line1, line2)
let text1 = getline(a:line1)
let text2 = getline(a:line2)
call DiffTwoTexts(text1, text2)
endfunct
comma! DiffWithNext call DiffTwoLines('.', line('.') + 1)
This will still be pretty hard to read, since it keeps everything on a single line, so I came up with this modification:
funct! EvalTextPreprocessor(expr, text)
let text = a:text
return eval(a:expr)
endfunct
comma! -nargs=1 DiffWithNextPre call DiffTwoTexts(
\ EvalTextPreprocessor(<q-args>, getline('.')),
\ EvalTextPreprocessor(<q-args>, getline(line('.') + 1)))
This new command takes a vimscript expression as its argument, wherein the variable text refers to whichever line is being preprocessed. So you can call, e.g.
DiffWithNextPre split(text, '[(,)]\zs')
For your sample data, this gives the two buffers
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(
That *is,
Overloaded *with,
Multiple *different,
Parameter *lists)
;
and
AVeryLongReturnType* MyLongClassName:hasAnEvenLongerFunction(
That *is,
Overloaded *with,
Multiple *different,
Parameter *1ists)
;
Only the lines that start with Parameter are highlighted.
You can even build up from there, creating a command
comma! DiffTwoCFunctionSigs DiffWithNextPre split(text, '[(,)]\s*\zs')
Notice that I modified the regexp a bit so that it will keep trailing spaces at the end of lines. You could get it to ignore them entirely by moving the \s* to after the \zs. See :help /\zs if you're unfamiliar with what that vim-specific RE atom does.
A nicety would be to make the command take a range (see :help command-range), which you could use by diffing the first line of the range with the last line. So then you just visual-select from the first line to the second and call the command.
I used linediff.vim.
This plugin provides a simple command, ":Linediff", which is used to diff two separate blocks of text.
That is not a feature, however it is easily scripted, e.g. in your vimrc:
function! DiffLineWithNext()
let f1=tempname()
let f2=tempname()
exec ".write " . f1
exec ".+1write " . f2
exec "tabedit " . f1
exec "vert diffsplit " . f2
endfunction
This will open the current and next lines in vertical split in another tab.
Note that this code is a sample
it doesn't check whether next line exists (there are any following lines)
it doesn't cleanup the tempfiles created
a nice improvement would be to take a range, or use the '' mark to select the other line
You can leave off the 'vert' in order to have a horizontal split
Map it to something fancy so you don't have to :call it manually:
:nnoremap <F10> :call DiffLineWithNext()^M
you could also just create a new empty window buffer and copy line, then make command:
:windo diffthis
this should open a new window showing the differences of those 2 lines

Notepad++ like "multi editing" in Vim?

I’m switching from Notepad++ to Vim as my main text editor.
In Notepad++, you can have multiple cursors by holding down Ctrl and clicking anywhere in the text, so that if you type, the text appears in multiple locations.
Is it possible in Vim? Something like insert after selecting multiple rows in Visual mode, but with the possibility to have cursors anywhere in the text.
It’s a feature I rarely use, and it’s also quite easily avoidable; I’m just curious, since it’s the only one I could’t find a replacement for in Vim yet.
There is not a built-in feature of that kind.
Let me suggest a function that repeats command (for example . repeating last
change command) at the positions of given marks. Both marks and command are
specified as string arguments. Marks specified in the way ranges in regular
expressions or scanf-format specifier are defined. For example, za-dx
means marks z, a, b, c, d, x.
function! MarksRepeat(marks, command)
let pos = 0
let len = strlen(a:marks)
let alpha = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let beta = '1234567899bcdefghijklmnopqrstuvwxyzzBCDEFGHIJKLMNOPQRSTUVWXYZZ'
while pos < len
if a:marks[pos + 1] != '-'
exe 'norm `' . a:marks[pos] . a:command
let pos += 1
elseif a:marks[pos] <= a:marks[pos+2]
let mark = a:marks[pos]
let stop = a:marks[pos+2]
if mark =~ '[0-9a-zA-Z]' && stop =~ '[0-9a-zA-Z]'
while 1
exe 'norm `' . mark . a:command
if mark == stop
break
endif
let mark = tr(mark, alpha, beta)
endwhile
endif
let pos += 3
endif
endwhile
endfunction
In your case, the function could be used as follows.
Mark all places for simultaneous insertions (except one) using Vim
marks (by means of m command).
Actually insert text in the one place that has not been marked.
Run the function:
:call MarksRepeat(‹marks›, '.')
You could insert the text in one place, in a single operation, then use . to repeat that insertion at each other place you want the text.
It's the converse of what you asked for, because you wanted to mark the locations before entering the text, but it gives you the same result in the same number of keystrokes :).
Check multi select vim plugin: http://www.vim.org/scripts/script.php?script_id=953
ib's response and the multi select vim plugin are interesting, but the following is a suggestion that does not require a special function or plugin.
Temporarily set foldmethod=manual, then mark the blocks you want to operate on with zf.
Finally, use the ex command :folddoclosed to do ex commands on the folded blocks.
For example: :folddoclosed norm Iinsert some text at the front
Note, you can use :folddoclosed on any folded groups of lines, so you could use other foldmethods... but usually it makes sense to manually create the folds.
You can also use visual markers, followed by :norm which gives you :'<,'>norm... But visual markers only let you select a continuous range of lines. Using folds and :folddoclosed you can operate on multiple ranges of lines at once.
Another tip... to save time having to type out :folddoclosed, I will type :fo<shifttab><shifttab><shifttab>

Resources