Indenting Python with VIM

Vim’s default configuration for Python sucks! If you have used Vim to write Python code, you probably know this very well. It does not align to parentheses and braces.

dict(foo=bar<return>
        _ <= directly insert a 'tab'

{'hello': 'world',<return> _ <= Same thing!

It doesn’t add indentation after Python keyword

if True:<return>
_ <= Come on! I don’t want to hit 'tab' every time I open a block!

Not even mentioning insanities like: insert tabs instead of spaces, or tab width is 8 spaces. One can say it is a matter of taste; but it is generally accepted that Python code should have a 4 spaces indentation, as stated by PEP 8.

Making Vim PEP-8 friendly

Sane defaults

First I would like to point out something when writing a Vim configuration file.

Generally don’t use the autocmd command to add hooks to a specific filename’s extension. AutoCmd ... *.py ... might look like a good way of executing commands when opening a Python file; it’s not. Because all Python filenames do not necessarily end with .py; some executable scripts might not have extension for example.

autocmd FileType python is better. But there is an even better way: ftplugin. For instance, instead of the following in your vimrc

AutoCmd BufNewFile,BufRead *.py
    \ setlocal tabstop=4
    \ setlocal softtabstop=4
    \ setlocal shiftwidth=4
    \ setlocal textwidth=80
    \ setlocal smarttab
    \ setlocal expandtab

Create the directory ~/.vim/ftplugin; create a file named ~/.vim/ftplugin/python.vim containing:

setlocal tabstop=4
setlocal softtabstop=4
setlocal shiftwidth=4
setlocal textwidth=80
setlocal smarttab
setlocal expandtab

Now every files detected as Python files by Vim get the previous commands executed. Even those with filenames which do not end with .py.

Sexy edition

Now let’s tackle the edition problem. The solution lies in this script written by Eric Mc Sween. Download it and drop it in the ~/.vim/indent directory. You might have to rename the script to python.vim. Restart Vim and rejoice! This script intends to make Vim behave the right way when editing, and is largely successful. Editing Python code is now a real pleasure.

It aligns the cursor to parentheses and braces. It indents just after if, for, while and so forth. Most problems with the default Vim indentation are solved.

An additional tip about comments

The way comments are indented is still tedious; even when using the indent script.

def function(foo, bar):
    if foo:
# Some useful comment
        if bar:
            print 'foo bar'
        else:
# Another useful comment
            print 'foo'

This looks more reasonable.

def function(foo, bar):
    if foo:
        # Some useful comment
        if bar:
            print 'foo bar'
        else:
            # Another useful comment
            print 'foo'

To get this working properly, edit the indentation script ~/.vim/indent/python.vim. And replace

setlocal indentkeys=!^F,o,O,<:>,0),0],0},=elif,=except

with

setlocal indentkeys=!^F,o,O,<:>,0),0],0},=elif,=except,0#

With this modification, when pressing the # key, comments are indented correctly.

Update, Clay Gerrard noted that the solution previously described here doesn’t work on Vim 7. To get it to work, remove the set smartindent from your Vim configuration file, or better add set nosmartindent in your ~/.vim/ftplugin/python.vim.

A final note

Make sure filetype plugin and indentation are enabled. It is deactivated by default on many operating systems. Ubuntu and Debian deactivate indentation and OpenBSD deactivates both! To make sure everything is activated type :filetype plugin indent on.

Don’t forget to put the following line in your vimrc to make sure file type plugin and indentation are enabled when you start Vim.

filetype plugin indent on

Update: the link to Eric’s script was broken, it is OK now. Many Thanks to Robert for pointing this out!