My development toolbox

A recent discussion on Twitter has prompted me to write about the toolset I'm using during development, covering both work-related and personal projects. I will not just provide names and links, but also explain how exactly I use every tool.

OS and Hardware

During the last 14 years, I've been nearly exclusively a GNU/Linux user, and my go-to distribution is Fedora, I also occasionally use RHEL, CentOS (Stream) and Debian (Stable). My desktop environment is MATE, a fork of Gnome 2, although I keep pondering switching to a lightweight tiling window manager.

I'm a long-term user of Lenovo Thinkpad laptop series, currently on X1 Gen 7 (which does have a few annoying glitches compared to Gen 8). Connected to my Thunderbolt dock station are one high(er)-end full HD monitor from Philips, an ergonomic keyboard (Perixx PERIBOARD-512), an ergonomic mouse (Perixx PERIMICE-508), a simple headset and a full HD web camera, both from Logitech.

For occasional music recordings I use a Focusrite 2i4 external audio interface. (I don't provide a link, because Focusrite decided to completely wipe all mentions of it from their web site once they discontinued the model.)

Editing: ViM

I am on team ViM for the last 7 years after a year of using Emacs and several more years with Sublime. I must admit I'm still learning how to get the most out of it. I typically use a two-pane layout, here is an example with this blog post (in progress) and a bit of MiniScript:

ViM with two panes

My vimrc is not the largest I've seen, but it does list a few plugins and some non-default settings and short-cuts.

Plugins

syntastic

Automatically compiles source code or runs linters on it, and displays errors in a useful ways. I use it with Python (Flake8), Go and Rust (compilation and formatting). My settings currently are:

let g:syntastic_always_populate_loc_list = 1
let g:syntastic_auto_loc_list = 1
let g:syntastic_check_on_open = 1
let g:syntastic_check_on_wq = 0
let g:syntastic_aggregate_errors = 1
let g:syntastic_python_checkers = ['flake8']
let g:syntastic_python_flake8_args = "--ignore=W503,E129,H238"
let g:syntastic_rst_checkers = []
let g:syntastic_rust_checkers = ['rustc']
let g:syntastic_rust_rustc_exe = 'cargo check'
let g:syntastic_rust_rustc_fname = ''
let g:syntastic_rust_rustc_args = '--'
highlight SyntasticErrorSign guifg=white guibg=red

Note

W503 contradicts PEP8 and should be disabled in any modern project.

Note

While I like the idea of a single blessed format for a language, I'm of the strong opinion that it has to come with the language itself and must be tied to its version. This is why I use go fmt and rust fmt but avoid Black.

vim-visual-multi

Multiple cursors (and more) for ViM, Sublime users know what I'm talking about. I tend to use it more often than not even for operations that have native ViM counterparts, like replacing occurences of the same word. The only customizations I have are mappings to avoid using arrow keys (you don't use arrow keys with ViM/Emacs, do you?):

let g:VM_maps = {}
let g:VM_maps["Select Cursor Down"] = '<C-j>'
let g:VM_maps["Select Cursor Up"]   = '<C-k>'
let g:VM_maps["Select l"]   = '<C-l>'
let g:VM_maps["Select h"]   = '<C-h>'

The first two commands create or remove an additional vertical cursor below or above the current cursor, the last two add text to the right or to the left to a cursor.

YouCompleteMe

An advanced auto-completion engine supporting many programming languages. Offers fuzzy search, which I'm a big fan of. It did take some time to tune auto-completion pop-up to work exactly the way I want, the current configuration is:

set completeopt=preview,menuone,noselect,noinsert
inoremap <expr> <CR>  pumvisible() ? "\<C-y>\<CR>" : "\<CR>"
let g:ycm_autoclose_preview_window_after_completion = 1

This prevents the completion from interfering with my input if I decide to ignore it (which I often do, especially when writing a text like this).

A downside of this plugin is that it requires compiling an external server, which crashes from time to time. It also does not work with ViM 7, so to support RHEL/CentOS I need to do

if v:version > 800
    Plugin 'ycm-core/YouCompleteMe'
endif
python-mode, rust.vim and vim-go

No big surprises here, these plugins add additional support for Python, Rust and Go languages accordingly.

Go highlighting needs some tuning to my liking:

let g:go_highlight_fields = 1
let g:go_highlight_functions = 1
let g:go_highlight_function_calls = 1
let g:go_highlight_extra_types = 1
let g:go_highlight_operators = 1

Python configuration needs some tuning as well:

let g:pymode = 1
let g:pymode_indent = 1
let g:pymode_lint = 0
let g:pymode_breakpoint_bind = '<Leader>B'
let g:pymode_rope = 1

(I'm enabling Rope integration and disabling features that conflict with other plugins and settings).

fzf and fzf.vim

This is a true pearl of my toolbox! FZF is a fuzzy finder for… anything! If you just run it from the command line, it will start fuzzy search of files in the current directory. But together with ViM it opens amazing opportunities! Check out these short-cuts (the leader key is \`):

" Search for files in the current git repository
map <Leader>f :GFiles<CR>
" Search for open buffers
map <Leader>b :Buffers<CR>
" Search for lines in the output of the_silver_searcher
map <Leader>a :Ag<CR>
" Search for lines in open buffers
map <Leader>l :Lines<CR>
" Search for lines in the current buffer
map <Leader>s :BLines<CR>

All searches are fuzzy, which I am personally really fond of! It's also surprisingly fast.

vim-commentary

Commands for file type dependent commenting and uncommenting of lines.

ReplaceWithRegister

Command for replacing text with the content of a register.

Note

Deleting and cutting operations in ViM modify the default register (clipboard in GUI terminology), so combining them with a paste operation yields surprising results.

vim-wordmotion

This is a recent and controversial addition to my toolbox. Together with this bit of configuration:

let g:wordmotion_uppercase_spaces = ['(', ')', "'", '"', ',', '.', '[', ']']

it makes lower-case word motions stop at underscores and camel-case words, while the upper-case motions work more or less as normal motions in ViM.

vim-swap

Commands for swapping arguments in a function call or a signature. Addresses the annoyingly common need of turning

def foo(b, a):

into

def foo(a, b):

(yes, I used this plugin right now to create these examples).

Vundle

A plugin system for enabling the described plugins.

Settings

Some settings have not been covered above:

" Tabs and spaces (with an exception for YAML)
set tabstop=8
set expandtab
set softtabstop=4
set shiftwidth=4
autocmd BufEnter *.yml setlocal softtabstop=2
autocmd BufEnter *.yml setlocal shiftwidth=2
autocmd BufEnter *.yaml setlocal softtabstop=2
autocmd BufEnter *.yaml setlocal shiftwidth=2

" Enable line numbers
set number

" Configure text width (with an exception for Rust)
set textwidth=79
set colorcolumn=80
autocmd BufEnter *.rs setlocal textwidth=100
autocmd BufEnter *.rs setlocal colorcolumn=101

" Highlight trailing whitespace with red
autocmd BufEnter * highlight BadWhitespace ctermbg=red guibg=red
autocmd BufEnter * match BadWhitespace /\s\+$/

" Jump to current search results
set incsearch

" Keep more context when scrolling
set scrolloff=5

" Do not unload hidden buffers (prevents annoying requests to save changes
" on switching buffers
set hidden

Convenient short-cuts:

" Navigating the error list
map [f :lfirst<CR>
map [n :lnext<CR>
map [p :lprev<CR>
map [l :llast<CR>

" Inserting dates in ISO format
map <Leader>di i<C-R>=strftime("%Y-%m-%d %H:%M:%S UTC%z")<CR><Esc>hi:<Esc>ll
map <Leader>da a<C-R>=strftime("%Y-%m-%d %H:%M:%S UTC%z")<CR><Esc>hi:<Esc>ll

Shell

I spent most of my professional life in a shell. I'm using Fish, which is a modern shell with usable defaults and nice built-in features. My fish config includes an alias from vim to vimx (when present) and a custom prompt.

The tools I routinely use include:

ag

I've already mention ag aka the_silver_searcher when talking about ViM plugins, but I also actively use it as a replacement for grep. Its benefits include:

  • Understanding .gitignore files.

  • Extended regular expressions by default.

  • Recursive search by default.

  • Nice search highlighting.

bat

is an alternative to less with syntax highlighting, git integration, and other small improvements. The only thing that prevents me from aliasing less to bat is that the former can deal with files containing occasional binary data (QEMU console dumps are one example).

git

does not need an introduction! My gitconfig adds quite a few aliases, some of them inspired by SVN (remember it?):

[alias]
        ci = commit
        co = checkout
        dc = diff --cached
        ff = pull --ff-only
        st = status --short --branch
        top = log -1 --stat
        topp = log -1 --stat -p
        l = log --decorate
        ll = log --graph --all --decorate --oneline
        la = log --graph --all --decorate --stat
        lbr = branch --sort=-committerdate --verbose

git ll is a much useful version of git log, while git lbr helps me navigating the surprisingly high number of branches in some repositories (like ironic).

I also change a couple of defaults:

[push]
        default = upstream
[checkout]
        defaultRemote = origin
[core]
        excludesfile = ~/.gitignore

The latter references a custom gitignore:

*.swp
.ropeproject/
tmux

The battle-tested screen alternative probably does not require an introduction as well. I have a habit of using it on all remote servers I access.

Ansible

I'm using Ansible as part of my involvement in the Bifrost project, but I'm also relying on it for configuring my development environment (as described above) on remote machines and testing VMs. My configuration playbook is capable of installing packages, checking out repositories and even configuring ViM! I cannot recommend this approach enough, it makes me feel at home no matter if I'm on my laptop or in one of the numerous VMs I create.