Vim - Visual Block : Delete rather than insert - vim

I often use visual block then inserting on multiple lines when for example commenting out a lot of code. This is great for inserting text in the same position on multiple lines but I can't figure out how to delete this text later using visual block mode, Backspace, Del and d all don't work. I am using MacVim.

You're looking for x:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
Then visual-block-select, x:
root:/root:/bin/bash
daeaemon:/usr/sbin:/bin/sh
bin/bin:/bin/sh
sys/dev:/bin/sh
I use this frequently, for exactly the same reason -- commenting and uncommenting large blocks of code.

This isn't directly answering the question (sarnold has already done so), but I would suggest there are more efficient ways of (un-)commenting code blocks. I have a CommentToggle function which either comments or uncomments the current line, depending on whether or not it begins with the "comchar".
function! CommentToggle(comchar)
let firstchar = matchstr(getline("."),"[^ ]")
if firstchar == a:comchar
sil exe 'normal ^xx'
else
sil exe 'normal ^i' . a:comchar . ' '
endif
endfunction
So, for perl files you can map:
nnoremap <silent> <leader>c :call CommentToggle('#')<CR>
and pressing 3 \ c (un-)comments three lines from the cursor position.
You can also write a visual-mode mapping:
vnoremap <silent> <leader>c :call CommentToggle('#')<CR>
allowing you to select a visual region and press \c to (un-)comment them all.
This particular function only works for one-character comments ("#", "%", etc.), but it is straightforward to extend it to longer strings (e.g. "//"), and even more complex replacements, such as HTML comments.
Hope this helps.

Prince Goulash's answer doesn't work in lines with leading tabs.
I changed it, adding the tab character to the pattern, although lines lose their indent after comment and uncomment.
function! CommentToggle( comchar )
let firstchar = matchstr( getline( "." ), "[^ \t]" )
if firstchar == a:comchar
sil exe 'normal ^2x'
else
sil exe 'normal ^i' . a:comchar . ' '
endif
endfunction
I like more adding the comment char to first position in line, this modification to Prince Goulash's function does the trick:
function! CommentToggle( comchar )
let firstchar = matchstr( getline( "." ), "[^ \t]" )
if firstchar == a:comchar
sil exe 'normal ^2x'
else
sil exe 'normal gI' . a:comchar . ' '
endif
endfunction

Related

Vim command to comment out a selection of lines?

Say I have a bunch of lines:
#Override
public void draw(Graphics g) {
g.setColor(Color.MAGENTA);
g.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
g.setColor(Color.BLACK);
g.drawRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
}
When I want to comment them out with // (i prefer line comments instead of block comments), what I do is:
Place the cursor infront of the # symbol
Ctrl-V: Switch to enter block-select mode
Select the column down to the } closing parenthesis using multiple hits of j
Shift-I: to enter block-insert
Type //
ESC to excit
Enter to finish the command
--> The lines are now commented out.
Is there an easier way where I don't need to do the block-select? I found I can use a substitution like :'<, '>s/^/\/\///g but this has two problems:
Its very clumsy and error prone to type (multiple forward and backward slashes need to be
escaped)
It places the comment symbols (//) at the beginning of
the line (position 0), not at the position where the first character
of that line was (so indentation is lost).
How can I insert // on the selected lines at the position of the first character of each line using Vi?
You can define a custom mapping or command for your :substitute.
However, there are several commenter plugins that do this very well, and those are generic (and often extensible) so that they work for any filetype:
NERD Commenter plugin
tComment plugin
commentary.vim plugin
I'd highly recommend to use one of those plugins instead of trying to reinvent a poor solution yourself.
I use Commentary as in the other answer, but a few thoughts:
<C-v>jjjjj could be <C-v>} or <C-v>/}<CR>
:substitute doesn’t have to use / as a separator: :'<,'>s-^-//
with a visual selection, you can also do :'<,'>normal! I//
How can I insert // on the selected lines at the position of the first character of each line using Vi?
Although, I'm agree with others and the dedicated plugin is a must have, but, as it is formulated in the OP, that's quite an easy task which can be implemented as one-liner:
vnoremap <silent>gc :call setline(".", printf("%*s" . &cms, indent("."), "", trim(getline("."))))<CR>
Now select some text, press "gc", and, voila, it works. To force // usage instead of the default /**/ set the following option for your buffer: setlocal cms=//\ %s. See :h 'cms'.
" I have a 'toggle comment function' that looks like
" Reference: https://stackoverflow.com/a/24652257/2571881
" these lines are needed for ToggleComment()
" Reference: https://stackoverflow.com/a/24652257/2571881
autocmd FileType c,cpp,java let b:comment_leader = '//'
autocmd FileType arduino let b:comment_leader = '//'
autocmd FileType sh,ruby,python let b:comment_leader = '#'
autocmd FileType zsh let b:comment_leader = '#'
autocmd FileType conf,fstab let b:comment_leader = '#'
autocmd FileType matlab,tex let b:comment_leader = '%'
autocmd FileType vim let b:comment_leader = '"'
function! ToggleComment()
if exists('b:comment_leader')
let l:pos = col('.')
let l:space = ( &ft =~ '\v(c|cpp|java|arduino)' ? '3' : '2' )
if getline('.') =~ '\v(\s*|\t*)' .b:comment_leader
let l:space -= ( getline('.') =~ '\v.*\zs' . b:comment_leader . '(\s+|\t+)#!' ? 1 : 0 )
execute 'silent s,\v^(\s*|\t*)\zs' .b:comment_leader.'[ ]?,,g'
let l:pos -= l:space
else
exec 'normal! 0i' .b:comment_leader .' '
let l:pos += l:space
endif
call cursor(line("."), l:pos)
else
echo 'no comment leader found for filetype'
end
endfunction
nnoremap <Leader>t :call ToggleComment()<CR>
inoremap <Leader>t <C-o>:call ToggleComment()<CR>
xnoremap <Leader>t :'<,'>call ToggleComment()<CR>
" vnoremap <Leader>t :call ToggleComment()<CR>
So, once you have this function on your ~/.vimrc you can do:
vip ...................... visual inner paragraph
<leader>t ................ in order to call the function
Make a macro with q, lets put it into the a buffer, so hit qa on a given line. Then press I// to jump to start of line, and comment it out. hit Esc and q and now your macro is done. This macro will comment out the current line.
The full command is qaI//Escq
Now visually select a bunch of lines with V, and type :norm!#a to run your a macro over those lines. This will comment out a bunch of lines.
Record another macro to do the opposite with qb^xx. This can be invoked by visually selecting the lines you want to uncomment and typing norm!#b
You can save these macros in your .vimrc and map the specific macro to a key combination if you want to "save" these commands.

Is it possible to use vimgrep for a visual selection of the current file?

I would like to search with vimgrep only within a visual selection of the current file and not the whole file. Is that possible and how? I couldn't find something for this case with Google or in vim help.
The reason why I want this is because I need the result in the quicklist (copen) and :g/FOO which is showing the matching lines at the bottom is not doing this job.
Yes, you can, as Vim has special regular expression atoms for mark positions, and the start and end of the visual selection is marked by '< and '>. As there are atoms for on / before / after a mark, we need to combine those to cover the entire range of selected lines:
On the selection start | after the selection start and before the selection end | on the selection end.
To limit the search to the current file, the special % keyword is used.
:vimgrep/\%(\%'<\|\%>'<\%<'>\|\%'>\)FOO/ %
You are on the right path with using :g command. The basic idea is do something like this:
:g/FOO/caddexpr expand("%") . ":" . line(".") . ":" . getline(".")
Now lets make it a command
command! -range -nargs=+ VisualSeach cgetexpr []|<line1>,<line2>g/<args>/caddexpr expand("%") . ":" . line(".") . ":" . getline(".")
Now you can do :VisualSearch FOO and it will add the searches to the quickfix list.
Note that the issue w/ this is only finds one match per line.
This is from Steve Losh's vimrc. It makes * work on the visual selection. I've gotten pretty dependent on it.
" Visual Mode */# from Scrooloose {{{
function! s:VSetSearch()
let temp = ##
norm! gvy
let #/ = '\V' . substitute(escape(##, '\'), '\n', '\\n', 'g')
let ## = temp
endfunction
vnoremap * :<C-u>call <SID>VSetSearch()<CR>//<CR><c-o>
vnoremap # :<C-u>call <SID>VSetSearch()<CR>??<CR><c-o>

How to convert leading spaces to tabs?

Many people use spaces rather than tabs. I use both of them. Tabs at the beginning of line and spaces from the first non-whitespace character. No problem for starting new document and in case I have to modify one better adapt to using format. Still sometimes I need to fix the spaces issue anyway.
According to Search and replace I can just do :%s/spaces_for_tab/tab/g. It is simple and it will work for many cases. Anyway I want to refactor only spaces at the beginning of line.
This is more of a regex issue. To anchor at the beginning of the line, use the caret, e.g.
s/^ /\t/
Or do it using vim's builtin functionality:
:set tabstop=4 "four spaces will make up for one tab
:set noexpandtab "tell vim to keep tabs instead of inserting spaces
:retab "let vim handle your case
By the way, I too prefer tabs for indentation and spaces for alignment. Unfortunately, vim doesn't handle this well (and I don't know what other editors do), so I mostly use :set expandtab (maybe see :set softtabstop).
I've written a simple func for it. Anyway it will work only for 4-space tab.
fu! Fixspaces()
while search('^\t* \{4}') != 0
execute ':%s/^\t*\zs \{4}/\t/g'
endwhile
endfu
You can suggest better solution, if exists, and I will use it with pleasure.
The issue is that this func replaces spaces in strings as well.
David's response is very elegant but it doesn't address leading whitespace that has a mixture of tabs and spaces. For example to convert a line like:
<SPACE><SPACE><TAB>something...
you have to know the position of the tab to determine the number of spaces needed to replace the <TAB> and reach the next tabstop. My solution below, although not as compact as David's, addresses this. It also allows me to select which way to use leading whitespace without depending upon &expandtab. I would appreciate any suggestions to improve my code...
function! TabsToSpaces(...)
let ts = &tabstop
let pos = getpos('.')
while search('^ *\zs\t', "w") != 0
let l:curpos = getcharpos('.')
" The number of spaces needed depends upon the position of the <TAB>
let numsp = 1 + ts - ( curpos[2] % ts )
if numsp == 9
let numsp = 1
endif
silent execute ':s/^ *\zs\t/'.repeat(' ', numsp).'/'
endwhile
if a:0 == 0
echo 'Changed leading tabs to spaces'
endif
call setpos('.', pos)
endfunction
function! SpacesToTabs(...)
let ts = &tabstop
let pos = getpos('.')
" First normalize all tabs to spaces
call TabsToSpaces("quiet")
while search('^\t* \{'.ts.'}') != 0
silent execute ':%s/^\t*\zs \{'.ts.'}/\t/g'
endwhile
if a:0 == 0
echo 'Changed leading spaces to tabs'
endif
call setpos('.', pos)
endfunction
" Some keystrokes to implement the spaces/tabs functions
nmap <Leader>st :execute SpacesToTabs()<CR>
nmap <Leader>ts :execute TabsToSpaces()<CR>
I took Martin's answer and improved on it a bit if anyone's interested:
function Fixspaces()
let ts = &tabstop
let pos = getpos('.')
if &expandtab
while search('^ *\t') != 0
silent execute ':%s/^ *\zs\t/'.repeat(' ', ts).'/g'
endwhile
echo 'Changed tabs to spaces'
else
while search('^\t* \{'.ts.'}') != 0
silent execute ':%s/^\t*\zs \{'.ts.'}/\t/g'
endwhile
echo 'Changed spaces to tabs'
endif
call setpos('.', pos)
endfunction
This function does the appropriate thing depending on the values of the expandtab and tabstop settings and also remembers where the cursor is.

Configure Vim to insert text for Lines of Code

I'm using Vim for all program editing and I have a standard header I use at the top of all my source code files. I have a .vimrc file set up to update certain fields in this header (like Last Modified) when I save any changes using :w
My question is, how do I put in a function to count lines of code, following the basic rule that only non-blank lines are counted?
I know within an open vim buffer, I can use
:%s/\n//gn
to count all lines, and
:%s/\n\n//gn
to count blank lines (basically count how many times two newlines appear in a row, indicating a blank line). But how do I put this in my .vimrc file?
Here's the code fragment from my .vimrc that updates the header fields:
function! LastModified()
if &modified
let save_cursor = getpos(".")
let n = min([20, line("$")])
keepjumps exe '1,' . n . 's#^\(.\{,10}Last Modified:\).*#\1' .
\ strftime(' %a %b %d, %Y %I:%M%p') . '#e'
keepjumps exe '1,' . n . 's#^\(.\{,10}Filename:\).*#\1' .
\ ' ' . #% . '#e'
keepjumps exe '1,' . n . 's#^\(.\{,10}LOC:\).*#\1' .
\ ' ' . '' . '#e'
call histdel('search', -1)
call setpos('.', save_cursor)
endif
endfun
Also, I would just like to add, I know there are numerous other ways to do this (like using wc --lines from the shell) but I'm interested in learning how to really configure my editor (so call it a learning exercise).
You actually should not want to use :s here:
function! CountNonEmpty()
return len(filter(getline(1, line('$')), '!empty(v:val)'))
endfunction
By the way, I would have used getline+map+setline to implement your header updater:
function! LastModified()
if &modified
" If number of buffer lines is < 20, then getline(1, 20)"
" will return only existing lines without any errors "
call setline(1, map(getline(1, 20), 'substitute(substitute(substitute(v:val, '.
\'"^\\v(.{,10}Last Modified:).*", "\\1 ".strftime("%s %b %d, %Y %I:%M%p"), ""),'.
\'"^\\v(.{,10}Filename:).*", "\\1 ".escape(#%, "&\\~"), ""),'.
\'"^\\v(.{,10}LOC:).*", "\\1 ", "")'))
endif
endfunction
This might help:
function! CountNonEmpty()
redir => g:nonblank
silent %s/^.\+$/&/n
redir END
return substitute(g:nonblank, '\n\s*\(\d\+\)\D.*$', '\1', '')
endfunction
:redir => Stores the output of the following ex commands into the given variable. See :help :redir

How to delete multiple buffers in Vim?

Assuming I have multiple files opened as buffers in Vim. The files have *.cpp, *.h and some are *.xml. I want to close all the XML files with :bd *.xml. However, Vim does not allow this (E93: More than one match...).
Is there any way to do this?
P.S. I know that :bd file1 file2 file3 works. So can I somehow evaluate *.xml to file1.xml file2.xml file3.xml?
You can use <C-a> to complete all matches. So if you type :bd *.xml and then hit <C-a>, vim will complete the command to :bd file1.xml file2.xml file3.xml.
:3,5bd[elete]
Will delete buffer range from 3 to 5 .
You also can use alternatively use:
:.,$-bd[elete] " to delete buffers from the current one to last but one
:%bd[elete] " to delete all buffers
You can use this.
:exe 'bd '. join(filter(map(copy(range(1, bufnr('$'))), 'bufname(v:val)'), 'v:val =~ "\.xml$"'), ' ')
It should be quite easy to add it to a command.
function! s:BDExt(ext)
let buffers = filter(range(1, bufnr('$')), 'buflisted(v:val) && bufname(v:val) =~ "\.'.a:ext.'$"')
if empty(buffers) |throw "no *.".a:ext." buffer" | endif
exe 'bd '.join(buffers, ' ')
endfunction
command! -nargs=1 BDExt :call s:BDExt(<f-args>)
Try the script below. The example is for "txt", change it as needed, e.g. to "xml".
Modified buffers are not deleted. Press \bd to delete the buffers.
map <Leader>bd :bufdo call <SID>DeleteBufferByExtension("txt")
function! <SID>DeleteBufferByExtension(strExt)
if (matchstr(bufname("%"), ".".a:strExt."$") == ".".a:strExt )
if (! &modified)
bd
endif
endif
endfunction
[Edit]
Same without :bufdo (as requested by Luc Hermitte, see comment below)
map <Leader>bd :call <SID>DeleteBufferByExtension("txt")
function! <SID>DeleteBufferByExtension(strExt)
let s:bufNr = bufnr("$")
while s:bufNr > 0
if buflisted(s:bufNr)
if (matchstr(bufname(s:bufNr), ".".a:strExt."$") == ".".a:strExt )
if getbufvar(s:bufNr, '&modified') == 0
execute "bd ".s:bufNr
endif
endif
endif
let s:bufNr = s:bufNr-1
endwhile
endfunction
I too had a need for this functionality all the time. This is the solution I have in my vimrc.
function! GetBufferList()
return filter(range(1,bufnr('$')), 'buflisted(v:val)')
endfunction
function! GetMatchingBuffers(pattern)
return filter(GetBufferList(), 'bufname(v:val) =~ a:pattern')
endfunction
function! WipeMatchingBuffers(pattern)
let l:matchList = GetMatchingBuffers(a:pattern)
let l:count = len(l:matchList)
if l:count < 1
echo 'No buffers found matching pattern ' . a:pattern
return
endif
if l:count == 1
let l:suffix = ''
else
let l:suffix = 's'
endif
exec 'bw ' . join(l:matchList, ' ')
echo 'Wiped ' . l:count . ' buffer' . l:suffix . '.'
endfunction
command! -nargs=1 BW call WipeMatchingBuffers('<args>')
Now, I can just do :BW regex (e.g. :BW \.cpp$ and wipe all matching buffers that have match that pattern in their pathname.
If you want to delete rather than wipe, you can of course replace exec 'bw ' . join(l:matchList, ' ') with exec 'bd ' . join(l:matchList, ' ')
TAB will only autocomplete one file for you as of Vim 7.4.282
use <c-a> to autocomplete all files.
You can just use:
bd filetype
then just use <c-a> to facilitate the completion of all open files of specified filetype.
for example, you have 1.xml, 2.xml, 3.xml, and 4.xml,
you can do:
bd xml
then press <c-a>
vim will autocomplete for you as follow:
bd 1.xml 2.xml 3.xml 4.xml
you can just press enter to complete the command.
if you have made changes in one of the files mentioned above, do remember to do:
bd! xml
Very simply: use the :bd[elete] command. For example, :bd[elete] buf#1 buf#5 buf#3 will delete the buffers 1, 3, and 5.

Resources