简体   繁体   English

如何在 Vim 中修剪文件末尾的空行?

[英]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?如何在 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.要仅删除真正的“空”行,请从上述命令中删除\\s*

EDIT编辑

Explanation:解释:

  • \\( ..... Start a match group \\( ..... 开始比赛组
  • $\\n ... Match a new line (end-of-line character followed by a carriage return). $\\n ... 匹配新行(行尾字符后跟回车符)。
  • \\s* ... Allow any amount of whitespace on this new line \\s* ... 在这个新行上允许任意数量的空格
  • \\) ..... 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): 1.一个优雅的解决方案可以基于:vglobal命令(或者,这是同一件事,基于:global!修饰符):

: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).此命令在其中没有非空白字符的每一行上执行:delete ,以及在它之后的剩余文本中到缓冲区末尾(参见:help /\\s:help /\\S:help /\\_来理解模式)。 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.要删除严格意义上的行——而不是只包含空格的空白行——更改:vglobal命令中的模式,如下所示。

: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. 2.在包含大块连续空白字符(从大约数百千字节的空白开始)的巨大稀疏文件上,上述命令可能具有不可接受的性能。 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.如果是这种情况,可以使用相同的优雅想法将:vglobal命令转换为更快(但可能不那么优雅)的:delete命令,具有模式定义的范围。

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 :; ).将光标移动到缓冲区的第一行,然后再解释范围的其余部分 ( 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. 01行号之间的区别在于前者允许在第一行匹配,当存在用于定义范围结束行的搜索模式时。

  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 /\\@! ).搜索描述非尾随空行( \\_s*\\S\\n*. )的模式不匹配的行(否定是由于\\@!原子 - 请参阅: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 :$ ).将范围的结束行设置为缓冲区的最后一行 ( ,$ —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 ). 3.要在保存时运行上述任何命令,请使用将在BufWrite事件(或其同义词BufWritePre )上触发的自动命令触发它。

You can put this into your vimrc你可以把它放到你的 vimrc 中

au BufWritePre *.txt $put _ | $;?\(^\s*$\)\@!?+1,$d

(replace *.txt with whatever globbing pattern you want) (用你想要的任何通配符模式替换*.txt

Detail:细节:

  • BufWritePre is the event before writing a buffer to a file. BufWritePre是将缓冲区写入文件之前的事件。
  • $put _ appends a blank line at file end (from the always-empty register) $put _在文件末尾附加一个空行(来自始终为空的寄存器)
  • | chains Ex commands连锁 Ex 命令
  • $;?\\(^\\s*$\\)\\@!? goes to end of file ( $ ) then ( ; ) looks up backwards ( ?…? ) for the first line which is not entirely blank ( \\(^\\s*$\\)\\@! ), also see :help /\\@!转到文件末尾 ( $ ) 然后 ( ; ) 向后查找 ( ?…? ) 第一行不是完全空白的 ( \\(^\\s*$\\)\\@! ),另见:help /\\@! for negative assertions in vim searches.用于 vim 搜索中的否定断言。
  • ×××+1,$ forms a range from line ×××+1 till the last line ×××+1,$形成从行×××+1 到最后一行的范围
  • d deletes the line range. d删除行范围。

受到@Prince Goulash 解决方案的启发,将以下内容添加到您的~/.vimrc以删除每次保存 Ruby 和 Python 文件时的尾随空行:

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:我有一个名为Preserve的独立函数,我可以从其他函数调用它,使用 Preserve 做其他事情很容易:

" 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:这就是为什么我有一个单独的 Preserve 功能:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM