我使用的是vim 8.1,在编写C代码时,我注意到我所有的花括号都使用了红色背景和白色前景。经过一番挖掘,我在我的主题中发现了这样的设置:call s:hi('Error', { 'fg': s:white, 'bg': s:red })
通过修改这里的颜色,我已经确认了这就是颜色的来源。
由于这是一个语法错误,并且我的vimrc中有syntax on
,我很困惑为什么我的大括号显示为语法错误。即使在非常简单的代码中也会发生这种情况,例如:
int main(int argc, char **argv)
{
return 0;
}
我很困惑为什么这会显示为一个错误。如果我把开头的大括号放在主定义的末尾或后面,就会发生这种情况。如果我运行clist
vim,则会告诉我没有错误。vimrc可在以下位置查看:https://pastebin.com/LukL8MBg
编辑1:在进一步的挖掘中,问题似乎与我使用的颜色主题有关。有一个文件包含以下语法:
syn keyword cDeclarationOverwrite var const type
syn match cBraces "[{}[]]"
syn match cParens "[()]"
syn match cOpSymbols "={1,2}|!=|<|>|>=|<=|++|+=|--|-="
syn match cEndColons "[,]"
syn match cLogicSymbols "(&&)|(||)|(!)"
编辑2:根据用户@filbranden的建议,我运行了:scriptnames
,并看到以下内容:
1: /usr/share/vim/vimrc
2: ~/.vimrc
3: ~/.vim/autoload/plug.vim
4: /usr/share/vim/vim81/filetype.vim
5: /usr/share/vim/vim81/ftplugin.vim
6: /usr/share/vim/vim81/indent.vim
7: /usr/share/vim/vim81/syntax/syntax.vim
8: /usr/share/vim/vim81/syntax/synload.vim
9: /usr/share/vim/vim81/syntax/syncolor.vim
10: ~/.vim/plugins/purify/vim/colors/purify.vim
11: ~/.vim/plugins/purify/vim/autoload/purify.vim
12: /usr/share/vim/vim81/plugin/getscriptPlugin.vim
13: /usr/share/vim/vim81/plugin/gzip.vim
14: /usr/share/vim/vim81/plugin/logiPat.vim
15: /usr/share/vim/vim81/plugin/manpager.vim
16: /usr/share/vim/vim81/plugin/matchparen.vim
17: /usr/share/vim/vim81/plugin/netrwPlugin.vim
18: /usr/share/vim/vim81/plugin/rrhelper.vim
19: /usr/share/vim/vim81/plugin/spellfile.vim
20: /usr/share/vim/vim81/plugin/tarPlugin.vim
21: /usr/share/vim/vim81/plugin/tohtml.vim
22: /usr/share/vim/vim81/plugin/vimballPlugin.vim
23: /usr/share/vim/vim81/plugin/zipPlugin.vim
24: /usr/share/vim/vim81/autoload/dist/ft.vim
25: /usr/share/vim/vim81/ftplugin/c.vim
26: /usr/share/vim/vim81/indent/c.vim
27: ~/.vim/plugins/purify/vim/syntax/c.vim
28: /usr/share/vim/vim81/syntax/c.vim
Press ENTER or type command to continue
从这里的情况来看,purify插件中的c.vim是在最后加载的vim语法/c.vim之前加载的。
:set rtp?
的输出显示:
runtimepath=~/.vim,~/.vim/plugins/purify/vim,/usr/share/vim/vimfiles,/usr/share/vim/vim81,/usr/share/vim/vimfiles/after,~/.vim/after
有人能解释为什么会发生这种事吗?
因此,在我看来,这是kyoz/purify
插件设置的语法规则中的一个错误,特别是关于规则相对于Vim运行时规则的排序。
当插件管理器添加插件时,它会将该插件的目录添加到'runtimepath'
,并将其添加到Vim运行时目录之前。这样做是为了让插件有机会更早地运行,从而覆盖Vim运行时附带的文件。
特别是,这意味着当加载C类型的文件时,kyoz/purity
插件中的语法规则将被添加到Vim运行时的语法规则之前。
现在,事实证明,这引发了规则如何相互作用的冲突。
例如,让我们检查cParen
(来自Vim运行时)和cParens
(来自kyoz/purify
)之间的交互。为了简单起见,假设我们使用的是let g:c_no_curly_error = 1
,这样我们就不必讨论有两个end=
组,但同样的原理也适用于全局变量未设置的情况。
根据当前的排序,我们最终得到kyoz/purify
中的cParens
,位于Vim运行时的cParen
之前:
" From kyoz/purify:
syn match cParens "[()]"
" From Vim runtime:
syn region cParen transparent start='(' end=')' contains=ALLBUT,...
现在,语法规则的第一件事是之后的规则获胜。因此,当Vim遇到(
时,它可以同时匹配两者(或者cParens
匹配其中一个,或者cParen
的start
完全匹配(
),它将选择cParen
(来自Vim运行时),因为这是稍后发生的。
没关系。。。但为什么)
会出现问题呢?
由于cParen
是一个"区域",Vim将继续尝试匹配其中的end
部分。在这样做的同时,它还将尝试匹配contains
中的组。但在这里,contains
以ALLBUT
开头,这意味着它将尝试匹配除显式列表中的组之外的所有组。这意味着它也将匹配该组内的cParens
。
现在,这就是问题的原因,因为如果cParens
与)
匹配,那么这意味着)
将不会被识别为cParen
的末尾。这实际上是在:help :syn-keepend
:中描述的
默认情况下,包含的匹配可以掩盖结束模式的匹配。这对嵌套很有用。例如,以
{
开始、以}
结束的区域可以包含另一个区域。遇到的}
将结束包含的区域,但不结束外部区域。如果您不希望这样,
keepend
参数将使外部区域的结束模式的匹配也结束任何包含的项。这使得不可能嵌套相同的区域,但允许包含的项目高亮显示结束模式的部分,而不会导致跳过与结束模式的匹配。
因此,如果我们在Vim运行时的区域规则上有keepend
,这实际上会结束cParen
规则,这不会导致问题。但事实并非如此,使cParens
与)
匹配意味着cParen
将一直运行到文件末尾,这将导致大括号触发错误(因为在C中,括号内不允许大括号。)
如果我们确实有keepend
,情况会稍微好一点,因为cParen
会结束。但这仍然不是理想的,因为kyov/purify
的cParens
只匹配)
,而不会真正匹配(
。(您可能需要类似matchgroup
的东西来匹配分隔符,但这是另一个主题。)
所以这应该可以解释为什么会发生这种情况。
那么我们能做些什么呢?
在我看来,按相反的顺序加载规则在这里会更好。
" From Vim runtime:
syn region cParen transparent start='(' end=')' contains=ALLBUT,...
" From kyoz/purify:
syn match cParens "[()]"
由于,当有多个匹配时,Vim将使用最后出现的规则,这将始终只触发(
的cParens
,因此我们不会遇到失控组的问题。)
也将只匹配cParens
(在这种情况下,这与cParen
不再冲突,因为这只匹配组的末尾。)
这将使kyov/purify
中的规则按预期工作。
但它确实有副作用!Vim运行时的cParen
规则将被该插件完全遮蔽。(还有在相同字符上匹配的类似规则,例如cBlock
.
这是个问题吗?大概这些规则中的大多数似乎都标记为transparent
(它们不会直接影响高亮显示),主要用于标记不平衡或不匹配的大括号等情况,并将其标记为错误(与您看到的类似,但用于实际的语法问题)
如果您认为kyov/purify
中的用例(即为大括号上色)比Vim运行时中的用例更可取,那么您可能可以在Vim运行中隐藏规则。
这可以通过将语法规则移动到after/
子目录下来实现。插件管理器将识别插件中的after/
目录,并将其添加到Vim运行时之后。这是一种既定的机制,允许插件决定他们是希望自己的规则在Vim运行时的规则之前(这是最常见的情况),还是在之后。
由于我认为这是kyov/purify
中的一个错误,所以我打开了一个pull请求,该请求将C和C++的规则移动到after/
目录中。在本地测试它,它似乎解决了您上面报告的问题,而没有真正引入任何不利问题。(Pull请求被迅速合并,所以作者似乎同意这是一个错误。)