How can I use vim to detect blank lines containing whitespace and delete the whitespace?
For example, I use ⎵ to represent whitespace:
def⎵function(foo):
⎵⎵⎵⎵print(foo)
⎵⎵
⎵
function(1)
Is there a vim command that converts the code above to the following?
def⎵function(foo):
⎵⎵⎵⎵print(foo)
function(1)
:g/^\s\+$/s/\s\+//
Explanation:
g — execute the command globally (for all lines)
/^\s\+$/ — search lines that contain only whitespaces
s/\s\+// — for every found line execute this
search and replace command:
search whitespaces and replace with an empty string.
Could be simplified as
:%s/^\s\+$//
% — execute for all lines
s/^\s\+$// — search and replace command:
search lines that only have whitespaces
and replace with an empty string.
I have a function that solves this problem and keeps your cursor position
if !exists('*StripTrailingWhitespace')
function! StripTrailingWhitespace()
if !&binary && &filetype != 'diff'
let b:win_view = winsaveview()
silent! keepjumps keeppatterns %s/\s\+$//e
call winrestview(b:win_view)
endif
endfunction
endif
command! Cls call StripTrailingWhitespace()
cnoreabbrev cls Cls
cnoreabbrev StripTrailingSpace Cls
nnoremap <Leader>s :call StripTrailingWhitespace()
You can use a command :cls or a shortcut <leader>s.
Actually you can change it to fit your needs.
Related
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>
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.
I tend to enable auto-wrap :
:set textwidth=80
:set formatoptions+=wt
But I don't want to wrap when I input a long string in quotes when I coding with C or javascript, because it will be error;
Can I configure my vim auto-wrap exclude quotes?
or auto typing '\' before wrapping this line?
You can start from this little script which I coded and add some improvements in order to fit your needs:
"""""the event that will trigger the wrap (leaving insert mode)
au InsertLeave * call WrapLines()
"""""highlight the column where the wrapping will be made
set colorcolumn=30
"""""WrapLines will be executed on lines
function! WrapLines()
execute ":%g/^/ call WrapFunction()"
endfunction
"""""check and wrap the line
function! WrapFunction()
let l:line=getline(".")
let l:length=strlen(l:line)
let l:occurence=0
let l:i=0
let l:nb=30
for l:i in split(l:line,'\zs')
if matchstr(l:i,'"') != ''
let l:occurence+=1
let l:occurence=l:occurence % 2
endif
let l:nb-=1
if l:nb == 0
break
endif
endfor
if l:length >= 30
if l:occurence == 0
"""""to get ^M you need to type <ctrl-v><enter> buttons
normal! 30|i^M
endif
endif
endfunction
Note: to get the ^M in the code please type ctrl+v Enter
Name and save the file ex:script.vim and call it after that by the command ":source script.vim"
Here is the example: ( 30 characters - Limit -):
Your settings are wrapping the lines by adding carriage return and which cause problem while compiling.
Instead you can use the wrap option which is a virtual wrap and doesn't affect the lines:
:set wrap
:set textwidth=0
:set wrapmargin=0
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>
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()