Ahmed El Gabri

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

_Update 6 Feb 2021: I mainly use neovim as my main editor & I have migrated my statusline to lua. Also it's much much minimal now._

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.

" Statusline

" :h mode() to see all modes
let g:currentmode={
    \ 'n'      : 'N ',
    \ 'no'     : 'N路Operator Pending ',
    \ 'v'      : 'V ',
    \ 'V'      : 'V路Line ',
    \ '\<C-V>' : 'V路Block ',
    \ 's'      : 'Select ',
    \ 'S'      : 'S路Line ',
    \ '\<C-S>' : 'S路Block ',
    \ 'i'      : 'I ',
    \ 'R'      : 'R ',
    \ 'Rv'     : 'V路Replace ',
    \ 'c'      : 'Command ',
    \ 'cv'     : 'Vim Ex ',
    \ 'ce'     : 'Ex ',
    \ 'r'      : 'Prompt ',
    \ 'rm'     : 'More ',
    \ 'r?'     : 'Confirm ',
    \ '!'      : 'Shell ',
    \ 't'      : 'Terminal '

" Automatically change the statusline color depending on mode
function! ChangeStatuslineColor()
  if (mode() =~# '\v(n|no)')
    exe 'hi! StatusLine ctermfg=008'
  elseif (mode() =~# '\v(v|V)' || g:currentmode[mode()] ==# 'V路Block' || get(g:currentmode, mode(), '') ==# 't')
    exe 'hi! StatusLine ctermfg=005'
  elseif (mode() ==# 'i')
    exe 'hi! StatusLine ctermfg=004'
    exe 'hi! StatusLine ctermfg=006'

  return ''

" Find out current buffer's size and output it.
function! FileSize()
  let bytes = getfsize(expand('%:p'))
  if (bytes >= 1024)
    let kbytes = bytes / 1024
  if (exists('kbytes') && kbytes >= 1000)
    let mbytes = kbytes / 1000

  if bytes <= 0
    return '0'

  if (exists('mbytes'))
    return mbytes . 'MB '
  elseif (exists('kbytes'))
    return kbytes . 'KB '
    return bytes . 'B '

function! ReadOnly()
  if &readonly || !&modifiable
    return '顐'
    return ''

function! GitInfo()
  let git = fugitive#head()
  if git != ''
    return '顐 '.fugitive#head()
    return ''

set laststatus=2
set statusline=
set statusline.=%{ChangeStatuslineColor()}               " Changing the statusline color
set statusline.=%0*\ %{toupper(g:currentmode[mode()])}   " Current mode
set statusline.=%8*\ [%n]                                " buffernr
set statusline.=%8*\ %{GitInfo()}                        " Git Branch name: requires https://github.com/tpope/vim-fugitive
set statusline.=%8*\ %<%F\ %{ReadOnly()}\ %m\ %w\        " File+path
set statusline.=%*
set statusline.=%9*\ %=                                  " Space
set statusline.=%8*\ %y\                                 " FileType
set statusline.=%7*\ %{(&fenc!=''?&fenc:&enc)}\[%{&ff}]\ " Encoding & Fileformat
set statusline.=%8*\ %-3(%{FileSize()}%)                 " File size
set statusline.=%0*\ %3p%%\ 顐 %l:\ %3c\                 " Rownumber/total (%)

hi User1 ctermfg=007
hi User2 ctermfg=008
hi User3 ctermfg=008
hi User4 ctermfg=008
hi User5 ctermfg=008
hi User7 ctermfg=008
hi User8 ctermfg=008
hi 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, Neovim 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.

You can tweet this post or reach out directly to me @ahmedelgabri.

2024 Ahmed El Gabri