DIY Vim statusline

Update 20 Aug 2017: I’m currently using a new & better version of the statusline code posted in this post, you can check the code here: statusline & statusline functions

Nearly two years ago I decided to switch to Vim and move all my work into one app. The command line/Terminal or more sepcifically iTerm2. That desicion turns out to be one of the best things I’ve ever done. I’m still not a Vim expert by any means but I always look for ways to improve my workflow.

As anyone who starts to use Vim you start installing lots of plugins to try and emulate some functionalities that you are used to in your text editor or IDE. Which is a wrong thing to do, but that’s a different story and already lots of people talk about this before.

So one of the things that is important in Vim is the statusline it shows lots of useful info about the file you are editing and the mode you are currently on, etc… and as nearly everything in Vim it’s insanely customizable so I went with the easiest solution which was airline, Airline is really powerful and customizable too. But after a while and as I usually do; I try to simplify my tools as much as I can and reduce the moving parts/dependencies. I started to read about statusline :h 'statusline' and check some articles about Vim and going through any dotfiles repo I can find and go through its code not only for Vim but for everything.

I found out that for my needs I actually don’t need airline, also because I was using lots of plugins this started to slow down Vim startup and sometimes editing too which was a red flag for me cause one of the reason of using Vim was that it’s blazingly fast to start, nearly instant. So I decided I’ll write my own customization to emulate what I liked about airline without all the overhead of it. I managed to achieve what I want and it turns out to be quite simple.

1" Statusline
2
3" :h mode() to see all modes
4let g:currentmode={
5 \ 'n' : 'N ',
6 \ 'no' : 'N·Operator Pending ',
7 \ 'v' : 'V ',
8 \ 'V' : 'V·Line ',
9 \ '\<C-V>' : 'V·Block ',
10 \ 's' : 'Select ',
11 \ 'S' : 'S·Line ',
12 \ '\<C-S>' : 'S·Block ',
13 \ 'i' : 'I ',
14 \ 'R' : 'R ',
15 \ 'Rv' : 'V·Replace ',
16 \ 'c' : 'Command ',
17 \ 'cv' : 'Vim Ex ',
18 \ 'ce' : 'Ex ',
19 \ 'r' : 'Prompt ',
20 \ 'rm' : 'More ',
21 \ 'r?' : 'Confirm ',
22 \ '!' : 'Shell ',
23 \ 't' : 'Terminal '
24 \}
25
26" Automatically change the statusline color depending on mode
27function! ChangeStatuslineColor()
28 if (mode() =~# '\v(n|no)')
29 exe 'hi! StatusLine ctermfg=008'
30 elseif (mode() =~# '\v(v|V)' || g:currentmode[mode()] ==# 'V·Block' || get(g:currentmode, mode(), '') ==# 't')
31 exe 'hi! StatusLine ctermfg=005'
32 elseif (mode() ==# 'i')
33 exe 'hi! StatusLine ctermfg=004'
34 else
35 exe 'hi! StatusLine ctermfg=006'
36 endif
37
38 return ''
39endfunction
40
41" Find out current buffer's size and output it.
42function! FileSize()
43 let bytes = getfsize(expand('%:p'))
44 if (bytes >= 1024)
45 let kbytes = bytes / 1024
46 endif
47 if (exists('kbytes') && kbytes >= 1000)
48 let mbytes = kbytes / 1000
49 endif
50
51 if bytes <= 0
52 return '0'
53 endif
54
55 if (exists('mbytes'))
56 return mbytes . 'MB '
57 elseif (exists('kbytes'))
58 return kbytes . 'KB '
59 else
60 return bytes . 'B '
61 endif
62endfunction
63
64function! ReadOnly()
65 if &readonly || !&modifiable
66 return ''
67 else
68 return ''
69endfunction
70
71function! GitInfo()
72 let git = fugitive#head()
73 if git != ''
74 return ' '.fugitive#head()
75 else
76 return ''
77endfunction
78
79set laststatus=2
80set statusline=
81set statusline+=%{ChangeStatuslineColor()} " Changing the statusline color
82set statusline+=%0*\ %{toupper(g:currentmode[mode()])} " Current mode
83set statusline+=%8*\ [%n] " buffernr
84set statusline+=%8*\ %{GitInfo()} " Git Branch name
85set statusline+=%8*\ %<%F\ %{ReadOnly()}\ %m\ %w\ " File+path
86set statusline+=%#warningmsg#
87set statusline+=%{SyntasticStatuslineFlag()} " Syntastic errors
88set statusline+=%*
89set statusline+=%9*\ %= " Space
90set statusline+=%8*\ %y\ " FileType
91set statusline+=%7*\ %{(&fenc!=''?&fenc:&enc)}\[%{&ff}]\ " Encoding & Fileformat
92set statusline+=%8*\ %-3(%{FileSize()}%) " File size
93set statusline+=%0*\ %3p%%\ \ %l:\ %3c\ " Rownumber/total (%)
94
95hi User1 ctermfg=007
96hi User2 ctermfg=008
97hi User3 ctermfg=008
98hi User4 ctermfg=008
99hi User5 ctermfg=008
100hi User7 ctermfg=008
101hi User8 ctermfg=008
102hi User9 ctermfg=007

First we need to get create a map of Vim modes and change the color of the statusline depends on the mode we are in. What ChangeStatuslineColor() does is that it gets the current mode and checks in the currentmode map and change the color of the statusline accordingly. Pretty simple.

Next, the FileSize(), ReadOnly() and GitInfo() are pretty self explanatory. The next section is how we but all this information together and construct the statusline.

First we need to tell vim to always show the statusline by doing set laststatus=2 next we construct our statusline using all the functions that we created before. I won’t go through all the code here and I highly recommend reading :h statusline what all these characters means.

At last, we set the colors for statusline using highlight User1-9, more in :h hl-User1 and if you want to know where these color values comes from you can check my previous article on this.

Full credit goes to Reman on Stackoverflow and greduan dotfiles.

Here is a gif for my statusline and here is the code in my vimrc I have enabled true color in iTerm2, Neovm and tmux. So ignore the hex code for colors

Vim and CLI are both really powerful and the ability to have your workflow in one place is very productive as everything we don’t know it’s scary at first but once you get some basic understanding of it becomes easier to understand.

Tweet
Ahmed El Gabri © 2019