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 =
Related
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
I'm trying to add an indicator in my statusline for the total length of the line (not just the cursor column position, which can be shown with %c). How do I do this?
To get the contents of a line as a string, use getline(<line number>).
To get the contents of the current line as a string, you can use getline(".").
To get the length of a string, you can use strlen(<string>).
Putting it all together, we get strlen(getline(".")). To add it to your statusline, simply:
statusline += "%{strwidth(getline('.'))}"
or for vim-airline (what I use)
" can be any section; this is for section z (right hand side)
let g:airline_section_z = "%{strlen(getline('.'))}"
I often use gq$ to wrap a line in Vim.
For example, if I have set textwidth=80 as my only line in .vimrc, then
option1, option2, option3, option4, option5, option6, option7, option8, option9, option10, option11
wraps to
option1, option2, option3, option4, option5, option6, option7, option8, option9,
option10, option11
However, if I want to wrap a comma-delimited list (without spaces), this command does not work, because Vim considers this line as a single word:
option1,option2,option3,option4,option5,option6,option7,option8,option9,option10,option11
Whereas desired output is:
option1,option2,option3,option4,option5,option6,option7,option8,option9,
option10,option11
How can I allow Vim to wrap by splitting a line on commas? I didn't see anything immediately in :help fo-table that is relevant to my case.
One way to do it is to use Par. It's the best program for reflowing text hands down, but you have to really like abstractions to digest the manual. My cheat sheet for it:
set the environment variable PARINIT:
export PARINIT='grTbEiq B=.,!?_A_a Q=_s>:|'
in my vimrc:
set equalprg=par\ s0\ 72
set formatprg=par\ s0\ 72
function! s:FormatPar()
let old_format = &formatprg
let textwidth = &textwidth > 0 ? &textwidth : 72
let &formatprg = 'par s0 ' . textwidth . (v:count > 0 ? 'h1p' . v:count : '')
normal }{gq}
normal }
let &formatprg = old_format
endfunc
nnoremap <silent> <F2> :<C-u>silent! call <SID>FormatPar()<CR>
With that F2 re-formats the current paragraph, and using it with a count adds a hanging indent (that is 4F2 formats the paragraph with a hanging indent of 4).
It works very well for email messages, comments in code, and the like. It also has no problem with dealing with lists like above.
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
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