Vim Tip #22: Vim Modelines

2019-05-08(Wed)

tags: Vim

Vim Tips

Vim has modes, with the most obvious ones being "Normal" and "Insert." Vim has a line at the bottom of the editor that shows you what mode you're in. And here we come to a naming problem: that is NOT the "modeline," or at least not the one I'm going to discuss in this blog entry. A modeline is quite different, and has nothing to do with that - I think it's a poor choice of name.

A "modeline" in Vim is a line of text in the file you're editing that is Vim code to set Vim's state for your editing session. I was reminded of their existence today while working on a Debian Apache server because the Apache configurations have this at the bottom of the file:

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

The hash mark ("#") begins an Apache comment, but Vim (under certain circumstances) will interpret this line as code when it opens the Apache config file. First, let's get into the settings associated with this:

:set modeline?

For me, this replies modeline. The other option is nomodeline, which would mean that modelines wouldn't be interpreted - but with my current settings they will. You should also ask your Vim this:

:set modelines?

For me, this replies modelines=5. This seems to be a very common default, and what it means is that if it finds a modeline (such as the one shown near the beginning of this entry) within five lines of the start or end of the file, it will execute it. So if you put a modeline in a file but it's more than five lines from the beginning or end, it will be ignored.

I don't know about you, but I have a major problem with the whole concept of "autorun." That's what this is: I opened a file (an Apache configuration) and my editor auto-ran a block of code to perform some settings. In this case, the settings were made by the Debian distribution and are quite safe, but if you have modeline set and you edit a malicious file, it could theoretically be a problem. And it's not actually all that theoretical: in a few minutes research I found that there have been at least four known security issues with this feature. So I've now added some settings to my ~/.vimrc:

" Modelines - vim settings embedded in files - are awesome ... until
" they're a danger.  Disable them:
set nomodeline
" toggle use of 'modeline' command.  Doesn't reload the
" settings until you do BufRead.  vimrc doesn't support the command
" separator '|' so we use '<bar>':
nnoremap <leader>ml :setlocal invmodeline <bar> doautocmd BufRead<cr>

If you're not familiar with the <leader> keyword, see Map Leader and Normal mode mappings. The first command turns 'modeline' off, but the 'ml' mapping makes it easy to toggle it back on after I've inspected the modeline and decided it's valid and acceptable. This is a practical solution for me because I rarely see them - but they're occasionally useful, particularly for an Apache configuration file (the problem there is that Vim's syntax system is often stuck on a file called ".conf" because what kind of configuration is it exactly ... the modeline answers that question).

Debian's Apache configuration files have the modeline at the end of the file, but the modelines directive allows it to be at the beginning as well. It's my intention going forward to place them at the beginning, so I can check them and then toggle their use if I'm okay with what the modeline looks like.

So what does that stuff mean? Let's take a look again:

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

:help modeline explains the layout of the line, the two possible (very similar) syntaxes, and even (surprisingly) offers some examples (that's better than most of Vim's help entries). There are a lot of small traps around writing these, but the most important things are that you need a space between the beginning comment tag and the word vim:, and that it appears you have to start the line with set and end it with : if you have double-ended comments (straight from the Vim help page - this is the "second syntax" and may not be compatible with very old versions of vi(m)):

/* vim: set ai tw=75: */

As for the pieces syntax=apache seems pretty clear. ts=4 would be tabstop - although that's tricky to look up, as :help ts will lead you to help on tselect which is a command rather than a setting. Use :help 'ts' instead. With that in mind, sw is shiftwidth, sts is softtabstop, sr is shiftround, and noet is noexpandtab - which I immediately changed to et as I'm a spaces kind of guy. The latter example seems a bit conflicted as set ai means set autoindent - commonly desirable for programming), and tw=75 which is textwidth=75, which is commonly used in plain text editing but never for programming ... Although I admit I've used the two together when taking notes occasionally.

I was disappointed to find that you can't specify a colorscheme with the modeline: you can only set options, and colorscheme isn't an option but a command. The best you can do is (as shown above) specify the filetype.