💾 Archived View for republic.circumlunar.space › users › johngodlee › posts › 2018-01-11-lightline.g… captured on 2023-09-08 at 16:22:30. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-12-04)
-=-=-=-=-=-=-
DATE: 2018-01-11
AUTHOR: John L. Godlee
Yesterday I decided, in an effort to remove plugin dependencies in my .vimrc and to learn a bit more about how VIM works, to get rid of lightline.vim[1] and replace it with a statusline and tabline of my own design.
1: https://github.com/itchyny/lightline.vim
I'v never had any problems with lightline in the year or so that I've been using it. I've never used VIM without lightline in fact. Lightline provides lots of useful information as standard, such as the file name, the cursor position in the file, the file type, and importantly, it changes colour depending on what mode you are in, insert, visual, normal, etc..
But, just having lightline do everything for me isn't particularly exciting. So first thing was to see what the default statusline looks like:
Very boring, all you can see is the file name.
Ideally, I want some useful information about my document in the statusline, namely in order from most to least wanted:
All of these, except the git branch, can be easily added to the statusline with code found in the help file for the statusline, (:help statusline)
To add an item to the statusline, in your .vimrc add a line that starts:
set statusline=
Then add one of the 'printf' style codes in the help file. e.g. for the file name, otherwise known as the tail of the filepath (%t):
set statusline=%t
After reloading your .vimrc (:source .vimrc) you will see nothing has changed. Yay!
To test it's actually worked, try adding some other codes:
set statusline=%t%m%y%p%l%c
which produces this:
which is very ugly, and not particularly easy to read, either in the vimrc or in the statusline itself. First, let's rectify this in the .vimrc. I like to separate out my statusline code so that each item or group of items is on its own line. The syntax used below is similar to that used to design a prompt in the bash shell:
set statusline=%t " file name set statusline+=%m " modified? set statusline+=%y " file type set statusline+=%p " percentage through file set statusline+=%l " line number set statusline+=%c " column number
Source your .vimrc to see that nothing has changed in the statusline, but the .vimrc is much easier to read.
Now we can break up the information in the statusline a bit, using spaces and special characters.
First, I want a space between certain items, which I can add by adding set statusline+=\ lines to my statusline code. Note the actual space after the \. You can also add normal characters to the statusline. For instance, to break up the line and column number you could add a : by adding set statusline+=::
set statusline=%t " file name set statusline+=\ " space set statusline+=%m " modified? set statusline+=\ " space set statusline+=%y " file type set statusline+=\ " space set statusline+=%p " percentage through file set statusline+=\ " space set statusline+=%l " line number set statusline+=: " colon separator set statusline+=%c " column number
Status line with spacing between content
Now. I don't really want the information about my position in the file on the left side, I'd rather have it on the right side. I can switch to the right side of the statusline by adding set statusline+=%= before the lines you want to appear on the right side:
set statusline=%t " file name set statusline+=\ " space set statusline+=%m " modified? set statusline+=\ " space set statusline+=%y " file type set statusline+=\ " space set statusline+=%= " switch to right side set statusline+=%p " percentage through file set statusline+=\ " space set statusline+=%l " line number set statusline+=: " colon separator set statusline+=%c " column number
Status line split left and right
So that's the basics of a statusline covered I think. Now onto making our own functions
I didn't come up with the ideas presented here myself, I adapted them from online sources. Three things on my list of desired features in the statusline don't appear as standard codes in the help file, those things are displaying the mode I am in, changing the colour based on the mode, and the git branch.
First let's do the mode, which is fairly easy, because VIM already has a global variable called currentmode, so I just need to give the default outputs some aliases that are easier to read:
let g:currentmode={ \ 'n' : 'Normal', \ 'no' : 'N·Operator Pending', \ 'v' : 'Visual', \ 'V' : 'V·Line', \ '^V' : 'V·Block', \ 's' : 'Select', \ 'S' : 'S·Line', \ '^S' : 'S·Block', \ 'i' : 'Insert', \ 'R' : 'R', \ 'Rv' : 'V·Replace', \ 'c' : 'Command', \ 'cv' : 'Vim Ex', \ 'ce' : 'Ex', \ 'r' : 'Prompt', \ 'rm' : 'More', \ 'r?' : 'Confirm', \ '!' : 'Shell', \ 't' : 'Terminal' \}
then call the variable in the statusline code:
set statusline=%{g:currentmode[mode()]} " mode set statusline+=\ " space set statusline+=%t " file name set statusline+=\ " space set statusline+=%m " modified? set statusline+=\ " space set statusline+=%y " file type set statusline+=\ " space set statusline+=%= " switch to right side set statusline+=%p " percentage through file set statusline+=\ " space set statusline+=%l " line number set statusline+=: " colon separator set statusline+=%c " column number
The git branch is a bit harder, requiring vim script and running some shell commands in the .vimrc. Here is my code to define a function to show the git branch in the statusline:
function CurrentGitBranch() let gitoutput = system('git status -b '.shellescape(expand('%')).' | head -1 | grep -oE "[^ ]+$" | tr -d "[:cntrl:]"') if gitoutput =~ "invalid" let b:gitstatus = '' else let b:gitstatus = gitoutput endif endfunc autocmd BufEnter,BufWritePost * call CurrentGitBranch()
Let's go through this line by line.
To add b:gitstatus to the statusline, follow the same rules as for the current mode function we did earlier.
set statusline=%{g:currentmode[mode()]} " mode set statusline+=\ " space set statusline+=%t " file name set statusline+=\ " space set statusline+=%{b:gitstatus} " git branch set statusline+=%m " modified? set statusline+=\ " space set statusline+=%y " file type set statusline+=\ " space set statusline+=%= " switch to right side set statusline+=%p " percentage through file set statusline+=\ " space set statusline+=%l " line number set statusline+=: " colon separator set statusline+=%c " column number
Status line with git repository
For the colour changing status bar you can use another function:
function! ChangeStatuslineColor() if (mode() ==# 'i') exe 'hi User1 ctermfg=black ctermbg=white' else exe 'hi User1 ctermfg=white ctermbg=black' endif return '' endfunction
This defines the User1 colour palette, with foreground (text) and background colours, reversing the colours depending on the current mode. To add it to the statusline:
set statusline=%{ChangeStatuslineColor()} " Load function set statusline+=%1* " Change colour palette to `User1` set statusline+=%{g:currentmode[mode()]} " Display the mode set statusline+=%0* " Return to default colour palette set statusline+=\ " space set statusline+=%t " file name set statusline+=\ " space set statusline+=%{b:gitstatus} " git branch set statusline+=%m " modified? set statusline+=\ " space set statusline+=%y " file type set statusline+=\ " space set statusline+=%= " switch to right side set statusline+=%p " percentage through file set statusline+=\ " space set statusline+=%l " line number set statusline+=: " colon separator set statusline+=%c " column number
Obviously you can change the colours to whatever you desire, using ANSI colour codes to extend the colour range, and if you have an X terminal, just change cterm to xterm and you can then use Xterm colour codes. You could also add extra elseif statements to add other modes. Lightline for example turns orange when in visual mode.
While the above has all the basic functionality, it's very ugly. For my real statusline I tweaked things a little bit more:
" statusline always showing, even when NERDTree is hidden set laststatus=2 " Map of modes and their codes for statusline let g:currentmode={ \ 'n' : 'Normal', \ 'no' : 'N·Operator Pending', \ 'v' : 'Visual', \ 'V' : 'V·Line', \ '^V' : 'V·Block', \ 's' : 'Select', \ 'S' : 'S·Line', \ '^S' : 'S·Block', \ 'i' : 'Insert', \ 'R' : 'R', \ 'Rv' : 'V·Replace', \ 'c' : 'Command', \ 'cv' : 'Vim Ex', \ 'ce' : 'Ex', \ 'r' : 'Prompt', \ 'rm' : 'More', \ 'r?' : 'Confirm', \ '!' : 'Shell', \ 't' : 'Terminal' \} " Change statusline colour based on mode function! ChangeStatuslineColor() if (mode() ==# 'i') exe 'hi StatusLine ctermbg=black ctermfg=032' elseif (mode() =~# '\v(v|V)') exe 'hi StatusLine ctermbg=black ctermfg=172' else exe 'hi Statusline ctermbg=white ctermfg=black' endif return '' endfunction " Get git branch in statusline function CurrentGitBranch() let gitoutput = system('git status -b '.shellescape(expand('%')).' | head -1 | grep -oE "[^ ]+$" | tr -d "[:cntrl:]"') if gitoutput =~ "invalid" let b:gitstatus = '' else let b:gitstatus = gitoutput endif endfunc autocmd BufEnter,BufWritePost * call CurrentGitBranch() " Statusline " left side set statusline=%{ChangeStatuslineColor()} " Change colour set statusline+=\ %-8.{toupper(g:currentmode[mode()])} " Current mode set statusline+=\ \|\ " Vert-line and space set statusline+=%t " File name set statusline+=\ \|\ " Vert-line and space set statusline+=%{b:gitstatus} " git branch set statusline+=%= " Switch to right side " right side set statusline+=%m%r " Modified and read only flags set statusline+=\ "Space set statusline+=%y " File type set statusline+=\ \|\ " Space, Vert-line and space set statusline+=%3.p%% " Percentage through file - min size 3 set statusline+=\ \|\ " Vert-line and Space set statusline+=%8.(%4.l:%-3.c%) " Line and column number in group set statusline+=\ " Space
Less important, but still something that lightline used to do which now I do myself is the tabline. With lightline the tabline looks like this:
Setting the tabline manually isn't as easy as the statusline. Technically you can set the tabline using the same code as the statusline (e.g. set tabline=%t), but this will result in the following, regardless of how many tabs are actually open:
I don't entirely understand how the tabline works at the moment, so I borrowed this tabline function from this website[2]:
2: http://dhruvasagar.com/2014/04/06/vim-custom-tabline
function! MyTabLine() let s = '' for i in range(tabpagenr('