Skip to content

feat(lsp): code lenses#5063

Open
matoous wants to merge 12 commits intohelix-editor:masterfrom
matoous:md/code-lens
Open

feat(lsp): code lenses#5063
matoous wants to merge 12 commits intohelix-editor:masterfrom
matoous:md/code-lens

Conversation

@matoous
Copy link
Copy Markdown
Contributor

@matoous matoous commented Dec 8, 2022

Initial (limited) implementation of LSP code lenses1. This is purposefully as small of a change as possible, it displays code lenses in the gutter and adds a single new command show_code_lenses_under_cursor that displays a picker for code lenses on the current line. Notably, displaying code lenses as annotations for the current line is not implemented and this implementation doesn't support resolving code lenses from multiple language servers. I am happy to address both but would prefer to do so separately to make this PR reviewable amidst the low bandwith and large backlog for maintainers.

Code Lens

From the VS Code features docs2:

The essence of the feature is "actionable contextual information interspersed" in your source code. That's quite a mouthful. Let me break it down for you. CodeLens are links in your code:
Actionable - You can click on the link and something happens.
Contextual - The links are close to the code they are representing.
Interspersed - The links located throughout your source.

And from the official LSP docs3:

A code lens represents a command that should be shown along with
source text, like the number of references, a way to run tests, etc.

Support

The support for code lens varies among LSPs but there are already nice features that can be achieved via code lenses (some of them listed below). The biggest limitation is that some language servers rely on client-side command execution which is usually solved via plugins. For other LSPs code lenses are opt-in only and without plugins can for example only display inline hints about number of references or number of implementations4.

Still, LSP is part of Helix and given Code Lenses are part of the specs, I think it's fair to implement them. Even if - without a plugin system - with limited capabilities.

  • golang
    • generate, vendor, update dependency, and more.
    • test execution isn't supported5
  • rust-analyzer
    • run for tests and main - requires client side execuity
    • references/implementations (show number of references/implementations and can be opened to view them, disabled by default) - requires client side execution
  • deno
    • run test support - requires client side execution

(didn't check more languages).

Future work

As mentioned above this implementation is limited and doesn't provide Code Lens as they should be provided - displayed contextually as a text - close to the code that they apply for. There should be a follow up PRs for:

  • supporting multiple language servers
  • displaying the code lenses as a line annotations

Showcase

Screen.Recording.2025-11-21.at.14.16.32.mov

Related/fixes: #3005
Related: #3272

Footnotes

  1. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeLens

  2. https://code.visualstudio.com/blogs/2017/02/12/code-lens-roundup

  3. https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeLens

  4. https://github.com/typescript-language-server/typescript-language-server?tab=readme-ov-file#code-lenses-textdocumentcodelens

  5. https://github.com/golang/go/issues/36787

@pascalkuthe pascalkuthe added the A-language-server Area: Language server client label Dec 8, 2022
@sigmaSd
Copy link
Copy Markdown
Contributor

sigmaSd commented Dec 8, 2022

rust-analyzer doesn't support executing commands (on the server side) so the commands need to be executed client side.

This is not specific to rust-analyzer, I think that other lsps work the same way (especially for test codelens), for example I know its the case with deno lsp.

I think this would require plugins to handle, because executing the commands is lsp dependent

@matoous
Copy link
Copy Markdown
Contributor Author

matoous commented Dec 8, 2022

@sigmaSd yes, indeed, you are right. Rust for example returns rust-analyzer.showReferences which is custom extension command which would indeed require plugins. I will push a few more changes but otherwise I guess this will need to be put on hold until the plugin system is available. Other option would be for the commands to be for now mapped to the shell via languages.toml configuration and the output being display the same way we do for :run-shell-command.

@matoous matoous changed the title feat(lsp): initital implementation of code lens feat(lsp): code lenses Dec 8, 2022
@pascalkuthe
Copy link
Copy Markdown
Member

pascalkuthe commented Dec 8, 2022

:sh currently does not return anything if the command returns an error (which is the interesting case for tests I would assume) aslo any tests that might want input will freeze the editor. I think for running tests to be a good experience we would need an integrated terminal #4649. Also going the sh route requires developing, maintaining and installing a separate CLI tool for each LSP command (these binaries don't exist yet) which I don't see people developing (and would only work for a subset of commands I guess).

How many servers actually support server side execution? If rust-analyzer is the odd-one out with client-only commands then this might be worth merging but if almost all LSPs only allow client-side command execution then I think this should probably wait for plugins.

@pascalkuthe pascalkuthe added the A-plugin Area: Plugin system label Dec 8, 2022
@ayoub-benali
Copy link
Copy Markdown
Contributor

rust-analyzer doesn't support executing commands (on the server side) so the commands need to be executed client side.

This is not specific to rust-analyzer, I think that other lsps work the same way (especially for test codelens), for example I know its the case with deno lsp.

I think this would require plugins to handle, because executing the commands is lsp dependent

I can confirm that at least Metals also expects the client to run some code lens commands and they are implemented in the plugin for sublime for example.

@Esnos33
Copy link
Copy Markdown

Esnos33 commented Apr 26, 2023

Hi, I'm new to git and I have question. I already have helix 23.03 and in release notes there is information about Initial support for snippets, but #3005 bug is still there. This is how it is supposed to be? Automatic type annotation is one of best things in haskell.

@matoous
Copy link
Copy Markdown
Contributor Author

matoous commented May 24, 2023

Closing this until Helix has support for plugins that are prerequisit for this being useful.

@matoous matoous closed this May 24, 2023
@maralorn
Copy link
Copy Markdown

I have a question about this: It is my impression that there are code lenses which don’t need a plugin to work. I think haskell-language-server has some of those. Is that wrong? Or could we maybe implement this, but only show code lenses in the case where they don’t require a plugin?
Actually, haskell-language-server simply offers code actions for when you click on the code lens. And those code actions can already be used right now. I am primarily interested in the code lenses because they display useful information, even if you don’t want to activate them right now.

@alexaandru
Copy link
Copy Markdown

Similar to the above comment, in gopls there are code actions that are available after running code lenses and there already is support for code actions so we wouldn't need any plugins to use them.

For example, when in go.mod file, there is a code lense Check for upgrades which when run it will populate diagnostics with info about which packages can be upgraded. Then for each of those lines, there is a code action available to upgrade it. So code lenses alone would be a very valuable addition.

@matoous
Copy link
Copy Markdown
Contributor Author

matoous commented Nov 23, 2023

👋 let me take a look if I could revive this. Indeed, the support might vary language to language but for some languages this might be worthwhile even in limited capacity.

@matoous matoous force-pushed the md/code-lens branch 5 times, most recently from cc7cdc8 to 4d7203d Compare November 26, 2023 11:31
@maralorn
Copy link
Copy Markdown

Also: Even if Code lenses can’t be triggered it might still be useful to show them, right? And that part should always work without plugin support, right?

@maralorn
Copy link
Copy Markdown

I am test driving this and it works just fine with haskell-language-server. Of course it would be nice if the code lenses would auto-update, but this is good enough for me.

Thank you!

@matoous
Copy link
Copy Markdown
Contributor Author

matoous commented Nov 18, 2025

Heya 👋 I picked this up again after a loooong time, there seems to be way more of the ground work done to get this working properly. Rebase was too hard, so I started from scratch, sorry for the nuked history of this PR.

It should now work properly by auto-requesting code lenses for documents. space + l on a line with code lenses to view them/execute them. Some parts that are missing and I will try to take a look into in the following days:

  • supporting multiple LSPs
  • rendering code lenses using virtual text (see below, keeping this out of scope of this PR)
  • code lenses refresh request (server -> client)

Done parts which weren't part of the outdated PR:

  • auto-request code lenses for documents (re-used the scaffolding build for color swatches)
  • code lenses resolve requests

Feel free to test-drive (I will try to keep the branch up to date) and report any issues (just keep in mind what's unimplemented and expected vs. what should be working and isn't).

@matoous
Copy link
Copy Markdown
Contributor Author

matoous commented Nov 18, 2025

Displaying code lenses as a virtual text would ideally be done by implementing EndOfLineAnnotation (similar to InlineAnnotation but anchored at the end of the graphem after the newline character). So I am leaving this out for now with gutter being the only place where the code lenses are denoted. It would blow up the size of the PR and I think it can be done separately as a follow-up enhancement.

@matoous matoous force-pushed the md/code-lens branch 4 times, most recently from 55acb30 to 314f869 Compare November 21, 2025 13:18
@matoous matoous marked this pull request as ready for review November 21, 2025 13:19
@matoous matoous force-pushed the md/code-lens branch 2 times, most recently from 2d8efc8 to a7e7749 Compare November 24, 2025 14:58
@matoous matoous force-pushed the md/code-lens branch 3 times, most recently from 856e245 to 097f7f5 Compare March 22, 2026 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-language-server Area: Language server client A-plugin Area: Plugin system

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing Code actions and code lenses (Haskell)

8 participants