c-在Vim中被标记为语法错误的花括号

  • 本文关键字:错误 语法 记为 Vim c vim
  • 更新时间 :
  • 英文 :


我使用的是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;
}

我很困惑为什么这会显示为一个错误。如果我把开头的大括号放在主定义的末尾或后面,就会发生这种情况。如果我运行clistvim,则会告诉我没有错误。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匹配其中一个,或者cParenstart完全匹配(),它将选择cParen(来自Vim运行时),因为这是稍后发生的。

没关系。。。但为什么)会出现问题呢?

由于cParen是一个"区域",Vim将继续尝试匹配其中的end部分。在这样做的同时,它还将尝试匹配contains中的组。但在这里,containsALLBUT开头,这意味着它将尝试匹配除显式列表中的组之外的所有组。这意味着它也将匹配该组内的cParens

现在,这就是问题的原因,因为如果cParens)匹配,那么这意味着)将不会被识别为cParen的末尾。这实际上是在:help :syn-keepend:中描述的

默认情况下,包含的匹配可以掩盖结束模式的匹配。这对嵌套很有用。例如,以{开始、以}结束的区域可以包含另一个区域。遇到的}将结束包含的区域,但不结束外部区域。

如果您不希望这样,keepend参数将使外部区域的结束模式的匹配也结束任何包含的项。这使得不可能嵌套相同的区域,但允许包含的项目高亮显示结束模式的部分,而不会导致跳过与结束模式的匹配。

因此,如果我们在Vim运行时的区域规则上有keepend,这实际上会结束cParen规则,这不会导致问题。但事实并非如此,使cParens)匹配意味着cParen将一直运行到文件末尾,这将导致大括号触发错误(因为在C中,括号内不允许大括号。)

如果我们确实keepend,情况会稍微好一点,因为cParen会结束。但这仍然不是理想的,因为kyov/purifycParens只匹配),而不会真正匹配(。(您可能需要类似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请求被迅速合并,所以作者似乎同意这是一个错误。)

最新更新