Notes

Vim Configuration from Minimal to Complete

From a configuration that works on low-resource, restricted permissions environments, to a complete configuration…
And from almost scratch. Because I like to know what’s in my .vimrc.

TL;DR the final vimrc and at the different steps is in this repository, with tags for each step.

Vim rocks 🤘! And recent plugins can make it really great for coding, most notably using Conquer of Completion with NodeJS, that has a bunch of extensions for a lot of languages using the Language Server Protocol.

In this article however, we’ll assume to be working on a low resource computer, or limited in what can be installed, runtimes available, etc. So code completion, linting, quality without external runtimes!

I’ll use this GitHub repository and tags for each steps in ths article.

You can apply the diffs in this post by copying them in a file, let’s say example.patch, and apply them, as long as you have the same directory structure as in my GitHub repository, and run $ git apply example.patch.

Minimal Configuration

Let’s create a minimal vimrc file with some basic configuration we need.

Adding Plugins

With vim 7.4, there was some tuning to do to manage the runtime path, that’s why there are a bunch of plugin managers out there, like the minimalistic, solid vim-pathogen by Tim Pope (again!), vundle, then asynchronous and fast dein, and my favorite Vim-Plug.

Since vim 8, there’s a /pack/ (think backslashes for Windows) directory in your vim files (~/.vim/ for linux and Mac OS, ~\vimfiles\ for Windows) where you can clone, submodule, or simply copy plugins that will be loaded on vim startup.

I’ll demonstrate and put the code here using the package path and with Vim-Plug, but if you like another plugin manager, it’s usually pretty simple to move from one to the other, they usually use the same pattern:

call#open
  plugin '<plugin path>'
  plugin '<plugin path>'
  plugin '<plugin path>'
call#close

If you are not sure vhether your vim supports packages, try typing :echo has('packages') in vim, it should display 1.

I recommend using a plugin manager. I like Vim-Plug because of how fast it is to fetch things asynchronously, and the options to run an installation script that you won’t have using the vendor thing. And pathogen for it’s simplicity. On old OS with vim 7.4, maybe go for vim-pathogen.

Plugin Initialization

Everything from here will be branched. So this is what the tags look like so far in the repository:

vimrc 0.3.0

Vim Packages

Here’s the initialization, I am also adding the scripts in the repository’s bin folder. You can find the text in the .vimrc file so far on the 0.3.0 tag

#!/usr/bin/env bash

if [ ! -d "$HOME/.vim/pack/bundle/start" ]; then
    mkdir -p ~/.vim/pack/bundle/start
fi

cd ~/.vim/pack/bundle/start

Vim-Plug

  1. Install Vim-Plug using the instructions for your OS, following the official instructions
  2. Call and close plug in the .vimrc file: ```diff diff –git a/.vimrc b/.vimrc index 7f727b8..143e7d2 100644 — a/.vimrc +++ b/.vimrc @@ -173,3 +173,9 @@ function! Smart_TabComplete() endfunction

inoremap =Smart_TabComplete() + +call plug#begin('~/.vim/plugged') + +" Put plugins here + +call plug#end()

  Find this content [the 0.3.0-plug tag](https://github.com/ThomasMarcel/vimrc/blob/0.3.0-plug/.vimrc).

## General Developer Experience (DX) Plugins

Now, let's add some cool DX plugins (let me know if I am missing some)

* [vim-sensible](https://github.com/tpope/vim-sensible) (we started our `.vimrc` file with that, so not mandatory, but like that it'll be up to do date)
* [mucomplete](https://github.com/lifepillar/vim-mucomplete) for code completion. Note that to use it properly, we need to remove the mapping for the tab key. And it is "better" than our omnifunc because it will check the language completion, then the tags, etc automatically in one command
* [vim-fugitive](https://github.com/tpope/vim-fugitive) git wrapper
* [vim-commentary](https://github.com/tpope/vim-commentary) comment stuff out
* [vim-surround](https://github.com/tpope/vim-surround) manipulate enclosing characters, tags, etc
* [vim-vinegar](https://github.com/tpope/vim-vinegar) to move around from buffer to netrw (vim's directory browser)
* [vim-sleuth](https://github.com/tpope/vim-sleuth) to set indentation
* [vim-obsession](https://github.com/tpope/vim-obsession) vim session autosave and other session-related things
* [delimitmate](https://github.com/raimondi/delimitmate) to automatically close quotes, etc
* [ctrlp.vim](https://github.com/ctrlpvim/ctrlp.vim) search for a file

### Vim Packages

I modified my script to clone repositories to not become very repetitive, and commented out the tab key mapping to avoid conflict with mucomplete. Find the code in [the 0.4.0 tag](https://github.com/ThomasMarcel/vimrc/tree/0.4.0).

```diff
diff --git a/.vimrc b/.vimrc
index 7f727b8..65a4c7a 100644
--- a/.vimrc
+++ b/.vimrc
@@ -148,6 +148,7 @@ silent function! WINDOWS()
 endfunction
 
 set omnifunc=syntaxcomplete#Complete
+set spell
 
 " Smart mapping for tab completion
 " https://vim.fandom.com/wiki/Smart_mapping_for_tab_completion
@@ -172,4 +173,4 @@ function! Smart_TabComplete()
   endif
 endfunction
 
-inoremap <tab> <c-r>=Smart_TabComplete()<CR>
+" inoremap <tab> <c-r>=Smart_TabComplete()<CR>
diff --git a/bin/plugins.sh b/bin/plugins.sh
old mode 100644
new mode 100755
index 23d88c2..ab903ec
--- a/bin/plugins.sh
+++ b/bin/plugins.sh
@@ -6,4 +6,15 @@ fi
 
 cd ~/.vim/pack/bundle/start
 
-git clone --depth 1 https://github.com/lifepillar/vim-mucomplete.git
+git_plugins=( "tpope/vim-sensible" "lifepillar/vim-mucomplete" "tpope/vim-fugitive" "tpope/vim-commentary" "tpope/vim-surround" "tpope/vim-vinegar" "tpope/vim-sleuth" "tpope/vim-obsession" "raimondi/delimitmate" "ctrlpvim/ctrlp.vim" )
+
+for plugin in "${git_plugins[@]}"; do
+    folder=$(echo $plugin | cut -d'/' -f2)
+    if [ ! -d "$folder" ]; then
+        git clone --depth 1 "https://github.com/$plugin.git"
+    else
+        cd "$folder"
+        git fetch origin
+        cd ..
+    fi
+done

Vim-Plug

The results can be found in the 0.4.0-plug tag.

diff --git a/.vimrc b/.vimrc
index 143e7d2..bcb980d 100644
--- a/.vimrc
+++ b/.vimrc
@@ -148,6 +148,7 @@ silent function! WINDOWS()
 endfunction
 
 set omnifunc=syntaxcomplete#Complete
+set spell
 
 " Smart mapping for tab completion
 " https://vim.fandom.com/wiki/Smart_mapping_for_tab_completion
@@ -172,10 +173,19 @@ function! Smart_TabComplete()
   endif
 endfunction
 
-inoremap <tab> <c-r>=Smart_TabComplete()<CR>
+" inoremap <tab> <c-r>=Smart_TabComplete()<CR>
 
 call plug#begin('~/.vim/plugged')
 
-" Put plugins here
+Plug 'tpope/vim-sensible'
+Plug 'lifepillar/vim-mucomplete'
+Plug 'tpope/vim-fugitive'
+Plug 'tpope/vim-commentary'
+Plug 'tpope/vim-surround'
+Plug 'tpope/vim-vinegar'
+Plug 'tpope/vim-sleuth'
+Plug 'tpope/vim-obsession'
+Plug 'raimondi/delimitmate'
+Plug 'ctrlpvim/ctrlp.vim'
 
 call plug#end()

Linter and Fixer

First let’s get the package.

There’s a bit of configuration to do for the fixers. And you can optionally add let g:ale_fix_on_save = 1 to automatically fix on save.
We are also adding some general fixes for all file types: remove trailing lines and whitespaces.

diff --git a/.vimrc b/.vimrc
index 65a4c7a..7711a6a 100644
--- a/.vimrc
+++ b/.vimrc
@@ -174,3 +174,9 @@ function! Smart_TabComplete()
 endfunction
 
 " inoremap <tab> <c-r>=Smart_TabComplete()<CR>
+
+let g:ale_fixers = {
+\   '*': ['remove_trailing_lines', 'trim_whitespace'],
+\}
+
+let g:ale_fix_on_save = 1

Vim Packages

Let’s get the package (cf. tag 0.5.0).

diff --git a/.vimrc b/.vimrc
index 65a4c7a..7711a6a 100644
--- a/.vimrc
+++ b/.vimrc
@@ -174,3 +174,9 @@ function! Smart_TabComplete()
 endfunction
 
 " inoremap <tab> <c-r>=Smart_TabComplete()<CR>
+
+let g:ale_fixers = {
+\   '*': ['remove_trailing_lines', 'trim_whitespace'],
+\}
+
+let g:ale_fix_on_save = 1
diff --git a/bin/plugins.sh b/bin/plugins.sh
index ab903ec..3b87ba4 100755
--- a/bin/plugins.sh
+++ b/bin/plugins.sh
@@ -8,6 +8,8 @@ cd ~/.vim/pack/bundle/start
 
 git_plugins=( "tpope/vim-sensible" "lifepillar/vim-mucomplete" "tpope/vim-fugitive" "tpope/vim-commentary" "tpope/vim-surround" "tpope/vim-vinegar" "tpope/vim-sleuth" "tpope/vim-obsession" "raimondi/delimitmate" "ctrlpvim/ctrlp.vim" )
 
+linter_plugins=( "dense-analysis/ale" )
+
 for plugin in "${git_plugins[@]}"; do
     folder=$(echo $plugin | cut -d'/' -f2)
     if [ ! -d "$folder" ]; then
@@ -18,3 +20,14 @@ for plugin in "${git_plugins[@]}"; do
         cd ..
     fi
 done
+
+for plugin in "${linter_plugins[@]}"; do
+    folder=$(echo $plugin | cut -d'/' -f2)
+    if [ ! -d "$folder" ]; then
+        git clone --depth 1 "https://github.com/$plugin.git"
+    else
+        cd "$folder"
+        git fetch origin
+        cd ..
+    fi
+done

Vim-Plug

Now we add the plugin with vim-plug, and the same bit of configuration as mentioned before. Tag 0.5.0-plug.

diff --git a/.vimrc b/.vimrc
index bcb980d..109ec2f 100644
--- a/.vimrc
+++ b/.vimrc
@@ -188,4 +188,12 @@ Plug 'tpope/vim-obsession'
 Plug 'raimondi/delimitmate'
 Plug 'ctrlpvim/ctrlp.vim'
 
+Plug 'dense-analysis/ale'
+
 call plug#end()
+
+let g:ale_fixers = {
+\   '*': ['remove_trailing_lines', 'trim_whitespace'],
+\}
+
+let g:ale_fix_on_save = 1

Language-specific configuration and plugins

Linting configuration

Installed linters for your language of preference should work out of the box, and fixers too within the available commands, unless specified otherwise.

To see what linters and fixers are available end enabled, open a file of the type you want, and write :ALEInfo. At the top, you will see something like the following:

 Current Filetype: rust
Available Linters: ['analyzer', 'cargo', 'rls', 'rustc']
  Enabled Linters: ['cargo', 'rls']
  Ignored Linters: []
 Suggested Fixers:
  'remove_trailing_lines' - Remove all blank lines at the end of a file.
  'rustfmt' - Fix Rust files with Rustfmt.
  'trim_whitespace' - Remove all trailing whitespace characters at the end of every line.

And from this info, you can also see the output of the command, so if a linter or fixer isn’t behaving like it should, check its output in :ALEInfo and debug!

The .vimrc looks like this in tags 0.6.0 and 0.6.0-plug, respectively.

  diff --git a/.vimrc b/.vimrc
index 7711a6a..9535a37 100644
--- a/.vimrc
+++ b/.vimrc
@@ -64,7 +64,7 @@ if &listchars ==# 'eol:$'
   set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+
 endif
 
-if v:version > 703 || v:version == 703 && has("patch541")
+if v:version > 703 || v:version == 703 && has('patch541')
   set formatoptions+=j " Delete comment character when joining commented lines
 endif
 
@@ -96,7 +96,7 @@ if &t_Co == 8 && $TERM !~# '^Eterm'
 endif
 
 " Load matchit.vim, but only if the user hasn't installed a newer version.
-if !exists('g:loaded_matchit') && findfile('plugin/matchit.vim', &rtp) ==# ''
+if !exists('g:loaded_matchit') && findfile('plugin/matchit.vim', &runtimepath) ==# ''
   runtime! macros/matchit.vim
 endif
 
@@ -175,8 +175,36 @@ endfunction
 
 " inoremap <tab> <c-r>=Smart_TabComplete()<CR>
 
+augroup FiletypeGroup
+    autocmd!
+    au BufNewFile,BufRead *.jsx set filetype=javascript.jsx
+augroup END
+
+let g:ale_linter_aliases = {
+            \ 'jsx': ['css', 'javascript'],
+            \ 'vue': ['eslint', 'vls']
+            \}
+
+let g:ale_linters = {
+            \ 'jsx': ['stylelint', 'eslint'],
+            \ 'rust': ['analyzer', 'cargo', 'rls'],
+            \ 'vim': ['vint'],
+            \ 'zsh': ['shell', 'shellcheck'],
+            \}
+
 let g:ale_fixers = {
-\   '*': ['remove_trailing_lines', 'trim_whitespace'],
-\}
+            \ '*': ['remove_trailing_lines', 'trim_whitespace'],
+            \ 'rust': ['rustfmt'],
+            \}
 
 let g:ale_fix_on_save = 1
+
+let g:ale_sign_column_always = 1
+let g:ale_echo_msg_error_str = 'E'
+let g:ale_echo_msg_warning_str = 'W'
+let g:ale_echo_msg_format = '[%linter%] %s [%severity%]'
+let g:ale_lint_on_insert_leave = 0
+let g:ale_set_loclist = 1
+let g:ale_set_quickfix = 1
+let g:ale_open_list = 1
+let g:ale_list_window_size = 5

So mainly what was done here is:

Language plugins

For better syntax highlighting and other functions, sometimes the default language support offered by vim out of the box is supbar or non-existant. In that case, there are several options to consider:

Colors and Themes

Now there’s a saying in French that goes like “Des goûts et des couleurs, on ne discute pas”. Literal translation being “of tastes and colors, we don’t discuss”.

So here I’ll just drop a basic configuration, one plugin I like, to easily switch between themes, and a bunck of themes I like.
Mostly dark. Cuz most developers have some common traits with vampires. Like hating light, not living the healthiest of lives. Some hate garlic, I guess. And now (03/06/2021) we can’t cross seas, but that’s valid for most of the human condition, not only vampires and developers. And developires.

Let’s take an example with the famous solarized8 color theme. Basically, we get the plugin, set the colorscheme variable, background tone (light/dark).

Optional: for a GUI, we can add a font. Mine is Dank Mono. It’s not free, but there are a lot of cool free fonts too. Check out Dev Fonts to see which one lookgs good.

You can find a list of popular color schemes at VimAwesome.

Vim Packages

You can see the results in tag 0.7.0 for the install script, tag 0.8.0 for the .vimrc.

diff --git a/.vimrc b/.vimrc
index 9535a37..373d076 100644
--- a/.vimrc
+++ b/.vimrc
@@ -148,7 +148,7 @@ silent function! WINDOWS()
 endfunction
 
 set omnifunc=syntaxcomplete#Complete
-set spell
+" set spell
 
 " Smart mapping for tab completion
 " https://vim.fandom.com/wiki/Smart_mapping_for_tab_completion
@@ -208,3 +208,15 @@ let g:ale_set_loclist = 1
 let g:ale_set_quickfix = 1
 let g:ale_open_list = 1
 let g:ale_list_window_size = 5
+
+set background=dark
+colorscheme solarized8
+if has('gui_running')
+  if has('gui_gtk2')
+    set guifont=Dank\ Mono\ Regular:h12
+  elseif has('gui_macvim')
+    set guifont=Dank\ Mono\ Regular:h12
+  elseif has('gui_win32')
+    set guifont=Dank\ Mono\ Regular:h12
+  endif
+endif
diff --git a/bin/plugins.sh b/bin/plugins.sh
index 3b87ba4..38b9e9c 100755
--- a/bin/plugins.sh
+++ b/bin/plugins.sh
@@ -10,6 +10,8 @@ git_plugins=( "tpope/vim-sensible" "lifepillar/vim-mucomplete" "tpope/vim-fugiti
 
 linter_plugins=( "dense-analysis/ale" )
 
+color_plugins=( "lifepillar/vim-solarized8" )
+
 for plugin in "${git_plugins[@]}"; do
     folder=$(echo $plugin | cut -d'/' -f2)
     if [ ! -d "$folder" ]; then
@@ -31,3 +33,14 @@ for plugin in "${linter_plugins[@]}"; do
         cd ..
     fi
 done
+
+for plugin in "${color_plugins[@]}"; do
+    folder=$(echo $plugin | cut -d'/' -f2)
+    if [ ! -d "$folder" ]; then
+        git clone --depth 1 "https://github.com/$plugin.git"
+    else
+        cd "$folder"
+        git fetch origin
+        cd ..
+    fi
+done

Vim-Plug

The tag 0.8.0-plug adds the colorscheme in the vim-plug branch.

diff --git a/.vimrc b/.vimrc
index 2844774..e3843a2 100644
--- a/.vimrc
+++ b/.vimrc
@@ -190,6 +190,8 @@ Plug 'ctrlpvim/ctrlp.vim'
 
 Plug 'dense-analysis/ale'
 
+Plug 'lifepillar/vim-solarized8'
+
 call plug#end()
 
 augroup FiletypeGroup
@@ -226,3 +228,15 @@ let g:ale_set_loclist = 1
 let g:ale_set_quickfix = 1
 let g:ale_open_list = 1
 let g:ale_list_window_size = 5
+
+set background=dark
+colorscheme solarized8
+if has('gui_running')
+  if has('gui_gtk2')
+    set guifont=Dank\ Mono\ Regular:h12
+  elseif has('gui_macvim')
+    set guifont=Dank\ Mono\ Regular:h12
+  elseif has('gui_win32')
+    set guifont=Dank\ Mono\ Regular:h12
+  endif
+endif

Extras

So now we have a pretty decent configuration. But there are still some plugins that are good to have:

Authoring

I’ve written an article abouth authoring with vim, all the plugins for completion and spelling, and some goodies.

More

Plugins

Visit VimAwesome for a list of popular vim plugins

Dotfiles and vimrc generators

There are several dotfiles from developers that you can find on GitHub. And there’s Vim Bootstrap and other .vimrc generators.

Conclusion

I hope this will help you. This is also part of a thought process that I went through to cleanup my own .vimrc, and know it better.

The .vimrc at that point is located in tag 1.0.0 and 1.0.0-plug. I will write separate posts for the programming languages I use.

Let me know what you think!

And note so self: write smaller articles but more often 😅.