I've been working on a Python project for some time now. In the beginning, it wasn't very comfortable, as I was trying to learn the language and figure out my workflow and the tooling.

After a couple of months, I now finally have a working Vim setup with proper syntax highlighting, smart code completion, and refactoring capabilities. Read on to see what it looks like.

Disclaimer: My setup may seem like a bit too much. You don't have to install every plugin listed in this article or copy every configuration line. Pick up what you like.

Here's How My Vim Looks Like

TL;DR:

Basics: vim-plug, scrooloose/nerdtree, tpope/vim-commentary and junegunn/fzf.vim

numirias/semshi is the best for the syntax highlighting

Vimjas/vim-python-pep8-indent for proper indenting

dense-analysis/ale is an asynchronous linter plugin. Use it with flake8 and pylint; plus google/yapf as a formatter.

neoclide/coc.nvim with neoclide/coc-python for intellisense code completion

Essentials

Let's start with a list of some general-purpose plugins which I find irreplaceable for any language.

vim-plug is a minimalistic plugin manager

scrooloose/nerdtree to navigate the file tree

junegunn/fzf.vim fuzzy search through the files (and much more, really)

tpope/vim-commentary ( or scrooloose/nerdcommenter ) — press gcc to comment out a line or gc to comment a selection in visual mode

to comment out a line or to comment a selection in visual mode liuchengxu/vista.vim which is a "tagbar" that learns from LSP servers

The other plugins I use include

jeetsukumaran/vim-pythonsense provides some Python-specific text objects: classes, functions, etc

jiangmiao/auto-pairs inserts closing quotes and parenthesis as you type

The colorscheme used on the screenshots is joshdick/onedark.vim, which is inspired by the Atom theme.

Syntax highlighting

Vim comes with syntax highlighting for many popular languages, including Python, though it is not always the best one.

There are several options to improve the default highlighting.

numirias/semshi, in my opinion, is the best. It works with Neovim only and requires the Python 3 support.

sheerun/vim-polyglot includes support for many languages including Python

python-mode/python-mode is also a decent one although it comes with a lots of other stuff beside highlighting which I don't quite like

Semshi (on the left) vs. the default one

My favorite color schemes (which I switch quite often) include junegunn/seoul256.vim and joshdick/onedark.vim (the one on the screenshots).

Indentation

You can set up indentation rules manually like this.

au BufNewFile,BufRead *.py \ set expandtab |" replace tabs with spaces \ set autoindent |" copy indent when starting a new line \ set tabstop=4 \ set softtabstop=4 \ set shiftwidth=4

A better alternative is Vimjas/vim-python-pep8-indent plugin. It does a much better job complying with the PEP8 style guide.

Folding

Folding ( :help foldmethod ) is when you collapse chunks of code to eliminate distraction.

The best approximation is to use the folding method indent though it doesn't work ideally.

au BufNewFile,BufRead *.py \ set foldmethod=indent

To toggle a fold you can press za ( :help fold-commands ), and I have it mapped to Space for convenience.

nnoremap <space> za

Linting & Fixing

The fantastical dense-analysis/ale plugin can be used for linting (which essentially means checking for syntax errors) and auto-fixing extremely well. It's asynchronous, meaning that it won't block the UI while running an external linter, and it supports a great range of languages and tools.

Python + ALE = ❤️

ALE highlights problems with your code in the gutter. When you move the cursor to the problematic line, it shows the full error message at the bottom of the screen.

By default, ALE will use all linters (which are just executables) it could find on your machine. Run :ALEInfo to see which linters are available and which are enabled.

It is better though to explicitly specify which ones you're going to use with a particular filetype:

let g:ale_linters = { \ 'python': ['flake8', 'pylint'], \ 'ruby': ['standardrb', 'rubocop'], \ 'javascript': ['eslint'], \}

Some of the linters are also capable of fixing the problems in your code. ALE has a special command :ALEFix that fixes the whole file. So far, I'm only Google's YAPF as a fixer that formats the whole file when I press F10 or save the current buffer.

let g:ale_fixers = { \ 'python': ['yapf'], \} nmap <F10> :ALEFix<CR> let g:ale_fix_on_save = 1

The last option is a huge time saver — it will automatically fix (and thus format) your file on save.

I also have a little piece of configuration that shows the total number of warnings and errors in the status line. Very convenient.

function! LinterStatus() abort let l:counts = ale#statusline#Count(bufnr('')) let l:all_errors = l:counts.error + l:counts.style_error let l:all_non_errors = l:counts.total - l:all_errors return l:counts.total == 0 ? '✨ all good ✨' : printf( \ '😞 %dW %dE', \ all_non_errors, \ all_errors \) endfunction set statusline= set statusline+=%m set statusline+=\ %f set statusline+=%= set statusline+=\ %{LinterStatus()}

And here are a couple of alternatives to ALE:

vim-syntastic/syntastic very popular one but synchronous which can cause significant lags in UI

neomake/neomake asynchronous linting and make framework for Neovim/Vim (didn't try that one)

Jedi

Jedi is a "language server" (see my LSP article), a separate process running in the background and analyzing your code.

Other clients (editors or IDEs) can connect to the server and request some information, like completion options, or "go to definition" coordinates.

Basically, Jedi is an IDE as a service, without the GUI.

In order to use it, you need to install it with pip install jedi , and then also add a client. The davidhalter/jedi Vim plugin does a good job.

Here's what it can do:

Press ctrl + space for the completion options

for the completion options <leader>d goes to definition

goes to definition <leader>g goes to assignment

goes to assignment K shows the documentation

shows the documentation and more

Recently I have switched to neoclide/coc.nvim and coc-python. Coc is an "an intellisense engine" for Vim and Neovim and does a really good job when it comes to completion and communicating with language servers.

Coc (Conquer of Completion)

To me, the selling point is the usage of the new floating window API of Neovim, which makes it very convenient.

Coc-python can use both Jedi and the Microsoft's Python Language Server. The first time you run it, it will ask you to install the components it needs (like a linter).

Coc provides (with the help of Jedi) some basic IDE capabilities like

Going To definition

nmap <silent> gd <Plug>(coc-definition)

Displaying documentation (in the floating window!)

nnoremap <silent> K :call <SID>show_documentation()<CR> function! s:show_documentation() if (index(['vim','help'], &filetype) >= 0) execute 'h '.expand('<cword>') else call CocAction('doHover') endif endfunction

Smart rename (renames the exports across all files)

nmap <leader>rn <Plug>(coc-rename)

And of course the auto-complete menu (which you can see on the very first screenshot) appears as you type.

Basically, it's a very solid and comprehensive plugin which covers almost all of our needs.

Read to the wiki for more information about its capabilities.

The 2 drawbacks of using Coc are:

An expensive Node process that Coc needs to run in order to operate

It has its own independent extension system and its own (JSON-based) configuration file. Not a Vim-way.

Still, it does the job well, and I continue using it (until there's a better option).

Where to go next?

Here are some other articles I learned from a lot:

And some more articles in the similar vein from this website:

And...