Vim command to comment out a selection of lines? - vim

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.

Related

How to make f and t wrap around the current line in vim

Is there a way to make the 'f' and 't' command wrap around the line? For example, if I have
Hello, my name is _intz,
where _ denotes my cursor position, I would like to be able to press fl for vim to place my cursor on the first l on the line.
Similarly, I would ideally like the , and ; commands to also wrap on the current line.
Thank you
No, this is not possible without implementing the feature yourself.
Note that fF are universally expected to mean "next on the line" and tT to mean "previous on the line", both of which being extremely useful in their own right. Instead of changing their meaning, and thus reducing the overall usefulness of Vim, you should consider making new commands.
Something like these quick and dirty mappings:
" move the cursor on first occurrence of character on the line
nnoremap <expr> <key> '0f' . nr2char(getchar())
" move the cursor before first occurrence of character on the line
nnoremap <expr> <key> '0t' . nr2char(getchar())
See :help <expr>, :help nr2char(), :help getchar().
With the help of this answer https://vi.stackexchange.com/questions/29167/determine-if-there-is-a-matching-character-on-the-current-line-past-the-cursor, the following maps <c-f> to allow that gives it the functionality of f with same line wrapping.
function!Neweff()
let character = nr2char(getchar())
let beforejump = getpos('.')
execute 'norm! f'.character.''
let afterjump = getpos('.')
if beforejump == afterjump
let firstcharacter = getline(".")[0]
execute 'norm! 0'
if character !=# firstcharacter
execute 'norm! f'.character.''
endif
endif
endfunction
nnoremap <c-f> :call Neweff()<CR>

In vim, how to get * to highlight and yank words including special chars (#):,^

In vim 8.0, for .sh files, I want to press * to highlight the word under the cursor, copy the word into the yank buffer, and I want words to include the characters:
( # ) : , ^
in addition to the alphanumeric and underscore characters. I also want a second press of the * to clear the highlighting. I note that the default for iskeyword is:
iskeyword=#,48-57,_,192-255
Here's what I have in my ~/.vimrc file:
function! Highlighting()
if g:highlighting == 1 && #/ =~ '^\\<'.expand('<cword>').'\\>$'
let g:highlighting = 0
return ":silent nohlsearch\<CR>"
endif
let #/ = '\<'.expand('<cword>').'\>'
let g:highlighting = 1
return ":silent set hlsearch\<CR>yiw"
endfunction
set iskeyword+=(,#,),:,,,^
let g:highlighting = 0
nnoremap <silent> <expr> <CR> Highlighting()
It works for the most part. What's not working is that words are not including the characters:
( #
but they are including the others, including the closing parenthesis. Here are some words that should get matched:
(#)_name
(#):name
(#)_name_name
(#):name^name
(#):name,name
Can anyone suggest a fix that allows this to work with words including all of the characters I want in a word? Please feel free to suggest a completely different implementation or plugins that solves my needs.
The answer is to do the following in the ~/.gvimrc file:
set iskeyword+=(,#-#,),:,,,^
To get more information on why the idiom #-# is needed, do in vim:
:help isfname

How to auto highlight the current word in vim?

For example, our text is:
hello world
abcd hello world
hello world
In eclipse, when your cursor is at some word, the word hello is auto highlight in the current file. When you type ww in normal mode, the cursor is at other word world will highlight in the current file, the hello is un-highlighted automatically. This feature is very convenient for users.
Does vim can do this with some plugin or else?
Something like this?
set updatetime=10
function! HighlightWordUnderCursor()
if getline(".")[col(".")-1] !~# '[[:punct:][:blank:]]'
exec 'match' 'Search' '/\V\<'.expand('<cword>').'\>/'
else
match none
endif
endfunction
autocmd! CursorHold,CursorHoldI * call HighlightWordUnderCursor()
This won't clobber the search register but will use the same highlighting as would normally be used. (If you want a different highlight color change Search to that highlight group.) A short update time is needed so that the CursorHold event it fired fairly often. It also won't highlight anything if the cursor is above punctuation or whitespace.
The iskeyword setting determines what is considered part of a word when expand('<cword>') is used.
Improving #FDinoff's amazing answer, with custom highlight - dark BG and underline, and disable on quickfix list, fugitive filetype and when on diff:
function! HighlightWordUnderCursor()
let disabled_ft = ["qf", "fugitive", "nerdtree", "gundo", "diff", "fzf", "floaterm"]
if &diff || &buftype == "terminal" || index(disabled_ft, &filetype) >= 0
return
endif
if getline(".")[col(".")-1] !~# '[[:punct:][:blank:]]'
hi MatchWord cterm=undercurl gui=undercurl guibg=#3b404a
exec 'match' 'MatchWord' '/\V\<'.expand('<cword>').'\>/'
else
match none
endif
endfunction
augroup MatchWord
autocmd!
autocmd! CursorHold,CursorHoldI * call HighlightWordUnderCursor()
augroup END
Yes there is a vim plugin for highlighting the occurances of a word automatically. This one is implemented exclusively for $variables and ->properties in .php files.
DEMO :
And here is the same one but adapted for Perl files.
DEMO :
May be you can modify it for your purpose.
There's a script on vim.wikia.com for doing exactly that. It waits until you've stopped moving the cursor and then highlights all instances of the current word. You can then use n and N to jump between them like you normally would with search results.
I'm copying it here in case the link goes down:
" Highlight all instances of word under cursor, when idle.
" Useful when studying strange source code.
" Type z/ to toggle highlighting on/off.
nnoremap z/ :if AutoHighlightToggle()<Bar>set hls<Bar>endif<CR>
function! AutoHighlightToggle()
let #/ = ''
if exists('#auto_highlight')
au! auto_highlight
augroup! auto_highlight
setl updatetime=4000
echo 'Highlight current word: off'
return 0
else
augroup auto_highlight
au!
au CursorHold * let #/ = '\V\<'.escape(expand('<cword>'), '\').'\>'
augroup end
setl updatetime=500
echo 'Highlight current word: ON'
return 1
endif
endfunction
As noted in a comment on that page, if you always want this feature on you can just call the function from your vimrc after defining it. That way you can use z/ (or whatever shortcut you assign it to) to turn it off again later.

How can I trim blank lines at the end of file in Vim?

Sometimes I accidentally leave blank lines at the end of the file I am editing.
How can I trim them on saving in Vim?
Update
Thanks guys, all solutions seem to work.
Unfortunately, they all reset current cursor position, so I wrote the following function.
function TrimEndLines()
let save_cursor = getpos(".")
silent! %s#\($\n\s*\)\+\%$##
call setpos('.', save_cursor)
endfunction
autocmd BufWritePre *.py call TrimEndLines()
This substitute command should do it:
:%s#\($\n\s*\)\+\%$##
Note that this removes all trailing lines that contain only whitespace. To remove only truly "empty" lines, remove the \s* from the above command.
EDIT
Explanation:
\( ..... Start a match group
$\n ... Match a new line (end-of-line character followed by a carriage return).
\s* ... Allow any amount of whitespace on this new line
\) ..... End the match group
\+ ..... Allow any number of occurrences of this group (one or more).
\%$ ... Match the end of the file
Thus the regex matches any number of adjacent lines containing only whitespace, terminated only by the end of the file. The substitute command then replaces the match with a null string.
1. An elegant solution can be based on the :vglobal command
(or, which is the same thing, on the :global with ! modifier):
:v/\_s*\S/d
This command executes :delete on every line that does not have
non-whitespace characters in it, as well as after it in the remaining
text to the end of buffer (see :help /\s, :help /\S, and :help /\_
to understand the pattern). Hence, the command removes the tailing
blank lines.
To delete the empty lines in a strict sense—as opposed to blank ones
containing only whitespace—change the pattern in that :vglobal
command as follows.
:v/\n*./d
2. On huge sparse files containing large blocks of consecutive
whitespace characters (starting from about hundreds of kilobytes of
whitespace) the above commands might have unacceptable performance.
If that is the case, the same elegant idea can be used to transform
that :vglobal commands into much faster (but perhaps less
elegantly-looking) :delete commands with pattern-defined ranges.
For blank lines:
:0;/^\%(\_s*\S\)\#!/,$d
For empty lines:
:0;/^\%(\n*.\)\#!/,$d
The essence of both commands is the same; namely, removing the lines
belonging to the specified ranges, which are defined according to the
following three steps:
Move the cursor to the first line of the buffer before interpreting
the rest of the range (0;—see :help :;). The difference between
0 and 1 line numbers is that the former allows a match at the
first line, when there is a search pattern used to define the ending
line of the range.
Search for a line where the pattern describing a non-tailing blank
line (\_s*\S or \n*.) does not match (negation is due to the
\#! atom—see :help /\#!). Set the starting line of the range to
that line.
Set the ending line of the range to the last line of the buffer
(,$—see :help :$).
3. To run any of the above commands on saving, trigger it using
an autocommand to be fired on the BufWrite event (or its synonym,
BufWritePre).
You can put this into your vimrc
au BufWritePre *.txt $put _ | $;?\(^\s*$\)\#!?+1,$d
(replace *.txt with whatever globbing pattern you want)
Detail:
BufWritePre is the event before writing a buffer to a file.
$put _ appends a blank line at file end (from the always-empty register)
| chains Ex commands
$;?\(^\s*$\)\#!? goes to end of file ($) then (;) looks up backwards (?…?) for the first line which is not entirely blank (\(^\s*$\)\#!), also see :help /\#! for negative assertions in vim searches.
×××+1,$ forms a range from line ×××+1 till the last line
d deletes the line range.
Inspired by solution from #Prince Goulash, add the following to your ~/.vimrc to remove trailing blank lines for every save for Ruby and Python files:
autocmd FileType ruby,python autocmd BufWritePre <buffer> :%s/\($\n\s*\)\+\%$//e
I found the previous answers using substitute caused trouble when operating on very large files and polluted my registers. Here's a function I came up with which performs better for me, and avoids polluting registers:
" Strip trailing empty newlines
function TrimTrailingLines()
let lastLine = line('$')
let lastNonblankLine = prevnonblank(lastLine)
if lastLine > 0 && lastNonblankLine != lastLine
silent! execute lastNonblankLine + 1 . ',$delete _'
endif
endfunction
autocmd BufWritePre <buffer> call TrimTrailingLines()
I have a separated function called Preserve which I can call from other functions,
It makes easy to use Preserve to do other stuff:
" remove consecutive blank lines
" see Preserve function definition
" another way to remove blank lines :g/^$/,/./-j
" Reference: https://stackoverflow.com/a/7496112/2571881
if !exists('*DelBlankLines')
fun! DelBlankLines() range
if !&binary && &filetype != 'diff'
call Preserve(':%s/\s\+$//e')
call Preserve(':%s/^\n\{2,}/\r/ge')
call Preserve(':%s/\v($\n\s*)+%$/\r/e')
endif
endfun
endif
In my case, I keep at least one blank line, but not more than one, at the end of the file.
" Utility function that save last search and cursor position
" http://technotales.wordpress.com/2010/03/31/preserve-a-vim-function-that-keeps-your-state/
" video from vimcasts.org: http://vimcasts.org/episodes/tidying-whitespace
" using 'execute' command doesn't overwrite the last search pattern, so I
" don't need to store and restore it.
" preserve function
if !exists('*Preserve')
function! Preserve(command)
try
let l:win_view = winsaveview()
"silent! keepjumps keeppatterns execute a:command
silent! execute 'keeppatterns keepjumps ' . a:command
finally
call winrestview(l:win_view)
endtry
endfunction
endif
Here's why I have a separated Preserve function:
command! -nargs=0 Reindent :call Preserve('exec "normal! gg=G"')
" join lines keeping cursor position
nnoremap J :call Preserve(':join')<CR>
nnoremap <Leader>J :call Preserve(':join!')<CR>
" Reloads vimrc after saving but keep cursor position
if !exists('*ReloadVimrcFunction')
function! ReloadVimrcFunction()
call Preserve(':source $MYVIMRC')
" hi Normal guibg=NONE ctermbg=NONE
windo redraw
echom "Reloaded init.vim"
endfunction
endif
noremap <silent> <Leader>v :drop $MYVIMRC<cr>
command! -nargs=0 ReloadVimrc :call ReloadVimrcFunction()
" Strip trailing whitespaces
command! Cls :call Preserve(':%s/\v\s+$//e')
And if by any chance you want to create an autocommand you must create a group
to avoid overloading autocommands:
augroup removetrailingspaces
au!
au! BufwritePre *.md,*.py,*.sh,*.zsh,*.txt :call Preserve(':%s/\v\s+$//e')
augroup END
This one is to change file headers (if you have any suggestions) feel free to interact:
" trying avoid searching history in this function
if !exists('*ChangeHeader')
fun! ChangeHeader() abort
if line('$')>=7
call Preserve(':1,7s/\v(Last (Change|Modified)|date):\s+\zs.*/\=strftime("%b %d, %Y - %H:%M")/ei')
endif
endfun
endif
" dos2unix ^M
if !exists('*Dos2unixFunction')
fun! Dos2unixFunction() abort
"call Preserve('%s/ $//ge')
call Preserve(":%s/\x0D$//e")
set ff=unix
set bomb
set encoding=utf-8
set fileencoding=utf-8
endfun
endif
com! Dos2Unix :call Dos2unixFunction()

Auto insert text at a newline in vim

I am editing a LaTeX file with vim. When I am in the \begin{itemize} environment, is there any way to tell vim to autoinsert \item whenever I open a new line?
function CR()
if searchpair('\\begin{itemize}', '', '\\end{itemize}', '')
return "\r\\item"
endif
return "\r"
endfunction
inoremap <expr><buffer> <CR> CR()
Put this into your .vim/ftplugins/tex.vim file (or any .vim inside .vim/ftplugins/tex directory).
I would recommend http://vim-latex.sourceforge.net. This package defines several maps useful for latex.
In particular for inserting \item you press <ATL-I>
I know noting about latex but I think it is a good idea to search in vim scripts
use the search button up left :D
for example search for
latex auto completion
I can hit Cntl-I and it'll put it in for me in either normal mode or insert mode. This is what I put in my .vimrc:
:imap <C-i> \item
:nmap <C-i> o\item
Note that there is a space at the end of \item.
I hacked the script ZyX supplied and came up with this. It adds support for the o and O commands. It does not require LaTeX-VIM.
function AddItem()
if searchpair('\\begin{itemize}', '', '\\end{itemize}', '')
return "\\item "
else
return ""
endif
endfunction
inoremap <expr><buffer> <CR> "\r".AddItem()
nnoremap <expr><buffer> o "o".AddItem()
nnoremap <expr><buffer> O "O".AddItem()
Extended Version of Answer by Samad Lotia and ZyX
Place this in your ~/.vim/after/ftplugin/tex.vim
function! AddItem()
let [end_lnum, end_col] = searchpairpos('\\begin{', '', '\\end{', 'nW')
if match(getline(end_lnum), '\(itemize\|enumerate\|description\)') != -1
return "\\item "
else
return ""
endif
endfunction
inoremap <expr><buffer> <CR> getline('.') =~ '\item $'
\ ? '<c-w><c-w>'
\ : (col(".") < col("$") ? '<CR>' : '<CR>'.AddItem() )
nnoremap <expr><buffer> o "o".AddItem()
nnoremap <expr><buffer> O "O".AddItem()
Improvements
auto-insert of \item also happens for environments enumerate and description
auto-insert of \item only happens if the immediate surrounding environment is one of the three (itemize/enumerate/description). It does not happen in following circumstance
\begin{itemize}
\item
\begin{minipage}
<CURSOR>
\end{minipage}
\end{itemize}
auto-insert of \item only happens if you are at the end of the line
deletion of auto inserted \item by pressing <CR> a second time. If one wants to add some indention in this case, change '<c-w><c-w>' to '<c-w><c-w><c-t>'.

Resources