If a file has a large number of lines, I'd like to enable line numbers, otherwise leave them off by default. If I run the command:

if line('$') > 150 | :set number | endif

while vim is open, file lines are enabled/unchanged as expected. Putting this line into my init.vim (for neovim) has no effect. What am I doing incorrectly? Thanks!


Scripts in your init.vim are only executed when you first launch Neovim, or when you source the file manually (same goes for vimrc and vim).

To trigger a script automatically, based on an event, you can use an autocommand.

For your case, this should do the trick:

augroup ShowLineNumberForLongFiles
  autocmd BufEnter * if line('$') > 150 | setlocal number | endif
augroup END

This will execute your script whenever you enter a new buffer. The first line of the group (autocmd!) is there to clear the group content. This helps to avoid piling up scripts if you ever source your config file manually.

Notice I change set to setlocal to make sure the option is only applied to the current buffer.

Here are some related help topics you can read for deeper explanations:

  • :h autocommand
  • :h :autocmd
  • :h BufEnter
  • :h augroup
  • :h :source
  • :h set
  • :h setlocal
  • @BLayer Thanks for your feedback. I've been posting here for over a year now and still feel I'm very green, so I'm still learning the customs of this friendly community. At first, I though the timestamp would be enough to let anyone know what came first, but I understand this situation could cause frustration. I honestly don't care about a few rep points, so I'll delete this answer later (when you've seen this comment), it feels like the right thing to do. I'll be more careful in the future. I hope you get it I never meant to cheat or upset anyone! Cheers.
    – Biggybi
    Feb 4 '21 at 13:41
  • You're considerate response tells me you'll have no problem continuing to be a valuable and welcome contributor here. BTW, this place is a bit friendlier and a lot less competitive than Unix/Linux was...I'd fairly regularly see disputes about this kind of thing there. It's pretty uncommon here. (Granted there's a lot more traffic there.) Anyways, I have no desire to "punish" you for something that you weren't aware of so please don't delete your answer. (And I'll be deleting my comments soon just because they're not about the questions/answers.) Cheers!
    – B Layer
    Feb 4 '21 at 14:28
  • FWIW, having multiple good answers is considered a sign of a healthy community. I like that this answer points out the exact help topics and explicitly calls out the use of setlocal. While the rest is substantively similar to parts of the other answer, I think both have value (hence the upvotes). Just my opinion.
    – D. Ben Knoble
    Feb 4 '21 at 18:03
  • Probably this one didn't need the extra outside opinion given that we concluded things so nicely. You point out use of local setting when mine has the same thing ("uses the buffer-specific setting rather than the global setting").
    – B Layer
    Feb 4 '21 at 20:19
  • 1
    My take is different. I think it was gratuitous and acts to undermine what I said...a moderator's words have weight. Anyways, Biggybi I'm telling you there's no need to feel bad about anything! Like you said it's just a few rep points. You need them more than I do. :)
    – B Layer
    Feb 5 '21 at 0:59

An auto command is appropriate here...

autocmd BufReadPost * if line('$') > 150 | :setlocal number | else | :setlocal nonumber | endif

OR, you can use this nice enhancement care of MartinTournoij. It has the same result but takes up a lot less space:

autocmd BufReadPost * let &l:number = line('$') > 150

Explanation: First, &l:number is a synonym, if you will, of the setting altered by setlocal number. The conditional line('$') > 150 returns, essentially, either true or false. Since 'number' should be set to true or false also we can just do a direct assignment to it from the conditional result let &l:number = ....

Either way, this autocommand approach accomplishes a few things...

  1. Ensures a check is made after any buffer is loaded (when number of lines is known!).
  2. Uses the buffer-specific setting rather than the global setting.
  3. Explicitly enables or disables as appropriate to ensure correct setting.

Note that if a file has a modeline that sets/unsets line numbers that has priority and will override this.

If you've not used auto commands before they usually go in your init.vim file. You should enclose them in an auto command group like this...

augroup InitVimAutocmds
   [auto commands go here]
augroup END

This ensures they don't get entered multiple times if you reload init.vim.

Update: I don't know if you were thinking about this but it's worth pointing out (another nod to MartinTournoij) that this is static, i.e. the line numbering is determined upon buffer load and if you subsequently surpass or drop below 150 lines nothing will change. It is definitely possible to extend this so it's dynamic. If that's something you're interested in let me know.

  • 2
    This can be a bit shorter with: let &l:number = line('$') > 150; also it won't set number if the lines grow to >150 while editing. I don't know if that's a requirement, but just something to be aware of. Can add CursorHold,CursorHoldI or something for this. Feb 4 '21 at 5:08
  • On the first item, good suggestion. On the second, good point. Though I didn't see anything from OP to suggest they needed that I'll note it.
    – B Layer
    Feb 4 '21 at 5:09
  • @D.BenKnoble You're talking about if it were made dynamic per the last paragraph of my answer?
    – B Layer
    Feb 4 '21 at 17:52
  • @BLayer facepalm I sort of skipped over that paragraph. Woops
    – D. Ben Knoble
    Feb 4 '21 at 18:01
  • @D.BenKnoble I had a feeling. :)
    – B Layer
    Feb 4 '21 at 18:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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