-
Notifications
You must be signed in to change notification settings - Fork 126
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
Preliminary inlay hint support #1159
Conversation
That makes a lot of sense.
That would be a big improvement IMO.
With the improvements above, they would be very useful.
Something that I've seen are annotations on |
This looks nice, thanks for your work on this feature. Is there some plan to also add the return type on functions? |
I'm not sure where to attach function return types. Putting them at the end of the parameter list could be confusing with the parameter hints. |
Thank you for this PR ! This works well for me in my Given that OCaml code has relatively fewer explicit type annotations, this feature will be very useful and I hope that this feature is merged to master soon :-) ! |
This is handy but I think many users may find it somewhat annoying. I believe it's worth considering implementing f# style "line lens" hints as a less intrusive but still very informative option.
I think that second point is very significant, type hints cause horrible shifting of the text while editing it as the hints come in and out of existence and types change. Great PR though, it's great to see ocaml's tooling improve so much :) |
Are "line lens" something specific to vscode ? inlay hints will work well across many LSP supporting editors. Rust analyzer uses inlay hints a lot also BTW and while there are some annoyances it doesn't seem to bother me too much. |
@sidkshatriya They are possible in any editor that supports virtual text, I believe that includes vscode neovim and emacs. https://github.com/fsharp/FsAutoComplete just implements a custom message that returns the location the LineLens should be and its contents. |
@faldor20 Given that these are type annotations, I personally prefer them to appear right after the variable name rather than at the end of the line. So let test : int list = List.map f lst (Here Will be better (in my opinion) than probably something like let test = List.map f lst (* test : int list *) (Here The issue with this is that for long lines the type information will be far away from the point that you wish to see it. The context will be lost. It will also be more verbose because you need to repeat the variable name again e.g. Alternatively if you / @jfeser wish to work on a proof of concept that is more in line with your suggestion I would love to test it out ! I personally am happy with the feature as-is, to be honest. |
Vscode uses a toggle for inlay hints, which addresses some of the concerns around verbosity. I use a similar toggle in emacs. I don't expect that users will have these hints enabled all the time. The F# approach is interesting, particularly because it hints more than just identifiers. I do think the F# style makes it less clear which expression the hint refers to. |
FWIW |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the nice feature @jfeser !
I made some comments on the iterator which I think could be simplified.
I am also wondering if we should restrict hints to let-bindings and function parameters (right now every pattern variable gets annotated and that adds a lot of noise).
More importantly, since the switch to merlin-lib
and the recent release supporting multiple versions of OCaml, we are trying to reduce direct Typedtree
usage in the codebase because it introduces tight coupling with a very unstable API of the compiler.
Do you think you could move it upstream to Merlin instead ? There is a bit more boilerplate but I can guide you through it if you want. The main additional task is to create a new command with a serializer that will allow testing the PR in Merlin's testsuite. It's totally understandable if you don't have the time to give it a try, and we can think of solutions with @rgrinberg to give early access to that feature until one of us do the upstreaming.
Like others I am also concerned with the verbosity and would prefer the hints to be disabled by default. A toggle to show the hints only when pressed would be nice. My personal opinion is that they are often redundant, break code formatting (especially if you have fixed column numbers) and generally make the code less readable. I think the OCaml pattern of "relying on type-inference and asking users to carefully name values and annotate the types when it makes sense" is very valuable for readability. Here are some examples in ocaml-lsp
's codebase:


Maybe we could introduce ellipsis ...
when types are too long ? (But this is probably the responsibility of the plugin, not the server.)
Thanks for the review!
I had configuration for this at one point. I suspect there isn't a one-size-fits-all choice here. We should look at what other servers do.
I think vscode does exactly this. Maybe someone who uses vscode can confirm.
Maybe there's a way to have the hints expand when hovered over? I'll have to check the spec.
I'm not sure I see the value here unless there is a desire to use this feature from non-lsp merlin clients (and in that case I think they should just become lsp clients). I think the testing style we use in e.g. https://github.com/ocaml/ocaml-lsp/blob/019f835a2334a0cd00bd005e3161cf8d85537092/ocaml-lsp-server/test/e2e-new/action_inline.ml is a better fit for this kind of ui-heavy code than merlin's cram testing.
That's fair. Is |
Using
As far as I understand This is the first PR since In the end we should aim at improving |
@voodoos wrote:
Inlay hints were introduced in the LSP specification 3.17 ( See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_inlayHint ). Strictly speaking, inlay hints are a part of LSP and OCaml's implementation of LSP lives in (Of course, this could be implemented in merlin -- but we have this feature built here and now and by the time it gets into merlin a lot more time may pass) |
P.S. As far as enabling or disabling inlay hints, your LSP client should provide a way to disable/or enable them. My LSP client (kakoune+kak-lsp) allows me to disable/enable inlay hints at any time I want while using the editor, start the editor with them disabled or start the editor with them enabled. In other words, this particular PR should not need to worry about this aspect, I think. (I enjoy inlay hints a lot with rust-analyzer) |
Right now, a majority of the features that are "part of LSP" are actually implemented in
That doesn't have to be the case if we work together 🙂. Especially for future PRs if they target directly |
Regarding the default behavior: How about we only annotate the structure items by default so that we match the behavior of the old code lens. That one was just as intrusive and people liked it. For anything more, we can allow it through configuration. Regarding the LSP/Merlin split: It appears that we have two separate concerns and we need to be careful to manage them without conflict. On one hand, we want to maintain some control to iterate on this feature - especially since it's not really clear in the short term what users are going to like. On the other hand, we don't want the typed tree to leak into our implementation. So I propose the following:
Regarding tests: I see two issues with writing most of the tests on the merlin side. The first one is that we'll still need the test on the lsp side to make sure we're speaking the protocol correctly, so if we're not careful, we'll just double the work without really testing anything in addition. The second issue is that testing this feature by printing json payloads isn't going to cut it - whether it's done through ppx_expect or cram. This feature is complex enough that I would expect the tests to display the document with the inline hints rendered in some way inside the tests. For example, output such as:
Where the If merlin provides us with the typed tree iterator that we need, I imagine that the only tests we'll really need on the merlin side are going to be unit tests of this iterator. I don't know how hard it is to write those, but I imagine it should be less work than full blown cram tests. In addition, I expect less redundancy in the tests this way. |
I think we should provide the option to additionally annotate locals and pattern variables. It might also be worth adding some documentation on server configuration. Many users weren't able to figure out how to reenable the code lenses.
There's a few other places where we currently use the typedtree. It would be worth looking at them to see if there are some common patterns that we can extract.
+1 to both. Sure. |
Just chiming in from here: https://discuss.ocaml.org/t/ocaml-platform-newsletter-june-2023/12640/2 I agree 100% with @faldor20 here. I remember turning this feature right off when I was learning a bit of rust a few months ago (very annoying/distracting). But the F# guys got it (the info is still very useful to have) Here's a couple of screenshots to illustrate: VScode + Ionide extension: Rider: I'm not sure if that's an lsp concern, but please consider pushing that info on the right if you're working on this (or make the position configurable if possible) |
I did some research into how ionide handles these linelens annotations and ended up doing a bit of refactoring on the codebase. We could likely just copy what ionide is doing directly: |
|
||
let%expect_test "labeled argument" = | ||
apply_inlay_hints ~source:"let f ~x = x + 1" (); | ||
[%expect {| let f ~x$: int$ = x + 1 |}] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also add cases for pattern matching and inline function definition, with the on/off options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, planning to.
@jfeser do you think this will be ready soon? I'd like release 1.17.0. If it's not ready, we can just push it back until 1.18.0 |
Let me see if I can finish it up by this weekend. If it doesn't happen by then, we can just push it back. Does that work for you? |
Can the inlay hint be placed above bindings instead of the side? I personally disable inlay hints in Rust exactly because they're in the horizontal. |
@EduardoRFS rendering of inlay hints is up to the editor really. However there is codelens which allows some info such as function signatures to be displayed above a line. If someone was smart they could probably use whitespace inside codelens to make type signatures appear over their definitions, but It'd probably be kind of a mess when types are longer than names |
@jfeser do you still plan to finish this up? We could just merge it as is and disable it until it's ready. At least it will save you some rebasing when you do decide to polish it up. |
@rgrinberg I think this is ready to go. |
Pull Request Test Coverage Report for Build 4103Details
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
linting helper function for positions Update README.md Co-authored-by: Ulysse <[email protected]> Update README.md Co-authored-by: Ulysse <[email protected]> Update ocaml-lsp-server/src/document.ml Co-authored-by: Ulysse <[email protected]> Update ocaml-lsp-server/src/document.ml Co-authored-by: Ulysse <[email protected]> 80 char per line limit use better descriptive function name adjust formatting nix: export an overlay that adds ocaml-lsp itself to pkgs.ocamlPackages (ocaml#1231) Preliminary inlay hint support (ocaml#1159) * start inlay hint support * update changes * promote tests * address issues with function parameters * fix optional arguments with defaults * remove config options until we have something to configure * simplify value_binding annotations * extract Range.overlaps * eliminate newlines in types * add config options for pattern variables and let bindings * add tests * formatting * allow passing settings to run_request * fix text edit application in tests * add config option for lambdas (doesn't work yet) * fix up hint_pattern_variables and hint_let_bindings
Here's what they look in Emacs:

Outstanding issues:
Do hints like this look useful? Where else might we want to add them?
Closes #685