/ src / content / posts / vim-statusline.mdx
vim-statusline.mdx
  1  ---
  2  title: Making your own statusline in (Neo)vim
  3  date: 2020-02-15
  4  description: A post where I made my own statusline to make my vim looks more personalised
  5  tags:
  6      - neovim
  7  ---
  8  
  9  import Update from "~/components/Update.astro";
 10  
 11  # Introduction
 12  
 13  Hi everyone! In this post, I will talk about making your own custom statusline in vim. There are a lot of plugins out there that makes vim statusline looks way better and works out of the box. But, if you make your own, that means you lose one dependency and it feels good to make your own custom one. That makes it unique compared to anyone else.
 14  
 15  The reason why I made this post is also because I want to change my statusline. While my current statusline looks eye candy (to me at least), it takes a whole lot of space. So, I want to simplifiy it and why not make that process as a post. Let's get into it!
 16  
 17  <Update date="2020-11-29">
 18  
 19  I wrote a better version of this in Lua which you can read [here](https://elianiva.my.id/posts/neovim-lua-statusline)
 20  
 21  Disclaimer: I don't know if the code here still works or not.
 22  
 23  </Update>
 24  
 25  # Prerequisite
 26  
 27  First of all, we need to prepare a few things :
 28  
 29  -   (Neo)Vim Text Editor (Duh, isn't that obvious?).
 30  -   Terminal that is capable of true colours
 31  -   Patience
 32  -   Googling skills incase something doesn't work correctly
 33  
 34  All is set, let's actually make the statusline!
 35  
 36  # Creating the statusline
 37  
 38  ## Deprecating the old one
 39  
 40  First thing first, I removed my old statusline. You don't need to do it if you don't have it already. If you are curious how my statusline looks, let me show you.
 41  
 42  ![old statusline](/assets/posts/vim-statusline/old.png)
 43  
 44  As you can see, it looks like a capsule for each module. I took the design from a reddit post that I've found the other day. [Here it is](https://www.reddit.com/r/vimporn/comments/efjcv0/gruvboxxx/?utm_source=share&utm_medium=web2x). It looks sick when I saw it for the first time. But, as time passes I started to think that it wasted quite a lot of space. So I decided to change it
 45  
 46  ## Making the structure
 47  
 48  Let's start with the structure of the statusline. Create 2 functions for your statusline as so.
 49  
 50  ```vim
 51  " We'll use this for the active statusline
 52  function! ActiveLine()
 53    let statusline = ""
 54    return statusline
 55  endfunction
 56  
 57  " We'll use this for the inactive statusline
 58  function! InactiveLine()
 59    let statusline = ""
 60    return statusline
 61  endfunction
 62  ```
 63  
 64  ## Base colour
 65  
 66  Next, we'll define the base colour for the background. I chose a lighter colour for the background so it stands out. To add a base colour, you need to add `%#Base#` where `Base` is the name of the color highlight. To set a colour highlight, you'd do:
 67  
 68  ```vim
 69    hi Base guibg=#212333 guifg=#212333
 70  ```
 71  
 72  You can freely change the colours as you like. The colour are set, let's apply it to our statusline. To apply it, you'd do:
 73  
 74  ```vim
 75  " We'll use this for the active statusline
 76  function! ActiveLine()
 77    let statusline = ""
 78    let statusline .= "%#Base#"
 79    return statusline
 80  endfunction
 81  ```
 82  
 83  ## Modes indicator
 84  
 85  Let's make a module for out statusline because so far, what we did is just setting the background colour. The most importan part for me is the indicator for the mode that you're currently in. To do that, you'd add:
 86  
 87  ```vim
 88    let g:currentmode={
 89        \'n' : 'Normal ',
 90        \'no' : 'N·Operator Pending ',
 91        \'v' : 'Visual ',
 92        \'V' : 'V·Line ',
 93        \'^V' : 'V·Block ',
 94        \'s' : 'Select ',
 95        \'S': 'S·Line ',
 96        \'^S' : 'S·Block ',
 97        \'i' : 'Insert ',
 98        \'R' : 'Replace ',
 99        \'Rv' : 'V·Replace ',
100        \'c' : 'Command ',
101        \'cv' : 'Vim Ex ',
102        \'ce' : 'Ex ',
103        \'r' : 'Prompt ',
104        \'rm' : 'More ',
105        \'r?' : 'Confirm ',
106        \'!' : 'Shell ',
107        \'t' : 'Terminal '
108        \}
109  
110  " Get current mode
111  function! ModeCurrent() abort
112      let l:modecurrent = mode()
113      let l:modelist = toupper(get(g:currentmode, l:modecurrent, 'V·Block '))
114      let l:current_status_mode = l:modelist
115      return l:current_status_mode
116  endfunction
117  ```
118  
119  Just calm down, don't get intimidated by the code. It looks like much, but it's just a list to indicate what mode you're currently in. Make sure you place that on top of the `ActiveLine` function. You don't need to understand all of that. All you need to know is, _It just works._
120  
121  Let's add some colours for that module. It's the same like before, you add `%#Mode#` where `Mode` is the name for highlight group. Set the colour for the highlight as so:
122  
123  ```vim
124  hi Mode guibg=#82aaff guifg=#181824 gui=bold
125  ```
126  
127  It will give the `Mode` module a blue background and a dark colour for the text. It will also make the text bold. Let's ppply it to our statusline once again.
128  
129  ```vim
130  " We'll use this for the active statusline
131  function! ActiveLine()
132    let statusline = ""
133    let statusline .= "%#Base#"
134  
135    " Current mode
136    let statusline .= "%#Mode# %{ModeCurrent()}"
137    return statusline
138  endfunction
139  ```
140  
141  ## Git integration
142  
143  Being able to see your git branch on your statusline is great. So, let's do that! First thing first, you'll need some kind of git plugin to show the git status (I think it's possible without it, but I'm not sure). I'll use a vim plugin called [vim-fugitive](https://github.com/tpope/vim-fugitive). It's not only for this reason, it has a lot of useful command too!
144  
145  Let's create the module for that. First thing first, the branch name that you're currently in.
146  
147  ```vim
148  " Get current git branch
149  function! GitBranch(git)
150    if a:git == ""
151      return '-'
152    else
153      return a:git
154    endif
155  endfunction
156  ```
157  
158  Create the colours for that module and apply it by doing so:
159  
160  ```vim
161  hi Git guibg=#292d3e guifg=#929dcb
162  
163  function! ActiveLine()
164    let statusline = ""
165    let statusline .= "%#Base#"
166  
167    " Current mode
168    let statusline .= "%#Mode# %{ModeCurrent()}"
169  
170    " Current git branch
171    let statusline .= "%#Git# %{GitBranch(fugitive#head())} %)"
172    return statusline
173  endfunction
174  ```
175  
176  ## Right Section
177  
178  After creating the left section, let's move to the right part. To move to the right part of the statusline, what you'd do is to add:
179  
180  ```vim
181  " Make the colour highlight normal
182  let statusline .= "%#Base#"
183  let statusline .= "%="
184  ```
185  
186  What that block code is doing is:
187  
188  -   It normalize the colour of the background
189  -   Move the next module to the right
190  
191  ## Filename
192  
193  I want to make the filename module as the first module for the right section. I also added the feature where if your file isn't saved yet, it'll give a star symbol at the end of the filename, change the colour to white and make it bold.
194  
195  ```vim
196  " Check modified status
197  function! CheckMod(modi)
198    if a:modi == 1
199      hi Modi guifg=#efefef guibg=#212333 gui=bold
200      hi Filename guifg=#efefef guibg=#212333
201      return expand('%:t').'*'
202    else
203      hi Modi guifg=#929dcb guibg=#212333
204      hi Filename guifg=#929dcb guibg=#212333
205      return expand('%:t')
206    endif
207  endfunction
208  ```
209  
210  Then we'll add it to our previous statusline like we did for the other modules.
211  
212  ```vim
213  function! ActiveLine()
214    let statusline = ""
215    let statusline .= "%#Base#"
216  
217    " Current mode
218    let statusline .= "%#Mode# %{ModeCurrent()}"
219  
220    " Current git branch
221    let statusline .= "%#Git# %{GitBranch(fugitive#head())} %)"
222  
223    " Make the colour highlight normal
224    let statusline .= "%#Base#"
225    let statusline .= "%="
226  
227    " Current modified status and filename
228    let statusline .= "%#Modi# %{CheckMod(&modified)} "
229    return statusline
230  endfunction
231  ```
232  
233  ## Filetype
234  
235  Similar to `filename`, filetype module only display a filetype from that file. You know, like `javascript`, `html`, `markdown`, etc. Let's make that module.
236  
237  ```vim
238  " Set the colour
239  hi Filetype guibg=#292d3e guifg=#929dcb
240  
241  " Get current filetype
242  function! CheckFT(filetype)
243    if a:filetype == ''
244      return '-'
245    else
246      return tolower(a:filetype)
247    endif
248  endfunction
249  ```
250  
251  After making it, let's apply it to our statusline.
252  
253  ```vim
254  function! ActiveLine()
255    let statusline = ""
256    let statusline .= "%#Base#"
257  
258    " Current mode
259    let statusline .= "%#Mode# %{ModeCurrent()}"
260  
261    " Current git branch
262    let statusline .= "%#Git# %{GitBranch(fugitive#head())} %)"
263  
264    " Make the colour highlight normal
265    let statusline .= "%#Base#"
266    let statusline .= "%="
267  
268    " Current modified status and filename
269    let statusline .= "%#Modi# %{CheckMod(&modified)} "
270  
271    " Current filetype
272    let statusline .= "%#Filetype# %{CheckFT(&filetype)} "
273    return statusline
274  endfunction
275  ```
276  
277  The reason why I use a function just to display a filetype is to make all of the letter lowercase and display `-` when the filetype is unidentified.
278  
279  ## Line Number
280  
281  Last but not least, it's the line number and line column module. It's used to display the line number that you're currently in. It's a really simple module. Let's make that!
282  
283  ```vim
284  " Colour for line number module
285  hi LineCol guibg=#82aaff guifg=#181824 gui=bold
286  
287  " Current line and column
288  let statusline .= "%#LineCol# Ln %l, Col %c "
289  ```
290  
291  ## Inactive Line
292  
293  We've made the statusline for the active window, let's make one for the inactive window. It's simple.
294  
295  ```vim
296  function! InactiveLine()
297    " Set empty statusline and colors
298    let statusline = ""
299    let statusline .= "%#Base#"
300  
301    " Full path of the file
302    let statusline .= "%#LineCol# %F "
303  
304    return statusline
305  endfunction
306  ```
307  
308  # Apply the statusline
309  
310  Let's see what we've made so far.
311  
312  ```vim
313  " Statusline colors
314  hi Base guibg=#212333 guifg=#212333
315  hi Mode guibg=#82aaff guifg=#181824 gui=bold
316  hi Git guibg=#292d3e guifg=#929dcb
317  hi Filetype guibg=#292d3e guifg=#929dcb
318  hi LineCol guibg=#82aaff guifg=#181824 gui=bold
319  " Get current mode
320  let g:currentmode={
321        \'n' : 'Normal ',
322        \'no' : 'N·Operator Pending ',
323        \'v' : 'Visual ',
324        \'V' : 'V·Line ',
325        \'^V' : 'V·Block ',
326        \'s' : 'Select ',
327        \'S': 'S·Line ',
328        \'^S' : 'S·Block ',
329        \'i' : 'Insert ',
330        \'R' : 'Replace ',
331        \'Rv' : 'V·Replace ',
332        \'c' : 'Command ',
333        \'cv' : 'Vim Ex ',
334        \'ce' : 'Ex ',
335        \'r' : 'Prompt ',
336        \'rm' : 'More ',
337        \'r?' : 'Confirm ',
338        \'!' : 'Shell ',
339        \'t' : 'Terminal '
340        \}
341  
342  " Get current mode
343  function! ModeCurrent() abort
344      let l:modecurrent = mode()
345      let l:modelist = toupper(get(g:currentmode, l:modecurrent, 'V·Block '))
346      let l:current_status_mode = l:modelist
347      return l:current_status_mode
348  endfunction
349  
350  " Get current git branch
351  function! GitBranch(git)
352    if a:git == ""
353      return '-'
354    else
355      return a:git
356    endif
357  endfunction
358  
359  " Get current filetype
360  function! CheckFT(filetype)
361    if a:filetype == ''
362      return '-'
363    else
364      return tolower(a:filetype)
365    endif
366  endfunction
367  
368  " Check modified status
369  function! CheckMod(modi)
370    if a:modi == 1
371      hi Modi guifg=#efefef guibg=#212333
372      hi Filename guifg=#efefef guibg=#212333
373      return expand('%:t').'*'
374    else
375      hi Modi guifg=#929dcb guibg=#212333
376      hi Filename guifg=#929dcb guibg=#212333
377      return expand('%:t')
378    endif
379  endfunction
380  
381  " Set active statusline
382  function! ActiveLine()
383    " Set empty statusline and colors
384    let statusline = ""
385    let statusline .= "%#Base#"
386  
387    " Current mode
388    let statusline .= "%#Mode# %{ModeCurrent()}"
389  
390    " Current git branch
391    let statusline .= "%#Git# %{GitBranch(fugitive#head())} %)"
392  
393    let statusline .= "%#Base#"
394  
395    " Align items to right
396    let statusline .= "%="
397  
398    " Current modified status and filename
399    let statusline .= "%#Modi# %{CheckMod(&modified)} "
400  
401    " Current filetype
402    let statusline .= "%#Filetype# %{CheckFT(&filetype)} "
403  
404    " Current line and column
405    let statusline .= "%#LineCol# Ln %l, Col %c "
406    return statusline
407  endfunction
408  
409  function! InactiveLine()
410    " Set empty statusline and colors
411    let statusline = ""
412    let statusline .= "%#Base#"
413  
414    " Full path of the file
415    let statusline .= "%#Filename# %F "
416  
417    return statusline
418  endfunction
419  ```
420  
421  As you can see, currently we didn't do anything to our current statusline because we haven't apply it yet. So, let's apply it!
422  
423  ```vim
424  " Change statusline automatically
425  augroup Statusline
426    autocmd!
427    autocmd WinEnter,BufEnter * setlocal statusline=%!ActiveLine()
428    autocmd WinLeave,BufLeave * setlocal statusline=%!InactiveLine()
429    autocmd FileType nerdtree setlocal statusline=%!NERDLine()
430  augroup END
431  ```
432  
433  We use autocmd to make the statusline changed automatically based of the current window status. Make sure you've set `laststatus` to 2 so your vim will always display the statusline. If you don't know what I mean, add
434  
435  ```vim
436  set laststatus=2
437  ```
438  
439  to your .vimrc or init.vim
440  
441  Here's what it looks like when it's finished
442  
443  ![new statusline](/assets/posts/vim-statusline/new.png)
444  
445  It doesn't look like an eye candy, but it doesn't take a whole lotta space.
446  If you want the old one, [Here it is](https://github.com/irrellia/dotfiles/blob/0c1ca17af07d7fdf72577a44d2a1e8bbab855d93/.config/nvim/modules/statusline.vim). Just take what you need from that file, it's not that hard to understand ;)
447  
448  <Update date="2020-10-15">
449  
450  Welp, I accidentally lost my old statusline because I `git push --force` the other day. Sorry :p
451  
452  </Update>
453  
454  # Conclusion
455  
456  Making a custom statusline of your own is quite a lengthy process. But, I think it's a fun process nonetheless. If you want to tinker with it even more, just do it! It's a repetitive process once you know the basic. I'm not a vim expert myself, so sorry if I've missed something in this post. Alright then, I'm gonna end this post right here. If you have any question regarding to this post, feel free to hit me up! See ya!