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  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  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!