Vim 在编辑 html 文件时意外地使用 javascript 缩进文件类型插件

Vim unexpectedly sources javascript indent filetype plugin on editing html files

TL;DR:vim 似乎在编辑 html 文件时同时采购 indent/javascript.vimindent/html.vim;这是故意的还是错误?我怎样才能使 html 文件仅源 html.vim?


最近我发现 vim 似乎在编辑 html 文件时对 javascript 和 html 使用缩进文件类型插件,我做了一些基于此行为在最小 vim 配置上进行测试。

这是我的单行.vimrc:

filetype plugin indent on

在我的 .vim 目录中:

~ % tree .vim
.vim
└── indent
    ├── html.vim
    └── javascript.vim

1 directory, 2 files

其中:

~ % cat .vim/indent/javascript.vim
setlocal formatprg=js-beautify
let g:testvar_js="js testvar"
let g:testvar="testvar defined in javascript.vim"

~ % cat .vim/indent/html.vim      
setlocal formatprg=html-beautify
let g:testvar_html="html testvar"
let g:testvar="testvar defined in html.vim"

然后我用 vim foo.html 打开一个新的空 vim 缓冲区,并用一些命令进行测试:

:set filetype?
  filetype=html
:set formatprg?
  formatprg=js-beautify
:echo g:testvar
testvar defined in javascript.vim
:echo g:testvar_html
html testvar
:echo g:testvar_js
js testvar

好像 vim 来源两个缩进文件类型插件,首先 indent/html.vim 然后 indent/javascript.vim

因此,我的问题是:

一些可能有用的附加信息:

如果你想让我做一些额外的测试或需要更多信息,请大声喊叫。

非常感谢

这是一个完全有效的 HTML 示例:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Sample</title>
        <script>
            console.log('Hello, World!');
        </script>
        <style>
            body {
                background: orange;
            }
        </style>
    </head>
    <body>
        <h1>Sample</h1>
    </body>
 </html>

您会注意到其中嵌入了一点点 JavaScript,这是 $VIMRUNTIME/indent/html.vim 来源 $VIMRUNTIME/indent/javascript.vim 的充分理由。毕竟,javascript 缩进脚本应该知道如何缩进 JavaScript,所以为什么不在 可以 包含的 html 缓冲区中使用它呢?嵌入式 JavaScript?

FWIW,这是导致该行为的代码片段:

if !exists('*GetJavascriptIndent')
  runtime! indent/javascript.vim
endif

请注意,$VIMRUNTIME/indent/html.vim 的维护者为 javascript 选择了外部路由,为 css 选择了内部路由。也许是因为 $VIMRUNTIME/indent/css.vim 不符合要求?我不知道,坦率地说,我认为这不重要。

现在,让我们回顾一下你的错误……

  • Filetype-specific 脚本(缩进、语法、ftplugins)按以下顺序获取:

    1. ~/.vim/indent/<filetype>.vim,
    2. $VIMRUNTIME/indent/<filetype>.vim
    3. ~/.vim/after/indent/<filetype>.vim

    如果您不小心,您放入较早脚本中的内容可能会在获取较晚脚本时被覆盖。出于这个原因,将您自己的东西放在 after/.

    下的脚本中更有意义
  • 以下几行在缩进脚本中无所事事

    setlocal formatprg=js-beautify
    setlocal formatprg=html-beautify
    

    它们应该在 ftplugins 中:

    " after/ftplugin/javascript.vim
    setlocal formatprg=js-beautify
    
    " after/ftplugin/html.vim
    setlocal formatprg=html-beautify
    

所以……

Did I make any silly mistakes?

是的,见上文。

If no, then is this an intentional design, a bug, or is that vim has nothing to do with this at all?

嗯,是的,这是一个非常有效的有意设计。它只是因为你误用了它才导致问题。

Is there a way to make vim only source on html.vim when editing html files?

  • indent/html.vim?是的,这当然是可能的,但你为什么要这样做?
  • ftplugin/html.vim?它已经按照你想要的方式工作,而且它是你错误地放入 indent/html.vim 开始的东西的正确位置。

--- 编辑 ---

Just curious, indent/ files are supposed to set indentation options right, then why shouldn't I set the indentation program there?

Filetype-specific 脚本通常在相应文件类型的文件加载到缓冲区中时获取一次。因为将语言嵌入其他语言(JavaScript in HTML)或其他语言的超集(C++ vs C)的语言是相对常见的,Vim 使得从其他语言中获取资源成为可能filetype-specific 个脚本。这几乎是代码重用的具体示例,通常被认为是一件好事。

缩进脚本可以作为其他缩进脚本的源,语法脚本可以作为其他语法脚本的源,ftplugins 可以作为其他 ftplugins 的源。

因此 Vim 为我们提供了一个有用的 low-level 机制,但由我们决定将什么放在哪里,这始终取决于上下文。

在 HTML 的情况下,使用现有的 JavaScript 缩进内容是有意义的,因此 $VIMRUNTIME/indent/html.vim 尽早获取 $VIMRUNTIME/indent/javascript.vim 然后继续设置 HTML-specific 东西。最终结果是一个 html 缩进脚本,它也支持嵌入 JavaScript。 html 语法脚本使用类似的机制来突出显示嵌入的 JavaScript。在一些简单的情况下,您甚至可以让一个 ftplugin 采购另一个 ftplugin,但 $VIMRUNTIME/ftplugin/html.vim 不会。

但它并不总是有意义:选项可能会被覆盖,映射可能会被覆盖或在它们没有意义的上下文中定义,等等。在这个特定的在这种情况下,用于格式化的外部工具是高度 context-sensitive:您不能真正期望 js-beautify 正确格式化 HTML 或 html-beautify 正确格式化 JavaScript因此必须为 javascripthtml 文件类型分别设置 formatprg

这就是你犯第一个错误的地方。

这里再次显示了 $VIMRUNTIME/indent/javascript.vim 来自 $VIMRUNTIME/indent/html.vim 的片段:

if !exists('*GetJavascriptIndent')
  runtime! indent/javascript.vim
endif

:help :runtime:help :source 的智能替代品,它在 :help 'runtimepath' 中查找文件。因为您的 ~/.vim/indent/javascript.vim 在您的 runtimepath 中,所以它会被获取。因为有一个 !,每个匹配的文件都将被获取。因为它在 runtimepath 中排在第一位,所以它可能会被后面的脚本覆盖。

在您的情况下,$VIMRUNTIME/indent/html.vim 会自动获取您的 ~/.vim/indent/javascript.vim,其中包含不应在 html 缓冲区中设置的内容。

after 目录允许您对给定文件类型的设置有最终决定权,因为 built-in 脚本很少(如果有的话)执行 runtime! after/indent/<filetype>.vim

这就解释了为什么不小心把你的 filetype-specific 东西放在 ~/.vim/{ftplugin,indent,syntax}/ 里是个坏主意,为什么你应该把它放在 ~/.vim/after/{ftplugin,indent,syntax}/ 里。