40

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()
2
  • 5
    If preserving cursor position was important for you, you could specify that requirement in the question.
    – ib.
    Commented Sep 23, 2011 at 0:40
  • 2
    Sorry about that, I did not think that it could reset the cursor position.
    – gennad
    Commented Sep 23, 2011 at 8:34

6 Answers 6

44

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.

9
  • Nice one! Can you break it down for us? Particularly how it matches at the end of the file... Commented Sep 21, 2011 at 7:32
  • 3
    @Merlyn Morgan-Graham: # is the pattern delimiter. You can choose it where using the s command, / is not mandatory.
    – Benoit
    Commented Sep 21, 2011 at 7:33
  • @Benoit: Thanks :) I figured that out after staring at it for a minute. Keep forgetting. Now I'm trying to figure out how this matches the end of the file. Commented Sep 21, 2011 at 7:34
  • 5
    Another pattern would be /\_s*\%$
    – Benoit
    Commented Sep 21, 2011 at 7:34
  • 3
    @Benoit: Beware that pattern, since it cause removing of the trailing whitespace on the last nonempty line, which could be undesirable.
    – ib.
    Commented Sep 21, 2011 at 14:04
16

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:

  1. 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.

  2. 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.

  3. 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).

5
  • 3
    This is not only more elegant, but more functional. People sometimes confuse the value of mastering something, which is not by over engineering your limited subset of knowledge, but learning the new possibilities from the features that your tool is able to provide.
    – sidyll
    Commented Sep 23, 2011 at 2:44
  • My initial thought to this was to use some range with :d, like $?\s*.?+d, but I find this solution to be much better. Sadly, I must confess that multi-line regexps along with :g or :v do not usually occur to me. Thank you for sharing. Commented Sep 23, 2011 at 6:39
  • +1 for elegance of the solution. However I suspect that with large amounts of whitespace this command could be less efficient.
    – Benoit
    Commented Sep 23, 2011 at 8:40
  • 1
    @Benoit: Your suspicion is right: starting from about hundreds of kilobytes of consecutive whitespace, :vglobals become slower. However, the same patterns used to detect tailing blank or empty lines could be used to find the first of such lines to delete starting from that position to the end of file. (Please, see the updated answer.) By the way, in case of huge almost all-whitespace files straightforward substitution solution often does not help (it fails with E363 error), since it requires a lot of memory for pattern matching (usually more than value of maxmempattern option).
    – ib.
    Commented Sep 23, 2011 at 11:00
  • Nice answer! Can you include some explanation on the last two commands?
    – mMontu
    Commented Sep 23, 2011 at 12:37
9

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.
4
  • 1
    +1; I like the use of $put _ to ensure there is always a blank line. Commented Sep 21, 2011 at 13:27
  • This is an original and sophisticated command! It has a flow, though. When all lines of a buffer are non-blank (think of several lines containing only a single space character, for example), this command does not remove any of that lines! By the way, a similar idea could be used to construct a much cleaner Ex command based on the :global command.
    – ib.
    Commented Sep 23, 2011 at 0:35
  • @ib.: A file consisting of only whitespace would be noticed by the user before being saved (odds are it is a program in the Whitespace language !)
    – Benoit
    Commented Sep 23, 2011 at 3:44
  • @Benoit: It could be noticed. Or could not. The whole point of automation is to get rid of the need to watch for a specific situation that requires a command to be run. Nevertheless, the proposed command does not remove blank lines at the end of file if there is no non-blank lines in it. Therefore, this command does not solve the issue completely (wording of the question makes no exceptions for this case).
    – ib.
    Commented Sep 23, 2011 at 4:02
4

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
2

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()
2

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()

Not the answer you're looking for? Browse other questions tagged or ask your own question.