So I have the following text (==== delimiters denotes selected text):
This is some text
I'm not interested in.
This is indented text
not important.
=====================
This text portion is
selected.
=====================
Also not important
indented text
Other text i'm not
interested in.
Now I want to create a vim function so that when called, it appends at the top and at the bottom some default text. For example I would like to end with:
This is some text
I'm not interested in.
This is indented text
not important.
THIS TEXT WAS APPENDED
AFTER FUNCTION CALL
This text portion is
selected.
THIS OTHER TEXT WAS
APPENDED AFTER THE SAME
FUNCTION CALL
Also not important
indented text
Other text i'm not
interested in.
Please note that indentation should be preserved (thanks benjifisher)
How can I do this?
This is a little sloppy, but it works:
function! Frame() range
'<put!=\"THIS OTHER TEXT WAS\nAPPENDED AFTER\nFUNCTION CALL\"
'>put=\"THIS OTHER TEXT WAS\nAPPENDED AFTER THE SAME\nFUNCTION CALL\"
endfun
Select your lines in Visual mode, and type
:'<,'>call Frame()
(The range '<,'> is inserted automatically.) There are some advantages to using :execute with a:firstline and a:lastlineas in :help function-range-example, instead of the Visual marks '< and '>, but that gets a little complicated. You could also prefix each command with :silent if you do not care to be told that 3 lines have been added (twice).
Here is a version that copies the indentation from the first and last selected lines, as requested in the updated question. This version uses :call append() instead of :put, which makes it more convenient to use a:firstline and a:lastline; possibly, this will be useful if you ever want to call the function with a range other than the Visual one.
" Add lines at the bottom before adding at the top, since the line numbers
" will change when you add lines.
function! Frame() range
" Define a list of lines and prepend the desired indentation.
let botlines = ['THIS OTHER TEXT WAS', 'APPENDED AFTER THE SAME', 'FUNCTION CALL']
let botspaces = matchstr(getline(a:lastline), '\s*')
let lines = []
for line in botlines
call add(lines, botspaces . line)
endfor
" Now add those lines at the bottom.
call append(a:lastline, lines)
" Now do the same thing at the top.
let toplines = ['THIS OTHER TEXT WAS', 'APPENDED AFTER', 'FUNCTION CALL']
let topspaces = matchstr(getline(a:firstline), '\s*')
let lines = []
for line in toplines
call add(lines, topspaces . line)
endfor
call append(a:firstline - 1, lines)
endfun
Related
I've been wanted to do this for a while, sometimes when I have a file open I want to be able to highlight certain line numbers to a different color. For example, say my LineNr is blue, and my Current LineNr is red. Say I'm on line 25, is there anyway I can change the LineNr color of lines 28-30 to green WITHOUT leaving my current line?
As a quick answer, if you don't mind highlighting only by groups of at most 8 lignes at once, you can use the matchaddpos({group}, {pos}) function and create a command to apply a highlight group to a range of lines.
command! -range -nargs=1 -complete=highlight HiLine call matchaddpos(<f-args>, range(<line1>,<line2>))
Which you can use, for example to highlight as 'cursorline' :
:28,30HiLine CursorLine
Note that completion applies on the argument for highlight groups.
To remove the group(s) of previously highlighted lines you could remove thoses that contains a particular line. I can't find a simpler way than to go through all getmatches() dicts and matchdelete({id}) the ones that contains the line on one of their 'posX' key :
function! s:RemoveMatchOnLine(line) abort
for l:match in getmatches()
let l:matchlines = values(filter(copy(l:match), 'v:key =~# ''pos\d\+'''))
if index(l:matchlines, [a:line]) >= 0
call matchdelete(l:match['id'])
endif
endfor
endfunction
command! -nargs=? LoLine call <SID>RemoveMatchOnLine(<q-args> ? <q-args> : line('.'))
Now, you can :LoLine to undo the highlighting on lines near the current line, or you can give it an argument to specify another line, so you don't have to move your cursor there : :LoLine 28.
Finally, you can set mappings :
nnoremap <leader>hi :HiLine CursorLine<CR>
xnoremap <leader>hi :HiLine CursorLine<CR>
nnoremap <leader>hc :<c-u>execute 'LoLine ' . v:count<CR>
Typing [count]<leader>hi in normal mode will highlight count lines from the cursor. And [count]<leader>hc will remove the highlighting on the group of line count.
Addendum
We could work on larger ranges using matchadd({group}, {pattern}), using \%xl to match line x. Replace call matchaddpos(... by
execute 'call matchadd(<f-args>, ''\%'.<line1>.'l\(.*\n\)\+\%'.(<line2>+1).'l'')'
and line 2 and 3 of the function by
let l:a = matchlist(get(l:match,'pattern',''), '^\\%\(\d\+\)l.*\\%\(\d\+\)l$')
if !empty(l:a) && l:a[1] <= a:line && a:line <= l:a[2]
But for me it breaks on large ranges, I would prefer to have the first solution which seems more robust.
For example, suppose I want to implement a plugin that draws a margin line at 80 colums using custom characters (suppose I want the line to made from a column of * characters).
How can that be done in Vim or Neovim?
But more generically, is there a way to draw stuff over the text buffer without affecting the text content?
For example, how can I draw an inner rectangle inside a window which I can make bright colored in order to show the active window? The effect would be that the first line visible line of what is currently a text buffer would be filled with --- characters, the right-most column of the what is currently a text buffer would be filled with |. This would be inside the window, separate from the statuslines or vertical split lines.
Etc. How to do such things?
Some plugins that currently draw over the text buffer in different ways:
https://github.com/easymotion/vim-easymotion
https://github.com/Yggdroot/indentLine
EasyMotion does not draw over the text, i don't think this is possible.
What it does is defined in following function (sourcecode):
function! EasyMotion#helper#VarReset(var, ...) "{{{
if ! exists('s:var_reset')
let s:var_reset = {}
endif
if a:0 == 0 && has_key(s:var_reset, a:var)
" Reset var to original value
" setbufvar( or bufname): '' or '%' can be used for the current buffer
call setbufvar('%', a:var, s:var_reset[a:var])
elseif a:0 == 1
" Save original value and set new var value
let new_value = a:0 == 1 ? a:1 : ''
" Store original value
let s:var_reset[a:var] = getbufvar("", a:var)
" Set new var value
call setbufvar('%', a:var, new_value)
endif
endfunction "}}}
So it saves every replaced char and restores them afterwards.
I haven't looked at indentLine but it probably does intelligent listchars, as there is never text under the indentchar.
Anyway it isn't as if I am an expert on one of the plugin or vim in general. I just write this answer because i think there are easier way to achieve what you want. You could for example highlight the border lines with a certain color, or change the background for the active split. There is also a plugin for dimming inactive splits: https://github.com/blueyed/vim-diminactive
Recently I was in need of a faster way to format similar code lines according a common character (usually =). For example, I want to format this:
myVar = getMyVar();
myLongerVar = getMyLongerVar();
myYetLongerVar = getMyYetLongerVar();
into that:
myVar = getMyVar();
myLongerVar = getMyLongerVar();
myYetLongerVar = getMyYetLongerVar();
then I wrote the following mappings:
" query used to find the common character. In this case i'm setting it to "find the ="
let g:defformatquery = "f="
" set current line as having the longer size till the common character
nnoremap <Leader>gm 0
\:execute "normal " . g:defformatquery<CR>
\:let b:epos = getpos(".")[2]<CR>
" format current line according to the position acquired above
nnoremap <Leader>g= 0
\:execute "normal " . g:defformatquery<CR>hvgeld
\:execute "normal " . (b:epos - getpos(".")[2]) . "i "<CR>
To use them I have to perform these steps (assuming , is my <Leader>):
position the cursor in the line with the longer text before the = sign (the third line in the example provided, myYetLongerVar)
press: ,gm
for each line I want to format, position the cursor there and press ,g=
Although this works, the process is kinda slow.
I want to create a function that would format the entire selected area at once. Then I could just create one map for the function.
Any ideas?
You should try the Align plugin.
For example, to align some selected lines (selected with v or CTRL-v) according to the = sign, you just type:
:Align =
Or to align from line 34 to 39:
:34,39Align =
I would like to know how to align to the right in a visual block without changing the text before and the block.
I used this code till before:
:<C-U>'<,'>s/\%V\(.\{-}\)\(\s\{-}\)\%(\%V\#!\|$\)/\2\1/
However I noted that it doesn't work when after the visual block is only spaces.
(There has to be text after the visual block in order to make above code work)
Is there no way to align text to the right in a visual block whatever is written after the block?
Example:
text before +align text text after
text before align text text after
text before align text text after
text before align text+ text after
What I want to do is select a block of text from + to + (see example above)
and align it to the right. Output must be:
text before align text text after
text before align text text after
text before align text text after
text before align text text after
Above code does the job but it does not work when there is not something written after align text in every line.
In order to solve the issue correctly handling all corner cases, I would use
the following function.
function! RightAlignVisual() range
let lim = [virtcol("'<"), virtcol("'>")]
let [l, r] = [min(lim), max(lim)]
exe "'<,'>" 's/\%'.l.'v.*\%<'.(r+1).'v./\=StrPadLeft(submatch(0),r-l+1)'
endfunction
function! StrPadLeft(s, w)
let s = substitute(a:s, '^\s\+\|\s\+$', '', 'g')
return repeat(' ', a:w - strwidth(s)) . s
endfunction
I use this. Put the cursor before the text which should be aligned to right. Run :Right
How does it work:
v0 Visualy select the text from current position to the beginning of the line
d Delete selected text and put into the buffer
:right Align the text on the right side of the cursor
0 Put cursor to the first column
gv Visualy select same area which we deleted before
p Replace selection with deleted text
command! Right execute "normal v0d\<CR>:right\<CR>0gvp"
:'<,'>s/\%V.*\%V/\=printf("%*s", col("'>")-col("'<"), substitute(submatch(0), '^\s*\|\s*$', '', 'g'))
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
(Vim)diff two subroutines in same file
Sometimes I see a block of code I suspect to be identical to another block in the same file, but it's a bit too long for visual inspection and I may just be missing something. I've tried to visually select the block and yank to the the default register, put that register into / (find), but it didn't match even the original block.
Is there a way to select a section, yank it in a register, select another section then diff the two, without creating a bunch of new files? I imagine the diff results opening in a new buffer in a tab or split.
EDIT: My question is basically a duplicate of This one. I found this answer to be the most helpful & closest to what I was looking for. The only thing I'd change is to make it output in Unified format so it looks like the diff output I'm used to (it has more info as well). I suppose this means using a different diff utility.
Inspired from my lh#path#strip_common() function:
echo matchstr(#a.'##'.#b, '^\zs\(.*\)\ze.\{-}##\1.*$')
will show what is common between registers #a and #b.
You can show more information with:
function ShowDiff(a,b)
" I expect neither string to contain '##'
let start = matchstr(a:a.'##'.a:b, '^\zs\(.*\)\ze.\{-}##\1.*$')
let end= matchstr(a:a.'##'.a:b, '^.\{-}\zs\(.*\)\ze##.\{-}\1$')
let a = a:a[len(start): -len(end)-1]
let b = a:b[len(start): -len(end)-1]
echo "identical beginning: ".strlen(start )." chars -> ".start
echo "identical ending : ".strlen(end)." chars -> ".end
echo "typical to a : ".strlen(a)." chars -> ".a
echo "typical to b : ".strlen(b)." chars -> ".b
endfunction
Used with:
:call ShowDiff(#a, #b)
You could use the following sequence assuming that the two segments are already in registers, 'a and 'b. Could probably be put into a macro or function.
new
only
put a
diffthis
vnew
put b
diffthis
This creates a new buffer, makes it the only visible buffer, puts 'a into it, sets it up to be diff'd, then opens a new buffer in a vertical split, puts 'b into this split empty buffer and also sets it up to diff. Immediately vim (or gvim) will show the differences.
When done, type :ls to get the list of buffers, use :buffer *N* to return back to the original file and use :bdel! *N* to delete the created buffers (named "[No Name]").
Here's a function to open two new windows side by side, each containing the specified register contents (called as DiffRegs(#a, #1), for instance) and diff them. The new buffers will not be written or modifiable:
" A list for bookkeeping..
let g:diffreg_buffers = []
function! DiffRegs(reg1, reg2)
" Preserve the unnamed register
let s:nonamereg = ##
let ## = a:reg1
" new window
:new
normal P
setlocal nomodifiable
setlocal buftype=nofile
diffthis
call add(g:diffreg_buffers, bufnr('%'))
let ## = a:reg2
:vsp +enew
normal P
setlocal nomodifiable
setlocal buftype=nofile
diffthis
call add(g:diffreg_buffers, bufnr('%'))
let ## = s:nonamereg
endfunction " DiffRegs(reg1, reg2)
" Function to wipe all buffers we're diffing with the function above
function! EndDiffs()
for buffer in g:diffreg_buffers
exe ':buffer ' . buffer
diffoff
quit
endfor
let g:diffreg_buffers = []
endfunction " EndDiffs()
You can bind those to key combinations of your choice, but if you don't call EndDiffs() after each call to DiffRegs(), you'll run into issues.
To compare quickly two different parts of a file, you can split the view in two by using:
:sp horizontal split
or
:vsp vertical split
Once you have splitted the screen, you must use :diffthis in each window to hightlight the differences. (Then :diffoff to leave diff mode)
Then to go back to a single window you can quit one of them with :q or use CTRLwo