<buffer>的这种用法正确吗?
我认为这是正确的,但是您只需要将其包装在augroup中,然后清除后者,以确保不会在每次执行重新加载相同缓冲区的命令时复制autocmd。
正如您所解释的,特殊模式<buffer>
允许您依靠在file内部实现的内置文件类型检测机制$VIMRUNTIME/filetype.vim
。
在此文件中,您可以找到Vim的内置autocmds,它们负责为任何给定的缓冲区设置正确的文件类型。例如,对于降价:
" Markdown
au BufNewFile,BufRead *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md setf markdown
在文件类型插件中,您可以为安装的每个autocmd复制相同的模式。例如,要在几秒钟内光标没有移动时自动保存缓冲区:
au CursorHold *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md update
但是<buffer>
方法不那么冗长:
au CursorHold <buffer> update
此外,如果某天另一扩展名有效,并且$VIMRUNTIME/filetype.vim
已被更新为包含该扩展名,则不会通知您的autocmds。而且,您必须在文件类型插件中更新其所有模式。
如果我擦除缓冲区并打开另一个缓冲区,事情会变得凌乱吗(希望数字不会冲突,但是……)?
我不确定,但我不认为Vim可以重用已擦除缓冲区的缓冲区号。我无法从帮助中找到相关部分,但可以从vim.wikia.com找到本段:
不会。Vim不会将已删除缓冲区的缓冲区号重新用于新缓冲区。Vim将始终为新缓冲区分配下一个顺序号。
此外,如@ Tumbler41所述,擦除缓冲区时,其autocmds也将被删除。来自:h autocmd-buflocal
:
当然,清除缓冲区后,其缓冲区本地自动命令也将消失。
如果要检查自己,可以通过将Vim的详细级别提高到6来进行。您可以使用:verbose
修饰符仅针对一个命令临时进行检查。因此,在降价缓冲区内,您可以执行:
:6verbose bwipe
然后,如果您查看Vim的消息:
:messages
您应该看到一行如下所示:
auto-removing autocommand: CursorHold <buffer=42>
42
减价缓冲区的编号在哪里。
我应该注意哪些陷阱?
我认为有3种情况属于陷阱,涉及特殊模式<buffer>
。在其中两个中,<buffer>
可能是一个问题,在另一个中,这是一个解决方案。
陷阱1
首先,在清除本地缓冲区autocmds的augroup时,应格外小心。您必须熟悉以下代码段:
augroup your_group_name
autocmd!
autocmd Event pattern command
augroup END
因此,您可能会想将其用于未经修改的本地缓冲区autocmds,如下所示:
augroup my_markdown
autocmd!
autocmd CursorHold <buffer> update
augroup END
但这会产生不良影响。第一次加载markdown缓冲区时,我们A
将其称为,它将自动安装autocmd。然后,当您重新加载时A
,autocmd将被删除(由于autocmd!
),然后重新安装。因此,augroup将正确地防止autocmd的重复。
现在,假设您B
在第二个窗口中加载了第二个Markdown缓冲区,我们称它为。augroup的所有autocmds将被清除:这是的autocmd A
之一B
。然后,将为安装一个autocmd B
。
因此,当您在中进行一些更改B
并等待几秒钟CursorHold
被触发时,它将被自动保存。但是,如果您返回A
并执行相同的操作,则不会保存该缓冲区。这是因为上次加载降价缓冲区时,删除的内容和添加的内容之间不平衡。您删除的内容超过您添加的内容。
解决方案是通过将特殊模式传递<buffer>
给:autocmd!
:
augroup my_markdown
autocmd! CursorHold <buffer>
autocmd CursorHold <buffer> update
augroup END
请注意,您可以CursorHold
用星号替换匹配删除autocmds的行中的任何事件:
augroup my_markdown
autocmd! * <buffer>
autocmd CursorHold <buffer> update
augroup END
这样,当您要清除augroup时,不必指定autocmds正在侦听的所有事件。
陷阱2
还有另一个陷阱,但这一次<buffer>
不是问题,这是解决方案。
当您在文件类型插件中包含本地选项时,您可能会这样做:
setlocal option1=value
setlocal option2
对于缓冲区本地选项,这将按预期工作,但对于窗口本地选项,并非总是如此。为了说明问题,您可以尝试以下实验。创建文件~/.vim/after/ftdetect/potion.vim
,并在其中写入:
autocmd BufNewFile,BufRead *.pn setfiletype potion
该文件将自动potion
为任何扩展名是的文件设置文件类型.pn
。您不需要将其包装在augroup中,因为对于这种特定类型的文件,Vim会自动进行处理(请参见参考资料:h ftdetect
)。
如果系统上不存在中间目录,则可以创建它们。
接下来,创建文件类型插件~/.vim/after/ftplugin/potion.vim
,并在其中写入:
setlocal list
默认情况下,在potion
文件中,此设置将导致制表符显示为^I
,行尾显示为$
。
现在,创建一个最小的vimrc
;里面/tmp/vimrc
写:
filetype plugin on
...启用文件类型插件。
另外,创建一个药水文件/tmp/pn.pn
和一个随机文件/tmp/file
。在药水文件中,写一些东西:
foo
bar
baz
在随机文件中,写入药水文件的路径/tmp/pn.pn
:
/tmp/pn.pn
现在,以最少的初始化启动Vim,仅需采购vimrc
,然后在垂直视口中打开两个文件:
$ vim -Nu /tmp/vimrc -O /tmp/pn.pn /tmp/file
您应该看到2个垂直视口。左侧的药水文件显示带有美元符号的行的结尾,右侧的随机文件根本不显示它们。
将焦点放在随机文件上,然后按gf
以显示其路径在光标下的药水文件。现在,您会在右侧视口中看到相同的药水缓冲区,但是这次,行尾没有显示美元符号。如果输入:setlocal list?
,Vim应该回答nolist
:
整个事件链:
BufRead event → set 'filetype' option → load filetype plugins
...没有发生,因为BufRead
您按下时其中的第一个没有发生gf
。缓冲区已加载。
这似乎是出乎意料的,因为当您setlocal list
在药水文件类型插件中添加内容时,您可能已经想到它将启用'list'
在显示药水缓冲区的任何窗口中选项。
该问题并非特定于此新potion
文件类型。您可以通过markdown
文件。
它也不特定于该'list'
选项。您可以与其他窗口局部设置体验它,像'conceallevel'
,'foldmethod'
,'foldexpr'
,'foldtitle'
,...
它也不特定于gf
命令。您可以通过其他命令来体验它,这些命令可能会更改当前窗口中显示的缓冲区:全局标记,C-o
(在窗口本地跳转列表中向后移动):b {buffer_number}
、、 ...
总而言之,当且仅当以下情况下,才会正确设置window-local选项:
- 在当前的Vim会话中尚未读取该文件(因为
BufRead
必须将其触发)
- 文件显示在已经正确设置了窗口本地选项的窗口中
- 使用诸如以下命令创建新窗口
:split
(在这种情况下,它应该从执行命令的窗口继承窗口本地选项)
否则,可能无法正确设置窗口本地选项。
一种可能的解决方案是不直接从文件类型插件中设置它们,而是从后者中安装的autocmd设置它们,该插件将监听BufWinEnter
。每次在窗口中显示缓冲区时应触发此事件。
因此,例如,与其编写:
setlocal list
您可以这样写:
augroup my_potion
au! * <buffer>
au BufWinEnter <buffer> setlocal list
augroup END
在这里,您再次找到特殊的模式<buffer>
。
陷阱3
如果更改缓冲区的文件类型,则autocmds将保留。如果要删除它们,则需要进行配置b:undo_ftplugin
(请参阅参考资料:h undo_ftplugin
),并在其中包括以下命令:
exe 'au! my_markdown * <buffer>'
但是,请勿尝试删除augroup本身,因为仍然可能会有一些带有autocmds的markdown缓冲区。
FWIW,这是我用来设置的UltiSnips代码段b:undo_ftplugin
:
snippet undo "undo ftplugin settings" bm
" teardown {{{1
let b:undo_ftplugin = get(b:, 'undo_ftplugin', '')
\ .(empty(get(b:, 'undo_ftplugin', '')) ? '' : '|')
\ ."${1:
\ setl ${2:option}<}${3:
\ | exe '${4:n}unmap <buffer> ${5:lhs}'}${6:
\ | exe 'au! ${7:group_name} * <buffer>'}${8:
\ | unlet! b:${9:variable}}${10:
\ | delcommand ${11:Cmd}}
\ "
$0
endsnippet
这是我拥有的价值的一个例子~/.vim/after/ftplugin/awk.vim
:
let b:undo_ftplugin = get(b:, 'undo_ftplugin', '')
\ .(empty(get(b:, 'undo_ftplugin', '')) ? '' : '|')
\ ."
\ setl cms< cocu< cole< fdm< fdt< tw<
\| exe 'nunmap <buffer> K'
\| exe 'au! my_awk * <buffer>'
\| exe 'au! my_awk_format * <buffer>'
\ "
附带说明一下,我理解您为什么要问这个问题,因为当我查找<buffer>
在Vim的默认文件中使用了特殊模式的所有行时:
:vim /au\%[tocmd!].\{-}<buffer>/ $VIMRUNTIME/**/*
我只找到9个匹配项(您可能会发现或多或少的情况,我使用的是Vim版本8.0,补丁最多134
)。在9个匹配项中,有7个在文档中,实际上只有2个是源。您应该在$ VIMRUNTIME / syntax / dircolors.vim中找到它们:
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
我不知道它是否会引起问题,但是它们不在augroup内,这意味着每次您重新加载文件类型为的缓冲区dircolors
(如果您编辑名为,或路径结尾为的文件.dircolors
,都会发生这种情况),语法插件将添加一个新的本地缓冲区autocmd。.dir_colors
/etc/DIR_COLORS
您可以像这样检查它:
$ vim ~/.dir_colors
:au * <buffer>
最后一条命令应显示以下内容:
CursorHold
<buffer=1>
call s:reset_colors()
CursorHoldI
<buffer=1>
call s:reset_colors()
CursorMoved
<buffer=1>
call s:preview_color('.')
CursorMovedI
<buffer=1>
call s:preview_color('.')
现在,重新加载缓冲区,并再次询问当前缓冲区的本地缓冲区autocmds是什么:
:e
:au * <buffer>
这次,您将看到:
CursorHold
<buffer=1>
call s:reset_colors()
call s:reset_colors()
CursorHoldI
<buffer=1>
call s:reset_colors()
call s:reset_colors()
CursorMoved
<buffer=1>
call s:preview_color('.')
call s:preview_color('.')
CursorMovedI
<buffer=1>
call s:preview_color('.')
call s:preview_color('.')
之后每次重新读入文件,s:reset_colors()
并且s:preview_color('.')
将被称为一个额外的时间,事件的每一次一个CursorHold
,CursorHoldI
,CursorMoved
,CursorMovedI
被解雇了。
这可能不是一个大问题,因为即使重新加载 dircolors
多次文件,我也没有看到明显的速度下降或Vim的意外行为。
如果您遇到问题,可以联系语法插件的维护者,但是与此同时,如果您想防止autocmds重复,则可以dircolors
使用file 为文件创建自己的语法插件~/.vim/syntax/dircolors.vim
。在其中,您将导入原始语法插件的内容:
$ vim ~/.vim/syntax/dircolors.vim
:r $VIMRUNTIME/syntax/dircolors.vim
然后,在后者中,您只需将autocmds包装在要清除的augroup中。因此,您将替换这些行:
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
...与这些:
augroup my_dircolors_syntax
autocmd! * <buffer>
autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI <buffer> call s:reset_colors()
augroup END
请注意,如果您dircolors
使用文件创建了语法插件,则该语法插件~/.vim/after/syntax/dircolors.vim
将无法工作,因为默认语法插件将在此之前获得。通过使用~/.vim/syntax/dircolors.vim
,您的语法插件将在默认插件之前获取,并且将设置buffer-local变量b:current_syntax
,这将阻止源默认语法插件,因为它包含以下防护:
if exists("b:current_syntax")
finish
endif
一般规则似乎是:使用~/.vim/ftplugin
和~/.vim/syntax
目录创建自定义文件类型/语法插件,并防止在运行时路径中获取下一个插件(对于相同文件类型)(包括默认插件)。并使用~/.vim/after/ftplugin
,,而~/.vim/after/syntax
不是阻止其他插件的来源,而只是保留某些设置的价值。