vim

The Ubiquitous Text Editor

(source: vim.org)

VK / 2020-09-17 (rev. 96952d)


Introduction

ubiquitous /juːˈbɪkwɪtəs/: present, appearing, or found everywhere

Vim is a highly configurable text editor built to make creating and changing any kind of text very efficient. It is included as “vi” with most UNIX systems and with Apple OS X. (vim.org)


xkcd#378: Real Programmers

xkcd#378: Real Programmers

  • while not as featureful as Emacs, has lots of features built-in, and provides very extensible platform for custom plugins & mods
  • by default almost everything is disabled
  • extremely lightweight compared to newer node.js based massive text editor frameworks

Maybe too lightweight by default?

By default vim is maybe even too simplified, and it has quite steep learning curve for newcomers. It can be improved also for more experienced users, as there is lots of tasks to be automated, new keybindings to be added etc.

Maybe we can add some good features that modern editor should have, to make vim much more usable, while keeping its low resource usage?

So, let’s see what we can do with some modifications.


Notes

Examples are based on the out-of-box experience (OOBE) on current Debian unstable/testing (future bullseye release). Some of the latter examples might depend on the customizations done on previous slides.

$ vim --version |head -n1
VIM - Vi IMproved 8.2 (2019 Dec 12, compiled May 12 2020 02:37:13)

vim is preinstalled on many systems, but you can install it easily, eg.

# Debian/Ubuntu and derivatives
sudo apt-get install vim
# Centos/Fedora/RHEL and derivatives
sudo dnf install vim

You can optionally use Docker for testing, docker run -it ypcs/tools:vim-latest has all tools and configs already installed for you.


vim 8.2 first start

First start using default settings (no file opened)


vim 8.2 editing file, default settings

Editing a file using default settings


vim 8.2 editing files, customized settings

Editing a file using some customizations


vim 8.2 editing files, more customized settings

Editing a file using lots of customizations (including: git status, syntax checking)


Different vim modes

In vim there is four different operating modes, and this is something that might confuse newcomers. Current mode isn’t even shown by default.

Mode Description How to enter
NORMAL Default, navigate inside a file. (“Read mode”) Esc
INSERT Edit/write: insert at cursor, at beginning of line iI
  insert after cursor, at end of line aA
  start new line before/after current oO
REPLACE Just replace everything as you type R
VISUAL Select characters / lines / blocks vV^V

You can switch from other modes to normal usually by hitting Esc.

Keyboard is your tool, but if you really want, :set mouse=a is there for some features…


vim command structure

vim commands consist of verb and noun. Commands can have a multiplier and commands can be repeated and chained.

Some examples:

  • i insert (verb) (at cursor) (noun)
  • d$ delete (verb) (to) end of line (noun)
  • 20k 20 times move up (verb) (a line) (noun)
  • /myword search (verb) myword (noun)
  • jj (twice) move down (verb) (a line) (noun)
  • j2kl move down, then two times up, and one time right
  • gv start visual mode at previous position

More about vim keys follows…


To quit vim…

To quit: you must be in normal mode (hit Esc) and then

  • :q quit
  • :q! quit, discard changes
  • :wq write changes and quit (:x write if there is changes, and quit)
  • :wq! write changes (with force) and quit
  • :qa quit all (tabs, split buffers etc.)
  • :cq quit, discard changes and use exit code 1

Also

  • :help for more tips

Stack Overflow, How to exit Vim, answer with +5100 votes…

There is some alternative ways to achieve somewhat similar results


Moving in the document

You must be in other mode than insert or replace.

  • h move one character left, l move one character right
  • w move one word right (to begin), e move one word right (to end), b move one word left
  • j move one line down, k move one line up
  • ( move one sentence left, ) move one sentence right
  • { move one paragraph left, } move one paragraph right
  • % jump to next matching item, parenthesis
  • /word search for word, n next match, d/word search and delete
  • : move to line
  • g; move to last edit

Editing the document

How to manipulate text. These apply either to current position of your line, or in visual mode your current selection.

  • y yank / copy
  • dw delete / cut a word, dd delete / cut a line, c cut rest of the line
  • p paste
  • u undo
  • < dedent
  • > indent
  • . repeat previous command

Basic configuration

You can either configure vim via ~/.vimrc or by configuring at runtime. Only changes made to ~/.vimrc will persist.

If you set runtime options, you need to switch to normal mode (usually hit Esc to exit other modes), and prefix the option with colon (:).

Eg. :set showmode to show current operating mode (caveat: normal doesn’t still show anything…)


Essential configuration

Some basic configuration to make life with vim much nicer.

" Show current mode
set showmode

" Set terminal title to current filename
set title

" Enable syntax highlighting
syntax on

" Always show ruler (current row/column etc.)
set ruler

" Security: Disable modelines
set modelines=0


Full example: https://ypcs.fi/misc/code/vim/vimrc.txt


… essentials, more

Some extra tuning for the basics.

" Wrap long lines
set wrap

" Show line numbering
set number

" Set default encoding
set encoding=utf8

" Wait 300ms until triggering plugins after text input stops
set updatetime=300

" Modify how backspace works (don't be as restrictive as by default)
set backspace=indent,eol,start


Pick up where we left off

You edit file, close the editor, do some tests maybe, and then decide that you need to modify same code again. What if vim could remember the last place you were in a file?

" Return to last edit position when opening files
autocmd BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") | exe "normal! g`\"" | endif

" Remember info about open buffers on close
set viminfo^=%
" Source: <https://stackoverflow.com/a/23036077>
"set viminfo=%,<800,'10,/50,:100,h,f0,n~/.vim/cache/.viminfo
"            | |    |   |   |    | |  + viminfo file path
"            | |    |   |   |    | + file marks 0-9,A-Z 0=NOT stored
"            | |    |   |   |    + disable 'hlsearch' loading viminfo
"            | |    |   |   + command-line history saved
"            | |    |   + search history saved
"            | |    + files marks saved
"            | + lines saved each register (old name for <, vi6.2)
"            + save/restore buffer list



Leader key

vim has special key, often denoted as <Leader> in configuration files. You can think this as custom namespace for your own keybindings, ie. some key that you press before actual command key(s). By default this is [, but that’s pretty awful key combination on non-US keyboard.

So, let’s remap that to ,.

" Map <Leader> key to , (default \),
" think this as a custom namespace for keybindings
let mapleader=","



Indentation

Quite often spaces are preferred over tabs, and you’d like to have SOME intelligence in how your editor handles indentation. Yet again, defaults need some tuning.

" Convert tabs to spaces
set expandtab

" Use 4 spaces as default indentation
set shiftwidth=4

" Set tabstop to 4 spaces
set tabstop=4

" Use same indent as previous line
set autoindent

" Enable smart indent (syntax/filetype specific)
set smartindent

" Fix invalid indents (round to nearest)
set shiftround



Search

By default there is no highlighting, search is case-sensitive and you can’t append to current search terms. Let’s change that.

" Highlight search terms
set hlsearch

" Ignore case when searching
set ignorecase

" ... but if pattern contains uppercase letters,
" then consider it case-insensitive
set smartcase

" Use incremental searching (move the highlight as you
" type in new characters to search)
set incsearch



Abbreviations

Good text editor is able to help you write correctly, eg. converts abbreviations to words, and maybe fixes some typos.

" Some common terms
abbr IANAL I am not a lawyer, but ...
abbr AFAIK As far as I know
abbr CGI Common Gateway Interface

" Fix typos
abbr lunix Linux
abbr todo TODO

" Hackish, but you can e.g define code snippets
abbr pyclass class MyClass(object):<CR>    def __init__(self, *args, **kwargs):<CR>        pass


Extras: Unicode emojis 🏆

You can also misuse some features, eg. to have emojis autocompleted from :markup: using abbr feature.

🦄 👹 🥴 💰 🏆 … because why not?

abbr :grinningface: 😀
abbr :grinningfacewithbigeyes: 😃
abbr :grinningfacewithsmilingeyes: 😄
abbr :beamingfacewithsmilingeyes: 😁
abbr :grinningsquintingface: 😆
abbr :grinningfacewithsweat: 😅
abbr :rollingonthefloorlaughing: 🤣
abbr :facewithtearsofjoy: 😂
abbr :slightlysmilingface: 🙂
abbr :upside-downface: 🙃
abbr :winkingface: 😉
abbr :smilingfacewithsmilingeyes: 😊
abbr :smilingfa[...]

Show trailing whitespace etc.

Sometimes you need to see if there is tabs, trailing whitespace or newlines in our code. Let’s map <Leader>l to show you these characters.

" Visualize trailing whitespace, tabs and newlines
set listchars=trail:·,tab:▸\ ,eol:¬
 
" Leader key + l = toggle showing whitespace/tab/newline
map <leader>l :set list!<CR>



I’m ready, let’s build

When you complete your changes, you often like to build…

:make build

… or just any command

:!mycustom_command --with-params

Of course these can be also mapped to eg. file save event,

autocmd BufWritePost *.py :!ls -lha

Look up for a function implementation with ctags

Using ctags it’s pretty easy to do code searches in vim.

cd $REPOSITORY
ctags -R * [../thirdparty/wordpress-core]
vim myfile.php

Then, using our custom mappings, we create easy mapping for looking up current word in ctags dataset.

" ctags: jump to definition of current word with <leader>a
nnoremap <leader>a <C-]>

" generate ctags on file close
autocmd BufWritePost *.c,*.cpp,*.h,*.hh,*.php,*.py,*.js silent! !ctags . &

Now, while cursor is over some interesting function name, just hit <Leader>a to see the implementation.

You can also look for specific function with :tag <searchterm>.


Write with sudo

You opened a file, did some changes and then realized you don’t have permissions to write that file, you’d need to be root? No problem!

:w !sudo tee %

ie. write buffer (and pipe) output to sudo tee %, where % points to current filename


Miscellaneous

Some misc. commands

  • :sort sort selected lines

See keymaps

  • :map all
  • :nmap normal mode
  • :imap insert mode
  • :vmap visual mode

File explorer

  • :Explore
  • :Vexplore
  • :Sexplore

UI: tabs, split screen

Yes, vim also supports tabs.

  • :tabedit path/to/file opens a new file in tab
  • :tabnext move to next tab
  • :tabprev move to previous tab
  • :help tab-page-commands for more

and you can also split a screen to have multiple buffers visible simultaneously.

  • :vsplit split screen vertically
  • :split path/to/file split screen horizontally, and open a file
  • :help split for more

Extend: Plugins, color schemes, …

If vim is missing some feature or eg. syntax higlighting support for specific language or filetype, it’s usually available as a plugin.

Also, if you don’t like the default colors, there is lots of color schemes/themes available too.


Plugins: Install

This focuses solely on plugins included in Debian repos, as like any software, you should first asses the security of the software before you install something, and I just don’t have time to. So, let’s use plugins that Debian project has reviewed.

Plugins we look here are included in these packages:

apt-get install vim-scripts vim-gitgutter vim-airline vim-syntastic vim-youcompleteme

In this case we depend on files that are already in the system, so we use symlinks to “install” these to our user. Eg.

mkdir -p ~/.vim/plugin
ln -s /usr/share/vim-scripts/plugin/{vcscommand,vcsgit}.vim \
      ~/.vim/plugin/

Plugins: Version control integration

vim-scripts plugins: vcscommand.vim, vcsgit.vim

So, you’d like to commit, blame and do other git stuff while editing file? It’s possible.


Plugins: GnuPG

vim-scripts plugin: gnupg.vim

Automatically decrypt/encrypt files using GnuPG when opening/closing a file. No more need to decrypt, open editor, re-encrypt.


Plugins: Secure modelines

vim-scripts plugin: securemodelines.vim

Modelines are vim feature, in which some details about file formatting (indentation etc.) get stored in target file. Modelines look like this:

/* vim: set noai ts=4 sw=4: */

and you can quite often find them in source code. Unfortunately, this feature has seen some security issues in the past, and it’s often recommended to disable the default implementation (like we did in config earlier).

But, there exists a little bit better implementation called securemodelines, that supports most of the features, but only those deemed secure.


Plugin: GitGutter

As almost all projects nowadays use git for their version control needs, it’s often desirable to have some support for VCS built in your text editor. For vim one great tool is plugin called GitGutter, which shows status of each line in left side of the window.

Also, some fixes to default config…

" Always show the sign column, removes some annoying on/off toggling
set signcolumn=yes

" Remap hunk modifier keys
nmap ghn <Plug>(GitGutterNextHunk)
nmap ghp <Plug>(GitGutterPrevHunk)
nmap ghs <Plug>(GitGutterStageHunk)
nmap ghu <Plug>(GitGutterUndoHunk)



Plugin: YouCompleteMe

Autocompletion. Let vim suggest you what to write next.

mkdir -p ~/.vim/{plugin,autoload}
ln -s /usr/share/vim-youcompleteme/plugin/youcompleteme.vim ~/.vim/plugin/youcompleteme.vim
ln -s /usr/share/vim-youcompleteme/autoload/youcompleteme.vim ~/.vim/autoload/youcompleteme.vim

Syntax checking

As no-one writes perfect code 100% of the time, it’s nice to have some helpers for checking your syntax. One nice tool is vim-syntastic, which does syntax checking.

" Syntax checks / linting using Syntastic

" Use flake8 for checking Python syntax
let g:syntastic_python_checkers = ["flake8"]

" Use PHP + PHPCS + PHPMD for checking PHP file syntax
let g:syntastic_php_checkers = ['php', 'phpcs', 'phpmd']

" Update status for syntastic
set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*

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



Extras

Add some filetype-specific extras (syntax fixes)

" Delete trailing white space on save, useful for eg. Python
func! DeleteTrailingWS()
  exe "normal mz"
  %s/\s\+$//ge
  exe "normal `z"
endfunc
autocmd BufWrite *.py :call DeleteTrailingWS()



Read more

Full example: https://ypcs.fi/misc/code/vim/vimrc.txt