Saturday, May 30, 2015

Re: My approach to a custom status line

On Saturday, May 30, 2015 at 3:45:07 PM UTC-5, Nicola wrote:
> Hi,
> I have recently re-implemented my status line from scratch. Since getting
> to my (hopefully final) solution took a while (and help from this group) I'm
> giving back my approach, in the hope that it may be useful to other
> non-expert Vim users, and maybe to get feedback to improve it even further.
> If you don't feel like reading this somewhat lengthy post, my code is here:
>
> https://github.com/lifepillar/lifepillar-vim-config/blob/master/vimrc#L480
>
> My requirements for a status line are as follows:
>
> 1. show slightly different information for active and inactive status lines;
> 2. display the current mode in the active status line;
> 3. color some parts of the active status line with a second color (besides
> the background) that depends on the current mode;
> 4. do not render everything slow as hell :)
>
> After trying at least three different ways to reach such goals, with varying
> amounts of bugs, and following some advice from the group, I have
> decided to keep it simple, which means:
>
> 1. set g:statusline once, and never redefine it;
> 2. do not use l:statusline, so that plugins (e.g., CtrlP) can override my
> status line without the need for me to add custom logic depending on
> the plugin;
> 3. try to avoid Ex-mode for performance;
> 4. do not maintain buffer or window variables;
> 5. do not use autocommands.
>
> Avoiding 4 and 5 almost surely means avoiding subtle bugs that are hard
> to fix (at least for me).
>
> The main problem is: how does a function that builds a status line know
> whether it is building the status line for the active window? This is
> not trivial
> because (see :h statusline), winnr() is evaluated differently depending on the
> context. Instead of fighting against Vim, I have decided to follow its logic,
> which inevitably leads to define:
>
> set statusline=%!BuildStatusLine(winnr())
>
> Here, winnr() is always the number of the currently *active* window.
> As far as I know, there is no way for BuildStatusLine() to know which window
> it is building a status line for, but it can put the information it knows
> (the number of the active window) into the status line's string. So,
> the minimal
> BuildStatusLine() is:
>
> func! BuildStatusLine(nr)
> return '%{ReallyBuildStatusLine(' . a:nr . ')}'
> endfunc
>
> Since ReallyBuildStatusLine() is evaluated in %{} context, winnr() returns the
> number of the window *that the status line belongs to*. So, a minimal
> implementation is:
>
> func! ReallyBuildStatusLine(nr)
> if (winnr() == a:nr)
> " return active status line
> else
> " return inactive status line
> endif
> endfunc
>
> Discovering that things were this simple was a "a-ha" moment for me. Of course,
> things are not that simple :) If all you want is putting different
> strings in different
> status lines, this is all you need, as far as I can see. But if you want to use
> '%' items, say highlight groups, you'll soon discover that you cannot use them
> in the latter function, because they will be taken as literal strings and not
> interpolated.
>
> To solve this problem, at first I have used a trick like this:
>
> func! SomeText(nr, flag)
> return (winnr() == a:nr) ? (flag ? 'text' : '') : (flag ? '' : 'text')
> endfunc
>
> func! BuildStatusLine(nr)
> return '%#MyHighlight#%{SomeText(' . a:nr . ',1)}%*%{SomeText(' .
> a:nr . ',0)}...'
> endfunc
>
> It works, but it's ugly. Eventually, I decided to redefine a highlight
> group on-the-fly:
>
> func! SetHighlight(nr)
> if (winnr() == a:nr)
> hi! link StlHighlight MyHighlight
> else
> hi! link StlHighlight StatusLineNC
> endif
> return ''
> endfunc
>
> func! BuildStatusLine(nr)
> return '%{SetHighlight(' . a:nr . ')}%#StlHighlight#%{SomeText()}%*...'
> endfunc
>
> I thought this would be slow, but in my (limited) benchmarks this seems
> to perform
> quite well.
>
> So, in the end, I have a bi-colored collapsible status line highlighting the
> current mode (and showing other information), which can be drawn in 1.5x-2x
> time compared to a basic status line. My approach may not be general enough
> to cover all use cases, but I'm pretty satisfied with it! If you know
> how I can do
> better than this, please let me know!
>
> Nicola

Have you ever tried vim-airline?

https://github.com/bling/vim-airline

--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vim_use+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments: