3

I would like to disable or hide Lightline (i.e. vim's statusline) when my cursor is in Nerdtree, while keeping it activated for other buffers.

Here is what I mean:

image

How can I achieve this?

Lightline's developper says it is not possible with Lightline itself : issue 193, issue 76

So I tried setlocal statusline=%#Normal# when I BufEnter nerdtree filetype, as suggested here :

augroup filetype_nerdtree
    autocmd!
    autocmd BufEnter,FileType nerdtree setlocal statusline=%#Normal#
augroup END

... which seems to work only when I toggle Nerdtree (:NERDTreeToggle). As soon I move my cursor to another buffer, Lightline fills vim's statusline up again with whatever I put in my g:lightline variable (in both Nerdreee and other buffers).

I use neovim 0.3.8.

3
  • If you're ok with the status line of the nerdtree window sometimes flickering, you could try this: 0x0.st/zkg-.txt
    – user938271
    Jan 2, 2020 at 8:59
  • Thanks a lot. It works very well, I don't see any flickering. I'm still trying to understand what your snippet do because I am new to vim scripting. But I guess it basically sets window's statusline to%#Normal# whenever the corresponding buffer is equal to 'nerdtree', on WinLeave and TabEnter events, does it ? Would you mind posting your code in a individual response so I can accept your answer? Anyway, thanks again.
    – vittorio
    Jan 2, 2020 at 20:33
  • I just realized that the statusline's change doesn't trigger when I open a new buffer from NerdTree (it triggers when I leave NerdTree towards an existing buffer or open a new tab). So I added the BufEnter event to the `timer_start' call, and now it works in that scenario as well.
    – vittorio
    Jan 2, 2020 at 21:13

2 Answers 2

5

It's a hack, but you could try this:

augroup filetype_nerdtree
    au!
    au FileType nerdtree call s:disable_lightline_on_nerdtree()
    au WinEnter,BufWinEnter,TabEnter * call s:disable_lightline_on_nerdtree()
augroup END

fu s:disable_lightline_on_nerdtree() abort
    let nerdtree_winnr = index(map(range(1, winnr('$')), {_,v -> getbufvar(winbufnr(v), '&ft')}), 'nerdtree') + 1
    call timer_start(0, {-> nerdtree_winnr && setwinvar(nerdtree_winnr, '&stl', '%#Normal#')})
endfu

I'm still trying to understand what your snippet do because I am new to vim scripting. But I guess it basically sets window's statusline to%#Normal# whenever the corresponding buffer is equal to 'nerdtree', on WinLeave and TabEnter events, does it ?

Yes, that's what it does. Although, now I think that WinEnter, BufWinEnter and TabEnter is a better set of events.

This displays the number of the last window in the current tab page:

:echo winnr('$')

This displays the numbers of all the windows in the current tab page, as a list:

:echo range(1, winnr('$'))

This displays the filetypes of the buffers displayed in all the windows of the current tab page:

                                   ┌ iterates over the numbers of the windows
                                   │              ┌ evaluates to the number of the buffer displayed in the window whose number is v
                                   │              ├─────────┐
:echo map(range(1, winnr('$')), {_,v -> getbufvar(winbufnr(v), '&ft')})
                                        ├───────────────────────────┘
                                        └ evaluates to the filetype of the buffer whose number is winbufnr(v)

This displays the number of the window displaying a nerdtree buffer (or 0 if there is none):

:echo index(map(range(1, winnr('$')), {_,v -> getbufvar(winbufnr(v), '&ft')}), 'nerdtree') + 1

Finally, this sets the local value of the 'statusline' option bound to the window displaying the nerdtree buffer to %#Normal#:

:call setwinvar(index(map(range(1, winnr('$')), {_,v -> getbufvar(winbufnr(v), '&ft')}), 'nerdtree') + 1, '&stl', '%#Normal#')

Now you could execute this last function call directly from an autocmd, but there would still be 2 issues:

  1. the function would be called even when there is no nerdtree buffer displayed in the tab page, which would disable the status line in the current window
  2. Lightline would still update the status line contents of the nerdtree window after your function call, undoing your reset

To fix 1., you need a guard which makes sure that a window is currently displaying a nerdtree buffer:

let nerdtree_winnr = index(map(range(1, winnr('$')), {_,v -> getbufvar(winbufnr(v), '&ft')}), 'nerdtree') + 1
if nerdtree_winnr
    call setwinvar(nerdtree_winnr, '&stl', '%#Normal#')
endif

To fix 2., you need to delay the function call until Lightline has finished updating all the status lines. You can do so with a timer:

call timer_start(0, {-> nerdtree_winnr && setwinvar(nerdtree_winnr, '&stl', '%#Normal#')})
     ^^^^^^^^^^^        ^^^^^^^^^^^^^^^^^
     timer              guard

You can make the nesting of function calls inside index() a little more readable, using the method call operator -> (introduced in 8.1.1803):

let nerdtree_winnr = winnr('$')->range()->map({_,v -> winbufnr(v+1)->getbufvar('&ft')})->index('nerdtree') + 1

It works very well, I don't see any flickering.

I can definitely see some flickering. If – in the future – you do too, try to replace the timer with a one-shot autocmd listening to the SafeState event (introduced in 8.1.2044):

let nerdtree_winnr = index(map(range(1, winnr('$')), {_,v -> getbufvar(winbufnr(v), '&ft')}), 'nerdtree') + 1
if nerdtree_winnr
    exe 'au SafeState * ++once call setwinvar('..nerdtree_winnr..', "&stl", "%#Normal#")'
    "                   ^^^^^^
    "                   fire only once
endif

During my limited tests, it seemed to fix the issue.


If one day you decide to use your own status line, you could use this template to achieve a similar effect:

filetype on
set ls=2
fu s:snr() abort
    return matchstr(expand('<sfile>'), '.*\zs<SNR>\d\+_')
endfu
let s:snr = get(s:, 'snr', s:snr())
let &stl = '%!'..s:snr..'stl()'
fu s:stl() abort
    return getbufvar(winbufnr(g:statusline_winid), '&ft', '') is# 'nerdtree'
        \ ? '%#Normal#'
        \ : '...'
    "        ^^^
    "        here, write the contents of your status line for filetypes other than nerdtree
endfu

It requires the patch 8.1.1372 to be able to inspect g:statusline_winid. The latter is a temporary global variable which contains the ID of the window to which the status line currently being drawn belongs.


Note that neither the method call operator ->, nor the SafeState event, nor the g:statusline_winid variable are available in Nvim.

If you know C, you could try to port the relevant Vim patches.


For more info, see:

:h index()
:h map()
:h range()
:h winnr()
:h getbufvar()
:h winbufnr()
:h setwinvar()
:h timer_start()
:h lambda
:h SafeState
:h autocmd-once
:h g:statusline_winid
6
  • Thank you very much for this very detailed answer. That solved my issue. Your explanations on the code you shared are greatly appreciated. They helped me better understand how vim scripting works. About making my own status line, it seems to me a bit too early to do that kind of stuff for now, but I will definitely use the template you shared as soon as I feel more comfortable with vim scripting.
    – vittorio
    Jan 3, 2020 at 21:04
  • Wonderfully detailed answer. Only nit is that I would really love to see non-abbreviated code. It’s harder to read and maintain when it’s abbreviated
    – D. Ben Knoble
    Jan 4, 2020 at 4:55
  • very quick note: I added ! to fu to not get the message "the function ... already exists" when I source my vimrc : fu! s:disable_lightline_on_nerdtree() abort
    – vittorio
    Jan 4, 2020 at 17:26
  • 1
    You need it because you're using Nvim 0.3.8. Starting from 0.4.0, you won't need it anymore, because Nvim merged the patch 8.1.0515 last september.
    – user938271
    Jan 5, 2020 at 2:19
  • You could install a more recent version reading this or this. You could also autoload your function; see this or :h autoload.
    – user938271
    Jan 5, 2020 at 2:19
3

Of course, your attempt won't work, because Lightline forcefully updates status line content on many auto events. That's quite obvious from its code and was clearly stated by the author in the issues you've quoted.

The only solution is to exclude some specific buffers from such update, but, again, as the author said he is not going to do so for several reasons.

Therefore, you have either (1) fork Lightline and add such functionality yourself, or (2) drop it and build a status line of your own, preferably without any tricky event-driven auto-updates, so Vim can manage everything by himself (using expression re-evaluation).

Personally, I prefer (2) even though Vim is not perfect and things may go out of sync on rare occasions.

3
  • 2
    Anecdote: (2) has been nicer for me in the long run, as I put things I actually care about in the statusline and keep it unobtrusive. I even managed to make a short and long version of it I can toggle between
    – D. Ben Knoble
    Jan 2, 2020 at 14:20
  • @D.BenKnoble I even ended up in writing a whole plugin to compose such status line(s) from pieces ;-)
    – Matt
    Jan 2, 2020 at 14:56
  • Thank you for your response and advice. I understand that implementing such a feature in Lightline is complicated. I will look into the possibility of making my own status bar, as you mentioned.
    – vittorio
    Jan 2, 2020 at 20:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.