I have created a few scripts in .vim/ftdetct to e.g. detect the filetype of sshconfig files based on their filename:

autocmd BufRead,BufNewFile ~/.ssh/host-* setfiletype sshconfig

Now, how can I make Vim detect the filetype based on the shebang (#!) line in files, e.g.:

#!/usr/bin/awk -f

If you want to introduce a file type and that file type can only be identified by examining the contents of such files then you'll need to add appropriate logic to a file named scripts.vim.

But first, let's take a step back and look at the broader picture.

File type detection is enabled with the command :filetype on (this is usually added to your vimrc file). When this command is run Vim will source the script named filetype.vim found in the $VIMRUNTIME directory. This sets up a bunch of au BufNewFile,BufRead autocommands including this one:

au BufNewFile,BufRead *
    \ if !did_filetype() && expand("<amatch>") !~ g:ft_ignore_pat
    \ | runtime! scripts.vim | endif

Every time a file is opened or read for the first time these auto commands will be run. When detection is successful for a file, application state will be changed such that the return value of did_filetype() is true when that file is in the current/active buffer.

The auto command pasted in above is located fairly deep into filetype.vim as it is preceded by the various filename-based file type detections which need to run first. If none of those identify the file type then scripts.vim gets its turn. Note that the :runtime command means that ALL files named scripts.vim appearing in one of the paths defined in 'runtimepath' will be sourced/executed. There should always be an instance of this file in the $VIMRUNTIME directory. For additional file types you'll need to add logic to a local instance. The normal place to put it is in your personal Vim folder (e.g. on *nix that's usually $HOME/.vim).

A word on the ordering of the paths in 'runtimepath': by default, the paths will start with $HOME/.vim (or non-Unix equivalent) followed by the system-level config in $VIM/vimfiles (e.g. /usr/share/vim/vimfiles) then $VIMRUNTIME (e.g. /usr/share/vim/vim82). (If you use a plugin-manager it will usually add one path per plugin after $HOME/.vim.) The important thing is that your local file type detection takes precedence over anything else.

If scripts.vim doesn't exist locally you'll want to populate it with something like this:

if did_filetype()   " filetype already set..
  finish        " ..don't do these checks

if getline(1) =~ '^#!/usr/bin/awk -f'
  setfiletype yourfiletype

If scripts.vim already exists with similar logic in an existing if block then insert an elseif near the end.

" Existing two conditionals...
if {condition1}
  setfiletype filetype1
elseif {condition2}
  setfiletype filetype2
" Add these two lines
elseif getline(1) =~ '^#!/usr/bin/awk -f'
  setfiletype yourfiletype

See also :h new-filetype-scripts and :h filetypes

  • 1
    Thanks. Didn't knew that. Is scripts.vim a special name? Could I choose any other name for the file? Would it be possible to add that code to a file in ftdetect to group all file-detection related files?
    – Shuzheng
    Jun 23 at 13:35
  • @BLayer You should probably include the explanation of scripts.vim (special filename, where it's searched for, runs with buffer loaded, etc.) as part of the answer...
    – filbranden
    Jun 23 at 14:59
  • 1
    @filbranden Fair enough.
    – B Layer
    Jun 23 at 22:49
  • 1
    The additions I made may convince you that you should stick with 'scripts.vim'.
    – B Layer
    Jun 23 at 23:34
  • 1
    @Shuzheng No worries! Thanks. BTW, I added a lot of into to the answer after your last comment of 6/23. Not sure if you saw that so FYI.
    – B Layer
    Jun 28 at 1:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.