【问题标题】:Curly braces marked as syntax error in Vim花括号在 Vim 中标记为语法错误
【发布时间】:2020-04-02 03:24:09
【问题描述】:

我正在使用 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: 在进一步挖掘中,问题似乎与我正在使用的颜色主题有关。有一个文件包含这个 for 语法:

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 的 syntax/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

谁能解释为什么会这样?

【问题讨论】:

  • 如果将{ 放在上一行的末尾会怎样?这可能是一种风格警告。
  • 无论我把牙套放在哪里,都不一样。
  • 您介意分享您的 vimrc 文件吗?
  • 当我的代码中有不匹配的东西或其他地方时,我会得到有趣的颜色效果。例如,如果在main() 之前的函数声明中有错误,您可能会看到奇怪的颜色。但是,如果您在显示的代码上看到它(并且文件中没有其他代码),那么您在 vimrc 设置中使用的点点滴滴可能有误——祝您调试好运。
  • @filbranden 设置删除了左花括号上的错误,但在右花括号上仍然显示错误。如果您能够理解问题所在,感谢您的帮助。

标签: c vim


【解决方案1】:

所以在我看来,这是 kyoz/purify 插件设置的语法规则中的一个错误,特别是关于与 Vim 运行时规则相关的规则排序。

当插件管理器添加插件时,它会将该插件的目录添加到'runtimepath',并将其添加到Vim 运行时目录之前。这样做是为了让插件有机会提前运行,从而覆盖 Vim 运行时中提供的文件。

特别是,这意味着当加载 C 类型的文件时,kyoz/purity 插件中的语法规则将被添加到之前来自 Vim 运行时的语法规则。

现在,事实证明这会引发规则之间如何交互的冲突。

例如,让我们检查 cParen(来自 Vim 运行时)和 cParens(来自 kyoz/purify)之间的交互。为简单起见,假设我们正在使用 let g:c_no_curly_error = 1,这样我们就不必讨论有两个end= 组,但同样的原理也适用于未设置该全局变量的情况。

根据当前的排序,我们最终得到cParens from kyoz/purifycParen from the Vim runtime 之前:

" 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 将使用 last 来的规则,这将始终只触发 cParens(,所以我们没有问题逃跑的群体。 ) 也只会匹配 cParens(在这种情况下,这与 cParen 不再冲突,因为它只会匹配组的末尾。)

这将使来自kyov/purify 的规则按预期工作。

但它确实有副作用!来自 Vim 运行时的 cParen 规则将完全被该插件所掩盖。 (还有匹配相同字符的类似规则,例如,cBlock

这是个问题吗?也许。似乎大多数规则都标记为transparent(它们不会直接影响突出显示),并且主要用于标记诸如标记不平衡或不匹配的大括号之类的情况,将它们标记为错误(类似于您所看到的,但实际上语法问题。)

如果您认为 kyov/purify 中的用例(用于为大括号着色)比 Vim 运行时中的用例更可取,那么您可以在 Vim 运行时中隐藏规则。

这可以通过将语法规则移动到after/ 子目录下来实现。插件管理器将识别插件中的after/ 目录,并将其添加到 Vim 运行时之后。这是允许插件决定他们是否希望他们的规则出现在 Vim 运行时的规则之前(这是最常见的情况)的既定机制,或者稍后出现。

因为我相信这是kyov/purify 中的一个错误,所以我打开了a pull request,它将C 和C++ 的规则移动到after/ 目录。在本地测试它,它似乎解决了您上面报告的问题,而没有真正引入任何不利问题。 (拉取请求很快被合并,所以作者似乎同意这是一个错误。)

【讨论】:

  • 这个@filbranden 做得很好,感谢您的详细解释。我学到了很多关于故障排除插件的知识。我看到您的 PR 已经被合并,并且现在在我的系统上表现得如预期。谢谢!
  • 现在我想知道包是在 vim 运行时之后还是之前 ?我从来没有注意到
  • @D.BenKnoble 同样在这里,直到这个问题我才真正知道。不幸的是,文档并没有明确说明这一点。我发现插件和包完全放在围绕 Vim 运行时,根在它之前,after/ 子目录在它之后。这是有道理的,因为您希望有机会从运行时覆盖某些东西,所以这种排序是有道理的......
  • @D.BenKnoble “现在我想知道包是在 vim 运行时之后还是之前”。我向question 询问了有关 Vi 和 Vim SE 的确切规则。希望我们现在能得到很好的记录。干杯!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
  • 2021-11-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多