LSP-based "sticky headers" to keep semantic context visible at the top of a window

Hi all!

I just saw this post that demos one of the new features in xcode: “sticky headers”.

This would be extremely useful when browsing a codebase with which you’re not already familiar (e.g. when visiting locations produced by grep).

Does LSP already support this (possibly under a different name/phrase)?

Does neovim need anything special to support it? Does it need anything from LSP that LSP does not already provide?

I guess in vim terms, it’s kind of like a set of dynamic folds, except that the fold lines are not displayed…

Or maybe it’s more accurate to say that the context lines are overlayed at the top of the window if those lines would otherwise be above the visible region?

I guess this raises questions about what the desired behavior would be if the cursor was just under the last context line, and the user hit k or <up>: presumably if the cursor is on line i, and the lowest context line is line j, and you hit <up>, you want to go to line i-1, not line j (unless j == i-1).

That said, you probably also want a motion command that jumps to the [count]th context line.

This would also imply that the cursor is never on a context line. (If you move/jump to a context line, it stops being a context line.)

Is there already a github issue for this?

It looks like you are searching for nvim-treesitter-context.

It can also be extended configured to show more elements as context like if/then/else above, or where you are in a JSON document.

Edit:
I’m aware you were asking for an LSP based solution, but coming from the xcode video and your example of quickfix list jumps and seeing their contexts I assumed the plug-in above would solve your issue, at least partially.

2 Likes

Maybe!

But I would be using this with C++ code, which made me look for a solution based on LSP (and therefore clangd), because Tree-sitter appears to be based on its own parsers.

Having maintained a 3rd-party C++ parser myself, and having an awareness of several areas in the ISO C++ standard that explicitly mention grammar ambiguities that must be resolved by semantic analysis (and having implemented some of those disambiguations), and having spent years helping many customers to configure that parser to emulate several different compilers so that our parser would have a relatively sane interpretation of what it sees while processing system headers for many different platforms, I have learned to refrain from trusting any C++ parser other than the one used by the compiler that was used to generate the binaries that I ship. (E.g. I trust the clangd that ships with the version of clang that I use in a given project, and I configure it to read the compile_commands.json generated by CMake for a particular build config.) Anything short of that is going to be inherently fragile, and can & will break in a number of different ways (which I would be happy to enumerate if given enough alcohol), and I’ve already spent too much of my life trying to pretend otherwise. So when I see a src directory like this, I say “nope”, and close the browser tab.

Having said that, I would love to use a version of Tree-sitter that delegates to clangd and consumes the AST sub-trees that clangd generates on request.

So I guess another question I should ask (which probably takes this out of the realm of neovim) is: “can Tree-sitter be configured to delegate to clangd?” (It should, because trying to do home-grown C++ parsing is always incorrect.)

How far you want to go for showing a better context is totally up to you.

Your best guess is probably checking clangd custom LSP commands for something that could return the context. To my knowledge the LSP specification doesn’t have something like this.

You find all information on treesitter here, including how to write parsers.

it sort of goes beyond context lines though…

Like, I’ve looked at tree-sitter before, and I want to use it (for all of the other reasons that you would normally use tree-sitter), but without support from the C++ compiler, tree-sitter will not produce trees correctly (at least, not consistently).

Note, I’m not saying that tree-sitter has a bug in their C++ parser. I am saying that there is nothing that they could do to make their parser behave correctly. The problem is that it is not possible to correctly parse C++ code without delegating to the C++ compiler. So when I see that Tree-sitter has a “C++ parser” that does anything other than delegate to clang, I can say with confidence that it’s going to do the wrong thing.