Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement code lens #71

Closed
wants to merge 1 commit into from
Closed

Implement code lens #71

wants to merge 1 commit into from

Conversation

mkcms
Copy link
Collaborator

@mkcms mkcms commented Aug 12, 2018

This is a work in progress code lens implementation.

What's currently missing is executing commands.

@joaotavora
Copy link
Owner

When I read the spec, I don't understand very well what code lens are. Can you explain in your words, maybe with examples? I'd like to find the best available Emacs abstraction to represent them

@mkcms
Copy link
Collaborator Author

mkcms commented Aug 12, 2018

They're clickable overlays that are placed on symbols. The eclipse.jdt.ls language servers puts them on function and class names.

They provide information about a symbol and action to perform on this symbol. E.g. the eclipse server puts on each symbol the number of references to it, and a clickable action "find references".

@mkcms
Copy link
Collaborator Author

mkcms commented Aug 18, 2018

I rewrote everything after I realized that the previous version had some flaws:

  1. Unlike code lens in other editors, the information they provided in the old version was never visible unless you hovered over the overlays.
  2. It assumed that the server will put them in the right place, without having duplicates in the same place.
  3. The code lens looked ugly when they spanned multiple lines.

I tried vscode to see how the code lens are implemented there. The current version is very similar, although unlike in vscode, the code lens are not visible at all times. Currently you have to toggle them with the command eglot-code-lens.

This command will make the buffer read only and enable a new minor mode which provides keybindings p, n to go to previous, next lens, RET to act on lens at point, q to exit the minor mode and clear the lenses.
The lenses are displayed like in vscode, above the line in which they start. If there are multiple lenses on the same line, they are placed one next to another. The title of their command is what's visible in the buffer.

Tell me what you think. If you think that a minor mode is too much, I believe I could also use a transient map, or simply read the events (go to next/previous lens, act on it) from the minibuffer. That will make the command self-contained.

@mkcms
Copy link
Collaborator Author

mkcms commented Aug 18, 2018

If you want to test this, I recommend cquery and eclipse.jdt.ls servers.

@cmccloud
Copy link

@joaotavora, for what it's worth, this vscode blog post on code lenses helped me to get a better understanding of what they are and how they can be used.

@joaotavora
Copy link
Owner

@joaotavora, for what it's worth, this vscode blog post on code lenses helped me to get a better understanding of what they are and how they can be used.

Thanks. From what I understand they're very valuable for editors which don't have the capabilities of Emacs. For example, in emacs you can M-x mark-defun and M-x vc-region-history to elegantly get to the history of a function. And finding references is already supported by xref-find-references.

But that doesn't mean this shouldn't be supported. I'm evaluating @mkcms proposal. I mostly need to sit down and configure Java in a medium or small sized project so I can start testing all these features, which seem to be big in that language.

@cmccloud
Copy link

cmccloud commented Aug 18, 2018

I agree with you. My own take away from the article was the importance of distinguishing between the links (overlays), actions, and the displaying of actions. I see lenses (the overlays) mostly as a discoverability tool, but would almost certainly leave them disabled in my own configuration, provided that I could independently access whatever actions are made available by the code lens. They could still be very useful for being made aware of obscure or less commonly used capabilities.

Similarly, finding a reference via an action might still use xref, but display the result differently, maybe in an undecorated child frame, for example.

@mkcms
Copy link
Collaborator Author

mkcms commented Aug 18, 2018

would almost certainly leave them disabled in my own configuration

Unlike in vscode, this implementation is not automatic. You only see the lenses when you explicitly toggle them with eglot-code-lens command.

@cmccloud
Copy link

Unlike in vscode, this implementation is not automatic. You only see the lenses when you explicitly toggle them with eglot-code-lens command.

I definitely took note of that, and do think it's the right approach.

@david-christiansen
Copy link

david-christiansen commented Aug 24, 2018

For displaying a list of commands based on the text properties and/or overlays at a particular buffer position, it might be worth considering using prop-menu. We use this in idris-mode and a couple of others for displaying commands available at a particular buffer position. It's both keyboard (via completing-read) and mouse (via a menu) accessible.

Something like this could be used to have the commands always be available without putting the buffer in a read-only mode, but without the visual noise of some other approaches.

@david-christiansen
Copy link

One more potential gain from prop-menu is that it would let the "code actions" menu easily coexist with the "code lens" menu.

@mkcms
Copy link
Collaborator Author

mkcms commented Aug 27, 2018

@david-christiansen Nice package. Would it be possible to have support for it and at the same time not actually require it?

@david-christiansen
Copy link

Would something like this be too ugly?

(with-eval-after-load prop-menu
    (setq-local prop-menu-item-functions '(eglot-prop-menu-items-function))
    (define-key eglot-mode-map (kbd "C-c C-SPC") 'prop-menu-by-completing-read)
    (define-key eglot-mode-map (kbd "<mouse-3>") 'prop-menu-show-menu))

If the concern for avoiding a dependency is the eventual desire to put eglot into ELPA or Emacs proper, I'm happy to assign copyright to the FSF for the library.

@mkcms
Copy link
Collaborator Author

mkcms commented Nov 14, 2018

@joaotavora Should I continue this develop this branch, or do you think code lens shouldn't be included in eglot? I personally find them distracting (when they're present all the time), but when browsing code they have some use. I know that ccls, cquery and eclipse.jdt.ls servers all have code lens support.

@joaotavora
Copy link
Owner

I'll be looking at this in the near future. What servers currently support codelens? EclipseJDT and some other one?

@mkcms
Copy link
Collaborator Author

mkcms commented Dec 3, 2018

What servers currently support codelens? EclipseJDT and some other one?

Like I said above, ccls and cquery :-)

@joaotavora
Copy link
Owner

Like I said above, ccls and cquery :-)

sorry 😊

* eglot.el (eglot-code-lens): New face.
(eglot--lens-mode): New minor mode.
(eglot-lens-act, eglot-next-lens, eglot-previous-lens
eglot-code-lens): New commands.
@joaotavora
Copy link
Owner

Hey, I finally tried this with ccls and it's quite interesting. I rebased to current master and pushed to your branch. I'm going to try a flymake-based approach to cut down on the needed code and push that to a separate branch.

@mkcms
Copy link
Collaborator Author

mkcms commented Dec 12, 2018

I'm going to try a flymake-based approach

Ha, that's very interesting. I've never even considered that. Can flymake be modified to display some special diagnostics different than others?

@joaotavora
Copy link
Owner

Ha, that's very interesting. I've never even considered that. Can flymake be modified to display some special diagnostics different than others?

Not yet :-)

joaotavora added a commit that referenced this pull request Dec 13, 2018
* eglot.el (eglot--lsp-interface-alist): New CodeLens interface.
(eglot--current-flymake-report-fn)
(eglot--unreported-diagnostics): Move up here.
(eglot--report-diagnostics): New helper.
(eglot-handle-notification textDocument/publishDiagnostics): Use
it.
(eglot--post-self-insert-hook, eglot--pre-command-hook)
(eglot--before-change): Fix docstring.
(eglot-lens-act, eglot-next-lens, eglot-previous-lens): Unimplement.
(eglot-code-lens): Implement using new Flymake.
(eglot--eclipse-jdt-contact): Fix docstring.
@joaotavora
Copy link
Owner

Just pushed a new Flymake to Emacs master, should be on GNU ELPA soon. Use it to test the new approach (which is very broken still).

@mkcms
Copy link
Collaborator Author

mkcms commented Dec 13, 2018

Use it to test the new approach (which is very broken still).

It's pretty cool. Although it messes up my display, because it "splits" the line in two to show the lens there. This happens when the end of a lens region is not the end of some line.

@Sarcasm
Copy link

Sarcasm commented Dec 13, 2018

Out of curiosity, would you mind sharing a screenshot of what it looks like?

@mkcms
Copy link
Collaborator Author

mkcms commented Dec 13, 2018

@Sarcasm

Out of curiosity, would you mind sharing a screenshot of what it looks like?

Here's screenshot of my version. @joaotavora's looks a bit different because of the display issue I described above.

screenshot from 2018-12-13 21-37-34

@MaskRay
Copy link
Contributor

MaskRay commented Dec 14, 2018

The screenshot looks great, however, the code lenses occupy some vertical space. Putting code lenses at line ends is better IMHO. I've experimented with the idea before but my elisp-fu isn't strong enough to tame display properties https://www.reddit.com/r/emacs/comments/9k3rf6/put_a_codelens_overlay_sticking_in_the_end_of_the/

@slotThe
Copy link

slotThe commented Oct 22, 2021

@joaotavora What's the status of this? I'd love to have proper code lens support in eglot (haskell-language-server and rust-analyzer both use them to great effect in displaying type signatures)

@skangas skangas added the WIP Work in progress label Jan 8, 2022
@skangas
Copy link
Collaborator

skangas commented Jan 15, 2022

@mkcms Did you get any further with this? Thanks!

@mkcms
Copy link
Collaborator Author

mkcms commented Jan 15, 2022

@skangas

@mkcms Did you get any further with this? Thanks!

No, I didn't. This can probably be closed.

@adonovan
Copy link

adonovan commented Oct 23, 2023

I would love to see this feature implemented. My immediate need is that it would be nice when editing a test--which in Go is a func TestFoo(t *testing.T), but most languages have something equivalent--to be able to run the test directly. The LSP server for Go already exposes this operation as a code lens, but eglot doesn't display it, so I have to write extra client-side code to expose it as an Emacs command.

(Aside: this particular feature seems like it belongs in the core LSP protocol since the concept is entirely language-agnostic.)

Ideally, the overlay offering code lenses would be queried automatically after each edit to the file, avoiding the clumsiness of an extra step to request the overlay.

@unhammer
Copy link

Did the flymake version get merged or is it testable somehow? (Should code lenses show with flymake-show-buffer-diagnostics or is there a different function?)

@joaotavora
Copy link
Owner

AFAIK every code developed for Flymake has been merged. But Eglot code lens support has not. I didn't even remember authoring the commit above (and I forgot what codelenses are, to be honest).

@unhammer
Copy link

I think they're basically code actions that come with a button label, e.g. in

bilde

the "Refresh …" text is not part of the source, but provided by the code lens, and you can click it to run the action (which in this case does the haskell equivalent of eval-print-last-sexp with overwrite mode on).

As others have mentioned, the useful part isn't really the button (I would prefer not to see it), but getting access to these code actions which for many language servers are often just available as code lenses.

@nemethf
Copy link
Collaborator

nemethf commented Jul 30, 2024

the useful part isn't really the button (I would prefer not to see it), but getting access to these code actions which for many language servers are often just available as code lenses.

Can you, please, list a couple of those language servers? Thanks.

@noinia
Copy link

noinia commented Jul 30, 2024

The haskell-language-server uses them. For example for evaluating example code snippets in the documentation (as shown in the screenshot above).

So it would be awesome if eglot would support these code lenses as well.

@nemethf
Copy link
Collaborator

nemethf commented Jul 30, 2024

The haskell-language-server uses them. For example for evaluating example code snippets in the documentation (as shown in the screenshot above).

So it would be awesome if eglot would support these code lenses as well.

Sure. But that is just one, not "many". Can you name additional language servers that only provide some code actions in code lenses and not in any other way? Thanks.

@slotThe
Copy link

slotThe commented Jul 30, 2024

The haskell-language-server uses them. For example for evaluating example code snippets in the documentation (as shown in the screenshot above).
So it would be awesome if eglot would support these code lenses as well.

Can you name additional language servers that only provide some code actions in code lenses and not in any other way? Thanks.

From the top of my head:

  • rust-analyzer uses code lenses for various things like trait implementations, references, tests, comment evaluation, etc.
  • clojure-lsp uses it for symbol references and tests (from what I've seen)
  • ocaml-lsp uses it for type signatures

As far as I know, none of these features are available as code actions, only as lenses (though type signatures may be available as local hover actions).

@nemethf
Copy link
Collaborator

nemethf commented Jul 30, 2024

Thank you for the list.

* `rust-analyzer` uses code lenses for various things like trait implementations, references, tests, comment evaluation, etc.

At least references can be accessed by other means (xref-find-references). (Tests are also available using an lsp-extension.) And if these are somewhat similar to hover-actions, then code-lenses support in Eglot won't be enough to access them, because they would require additional non-standard client commands.

I agree the code-lenses support would be a useful addition, but I do not think it is a show-stopper in general.

@joaotavora
Copy link
Owner

At least references can be accessed by other means (xref-find-references).

Yes, it seems code lenses are a UI-opinionated generic way to access LSP functionality that could be accessible via other commands (finding definition/references to go to places, code actions to transform)

I agree the code-lenses support would be a useful addition, but I do not think it is a show-stopper in general.

I agree. Fairly useful. And probably implementing them with Flymake (as was my initial idea) isn't the best. A hand-rolled implementation like for inlay hints (based on jit-lock-register) -- or a generic code-lens library (but for what clients other than LSP?) is probably a better fit.

@unhammer
Copy link

rust-analyzer also uses it for Run|Debug and Run Doctest, e.g. with lsp-mode in Emacs I see

bilde

but these are not available through eglot.

(I believe metals for scala does this kind of thing too; in general it seems common to put "run/test/eval" type things in code lenses.)

@adonovan
Copy link

Can you name additional language servers that only provide some code actions in code lenses and not in any other way?

The LSP server for Go (gopls) exposes a handful of features via code lenses, but none of them are enabled by default because the UI elements tend to be distracting. We have found that code actions tend to be a less intrusive (if less discoverable) way to expose the same features, and we plan to migrate these features towards code actions.

So, contra my earlier comment, I don't have any pressing desire for eglot code lens support.

@nemethf
Copy link
Collaborator

nemethf commented Aug 2, 2024

rust-analyzer also uses it for Run|Debug and Run Doctest, [...]
but these are not available through eglot.

Run and Debug relies on the Runnables lsp-extension. Extensions are usually out of scope for Eglot. (Eglot-x does support Runnables and the rust-analyzer.runSingle client-side command, so "Run" shows up in hover-actions.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
WIP Work in progress
Projects
None yet
Development

Successfully merging this pull request may close these issues.