Skip to content

fix: "invalid 'line'" when hidden_display is set and tree is empty - #3354

Merged
alex-courtis merged 2 commits into
nvim-tree:masterfrom
azazeal:fix-hidden-display-no-rows
Sep 7, 2026
Merged

fix: "invalid 'line'" when hidden_display is set and tree is empty#3354
alex-courtis merged 2 commits into
nvim-tree:masterfrom
azazeal:fix-hidden-display-no-rows

Conversation

@azazeal

@azazeal azazeal commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

renderer.hidden_display raises whenever a directory is left with no visible rows and the root row is not drawn.

Reproduction, with renderer.root_folder_label = false and renderer.hidden_display = "simple":

mkdir -p /tmp/one && printf x > /tmp/one/.envrc && cd /tmp/one && nvim

Open the tree and press H. The only entry is a dotfile, so hiding dotfiles empties the tree:

renderer/init.lua:65: Invalid 'line': out of range
stack traceback:
	[C]: in function 'nvim_buf_set_extmark'
	renderer/init.lua:65: in function '_draw'
	renderer/init.lua:102: in function 'draw'
	explorer/init.lua:514: in function 'reload_explorer'
	explorer/filters.lua:285: in function 'toggle'
	api/impl.lua:148: in function 'toggle'

Builder:add_hidden_count_string hangs the count under the last line that was drawn, #self.lines - 1. For the root, that line is its last child. With every child filtered out there are no lines at all, so the index is -1 and Renderer:_draw hands it to nvim_buf_set_extmark. The default root_folder_label hides the whole thing, because the root row is then a line of its own and #self.lines is never 0; setting it to false is what makes the case reachable. Only the root can reach zero lines, since a nested open directory is always preceded by its own row.

Two things follow from the raise, which is why it is worth more than the error text. _draw has already called nvim_buf_set_lines by the time it raises, so the rows are gone from the buffer but the window is never redrawn: the tree stays painted with entries that are no longer there. And when the draw comes from the filesystem watcher, the raise happens inside utils.debounce, which sets executing = true before the callback and clears it after, so the flag stays set and every later event for that directory reschedules forever. That directory stops refreshing for the rest of the session. I found this by deleting the last visible file from the tree and being left looking at a tree that still listed a file that was no longer on disk.

Solution

nvim_buf_set_lines(bufnr, 0, -1, false, {}) leaves the buffer holding one empty line, so line 0 always exists. Clamping the index to it puts the count under that empty line, where it renders on its own:

virt_line at row 0: "  (1 hidden)"

I considered skipping the count when there are no lines instead. That hides the information at the one moment it matters most: a tree that is empty with nothing to say why reads as a broken tree, which is the case hidden_display exists for.

Testing

A minimal config pointed at this branch, run against both values of root_folder_label, before and after the change. Only false raises, and only before it:

unpatched  root_folder_label=1  no raise  rows=[???]
unpatched  root_folder_label=0  RAISED: renderer/init.lua:65: Invalid 'line': out of range  rows=[]
patched    root_folder_label=1  no raise  rows=[???]
patched    root_folder_label=0  no raise  rows=[]

The extmark output above is from the same config after the change, so the count is drawn and not merely not raising.

Also run against my own config, which sets root_folder_label = false and passes a function for hidden_display. Seven cases, driven over a socket on a real editor with a pty: an empty directory, a directory of dotfiles with dotfiles hidden, a directory of git-ignored files with those hidden, a directory holding only .git, H taking the last row, deleting the last visible file, and a control with rows in it. Five of them fail on master and all seven pass with this change.

scripts/doc-comments.sh passes. luacheck, CodeFormat and luals are not installed on this machine, so make lint, make style and make check were not run; CI will be the first to run them.

AI usage

Per CONTRIBUTING.md, disclosed in full.

Claude (Anthropic's Claude Code) was used. It wrote the one-line change, the comment above it, and this description, and it ran the tests above on my machine at my direction. Nothing in the diff was written by hand. I have read the change and the reasoning behind it and I am accountable for it.

No AI review tools are enabled on this pull request.

@azazeal azazeal closed this Sep 4, 2026
@azazeal
azazeal deleted the fix-hidden-display-no-rows branch September 4, 2026 21:48
@azazeal
azazeal restored the fix-hidden-display-no-rows branch September 4, 2026 21:49
@azazeal azazeal reopened this Sep 4, 2026
@alex-courtis

alex-courtis commented Sep 6, 2026

Copy link
Copy Markdown
Member

Claude (Anthropic's Claude Code) was used. It wrote the one-line change, the comment above it, and this description, and it ran the tests above on my machine at my direction. Nothing in the diff was written by hand. I have read the change and the reasoning behind it and I am accountable for it.

No AI review tools are enabled on this pull request.

"The PR description and comments must be written by the contributor, with no AI assistance beyond grammar or English translations."

In the future, please write the PR description yourself. You are welcome to use AI to assist, however the PR itself should be human written. Keep it brief, concise and to the point. The above wall of text is difficult to read.

@alex-courtis

Copy link
Copy Markdown
Member

CodeFormat and luals are not installed on this machine, so make lint, make style and make check were not run; CI will be the first to run them.

In your future PRs, especially for more complex changes, it's recommended to install the tools so that you do not need to wait for CI.

@alex-courtis alex-courtis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix works as per the test case, as well as hidden_display "all" and function.

Please remove the unnecessary comment.

Comment thread lua/nvim-tree/renderer/builder.lua Outdated
Comment on lines +343 to +346
-- The count hangs under the last line that was drawn. The root has no
-- such line when every one of its children is filtered out: line 0 is
-- the empty line the buffer is left with, and is where the count goes
-- instead of line -1, which nvim_buf_set_extmark rejects.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this comment; referencing a fixed bug has no value to future maintainers.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines 343 to 346 were dropped. Thank you for the polite review.

@azazeal azazeal Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if you prefer a blank line in there, between locals and anything else. Please let me know what your preference is. Dropping 343 to 346 left a blank line, which I removed to get this to a 1 line diff.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if you prefer a blank line in there, between locals and anything else. Please let me know what your preference is. Dropping 343 to 346 left a blank line, which I removed to get this to a 1 line diff.

Looks good, matches the surrounding code style.

@azazeal
azazeal force-pushed the fix-hidden-display-no-rows branch from 7e5466b to 4fcd086 Compare September 7, 2026 09:21
@azazeal
azazeal force-pushed the fix-hidden-display-no-rows branch from 4fcd086 to 801fdda Compare September 7, 2026 09:23
@azazeal
azazeal requested a review from alex-courtis September 7, 2026 09:25

@alex-courtis alex-courtis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many thanks for your contribution!

@alex-courtis alex-courtis changed the title fix: hidden_display raises when every row is filtered out fix: "invalid 'line'" when hidden_display is set and tree is empty Sep 7, 2026
@alex-courtis
alex-courtis merged commit 882c54f into nvim-tree:master Sep 7, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants