TL;DR: vim seems to be sourcing both indent/javascript.vim
and indent/html.vim
on editing html files; is this intentional or a bug? How can I make html files only source html.vim
?
Recently I found out that vim seems to be using indent filetype plugins for both javascript and html on editing html files, and I've done some testing based on this behaviour on minimal vim configurations.
Here is my one-line .vimrc
:
filetype plugin indent on
Inside my .vim
directory:
~ % tree .vim
.vim
└── indent
├── html.vim
└── javascript.vim
1 directory, 2 files
Where:
~ % cat .vim/indent/javascript.vim
setlocal formatprg=js-beautify
let g:testvar_js="js testvar"
let g:testvar="testvar defined in javascript.vim"
and
~ % cat .vim/indent/html.vim
setlocal formatprg=html-beautify
let g:testvar_html="html testvar"
let g:testvar="testvar defined in html.vim"
Then I open up a new, empty vim buffer with vim foo.html
, and tested with some commands:
: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
As if vim sources both indent filetype plugins, with indent/html.vim
first and then indent/javascript.vim
.
Therefore, my questions are:
html.vim
when editing html files?Some additional information that might be helpful:
ftplugin/
, only indent/
indent/html.vim
: variables defined in indent/html.vim
are all undefined in a javascript bufferformatprg
of html files is always js-beautify
on open, regardless of if there are any javascript code pieces or <script>
tags inside that html fileindent/css.vim
will not be involved at all when editing html - I've testedjs-beautify
and html-beautify
are two separate executables (repository is here)
bin % ls -n js-beautify
lrwxr-xr-x 1 501 80 53 Apr 19 17:59 js-beautify -> ../lib/node_modules/js-beautify/js/bin/js-beautify.js
bin % ls -n html-beautify
lrwxr-xr-x 1 501 80 55 Apr 19 17:59 html-beautify -> ../lib/node_modules/js-beautify/js/bin/html-beautify.js
If you want me to do some additional tests or need more information, just shout.
Many thanks
Here is a perfectly valid HTML sample:
<!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>
You will notice it has a tiny bit of embedded JavaScript in it, which is a good enough reason for $VIMRUNTIME/indent/html.vim
to source $VIMRUNTIME/indent/javascript.vim
. After all, the javascript
indent script is supposed to know how to indent JavaScript, so why not use it in a html
buffer that can contain embedded JavaScript?
FWIW, here is the snippet responsible for that behaviour:
if !exists('*GetJavascriptIndent')
runtime! indent/javascript.vim
endif
Note that the maintainers of $VIMRUNTIME/indent/html.vim
chose the external route for javascript
and the internal one for css
. Maybe because $VIMRUNTIME/indent/css.vim
didn't fit the bill? I don't know and, frankly, I don't think it matters.
Now, let's go through your mistakes…
Filetype-specific scripts (indent, syntax, ftplugins) are sourced in this order:
~/.vim/indent/<filetype>.vim
,$VIMRUNTIME/indent/<filetype>.vim
~/.vim/after/indent/<filetype>.vim
If you are not very careful, stuff you put in an earlier script might be overwritten when a later script is sourced. For that reason, it makes a lot more sense to put your own stuff in scripts under after/
.
The following lines have nothing to do in indent scripts:
setlocal formatprg=js-beautify
setlocal formatprg=html-beautify
They are supposed to be in ftplugins:
" after/ftplugin/javascript.vim
setlocal formatprg=js-beautify
" after/ftplugin/html.vim
setlocal formatprg=html-beautify
So…
Did I make any silly mistakes?
Yes, see above.
If no, then is this an intentional design, a bug, or is that vim has nothing to do with this at all?
Well yes, this is an intentional design that works pretty well. It only caused problems because you misused it.
Is there a way to make vim only source on
html.vim
when editing html files?
indent/html.vim
? Yes, it certainly is possible but why would you want to do that?ftplugin/html.vim
? It already works the way you want and it is the right place for the things you mistakenly put in indent/html.vim
to begin with.--- EDIT ---
Just curious,
indent/
files are supposed to set indentation options right, then why shouldn't I set the indentation program there?
Filetype-specific scripts are typically sourced once, when a file of the corresponding filetype is loaded into a buffer. Because it is relatively common to have languages embedded in other languages (JavaScript in HTML) or languages that are supersets of other languages (C++ vs C), Vim makes it possible to source other filetype-specific scripts. That's pretty much a concrete example of code reuse and that's generally considered a good thing.
Indent scripts can source other indent scripts, syntax scripts can source other syntax scripts, and ftplugins can source other ftplugins.
So Vim gives us a useful low-level mechanism but it is up to us to decide what to put where, and that always depends on the context.
In the case of HTML, it makes sense to use the existing JavaScript indent stuff, so $VIMRUNTIME/indent/html.vim
sources $VIMRUNTIME/indent/javascript.vim
early on and then proceeds with setting HTML-specific stuff. The end result is a html
indent script that also supports embedded JavaScript. The html
syntax script uses a similar mechanism in order to highlight embedded JavaScript. In some simple cases, you can even have one ftplugin sourcing another ftplugin but $VIMRUNTIME/ftplugin/html.vim
doesn't.
But it doesn't always makes sense: options may be overwritten, mappings may be overwritten or defined in contexts where they don't make sense, etc. In this specific case, what external tool to use for formatting is highly context-sensitive: you can't really expect js-beautify
to format HTML properly or html-beautify
to format JavaScript properly so formatprg
must be set separately for the javascript
and html
filetypes.
ANd this is where your first mistake kicks in.
Here is once again the snippet that sources $VIMRUNTIME/indent/javascript.vim
from $VIMRUNTIME/indent/html.vim
:
if !exists('*GetJavascriptIndent')
runtime! indent/javascript.vim
endif
:help :runtime
is a smart alternative to :help :source
that looks for files in :help 'runtimepath'
. Because your ~/.vim/indent/javascript.vim
is in your runtimepath
, it will be sourced. Because there is a !
, every matching file is going to be sourced. Because it comes first in runtimepath
, it might be overwritten by later scripts.
In your case, $VIMRUNTIME/indent/html.vim
automatically sources your ~/.vim/indent/javascript.vim
, which contains stuff that shouldn't be set in a html
buffer.
The after
directory allows you to have the last word on what is set for a given filetype because built-in scripts rarely, if ever, do runtime! after/indent/<filetype>.vim
That explains why it is a bad idea to carelessly put your filetype-specific stuff in ~/.vim/{ftplugin,indent,syntax}/
and why you should put it in ~/.vim/after/{ftplugin,indent,syntax}/
instead.