-
-
Notifications
You must be signed in to change notification settings - Fork 82
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
Improved hover support: Structs, qualified calls and types, more info for modules #356
Conversation
@scohen would appreciate your input on this: In d1edb31, I added some additional utilities to Unfortunately, this introduced a Dialyzer error -- the Options I can think of:
|
@zachallaun this looks like a typespec bug in elixir, we should fix that too, then pull the fix into |
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.
For me, it looks good overall. However, I'm not sure if we need the assert_hover
abstraction. Can you explain the reason behind it?
I think you're right. It was my attempt to clean up the tests a bit, but I think they'll be just as clear and more explicit just using variables (if a little bit longer). |
textDocument/hover
support
@scohen @scottming This PR is ready for review. Please see the updated OP for additional context/notes. While I was originally planning for this PR to also include hover support for unqualified calls, I chose to punt on that until the next one. Resolving unqualified calls is a lot more complex, since it requires import tracking, so I plan to address it separately. |
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.
Only a few minor comments were left. Overall, I feel that this improvement is excellent.
apps/remote_control/lib/lexical/remote_control/code_intelligence/docs.ex
Outdated
Show resolved
Hide resolved
@scottming I think that's fair; perhaps the This is more of a meta-thought, but I wish there were a way for all of us to easily test these kinds of features in all major editors. Maybe it's worth setting up an "editor test configuration" repo that contains config for all the major editors and scripts to launch the editor using that config. Needs more thought, but could be useful in the long term. |
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.
Very good, you're awesome, @zachallaun . Thank you.
Another improvement may be to convert the Examples
section to an Elixir code block, but feel free to do that in another PR.
@scottming This is a great idea! I wanted to see if I could sneak it in, but it's unfortunately a bit more complicated than it first seems and, I think, should be in its own PR. Basically, we need to handle all of these cases: Indented code in an explicit code block shouldn't be re-wrapped:
Multiple indented blocks separated by empty lines should be joined together into a single block:
Code indented relative to a parent element (like a list item) should be wrapped, but retain the parent's indentation:
I think there's a somewhat simple way to do this that doesn't require a full Markdown parser by basically map-reducing over the lines, looking for lines that start with But it's enough work that I don't want to include it here :) |
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.
This looks very good. I like the abstractions and what you've done with cursor support.
I have some comments along the lines of how positions are represented and if we can do better in tests with defining ranges and positions, but this is nearly ready.
This moves all of the fetching of docs, specs, types, and callbacks into `Lexical.RemoteControl.Modules`. Those functions now also operate on BEAM object code, which means the object code doesn't need to be repeatedly read from disk. The object code for a module also appears to be available immediately after calling `Code.ensure_compiled(module)`, which allows us to get rid of a timeout/loop that was waiting for it to be written to disk.
The correct guard logic for a call is quite verbose, as you must ensure that the call in a `{call, meta, args}` node isn't a special form, like `:__aliases__`.
This commit currently pulls in a to-be-merged branch of `Sourceror` which contains fixes and changes around `Sourceror.get_range/1`. These are required in order to accurately determine the cursor position in an AST. An additional important change that this commit makes is to add the `unescape: false` option when converting strings to quoted forms. This is needed to determine whether newlines in strings were escaped literals (`\n`) or multi-line. Take this example: iex> Code.string_to_quoted!(~S|"foo\nbar"|) "foo\nbar" iex> Code.string_to_quoted!(~S|"foo ...> bar"|) "foo\nbar" iex> Code.string_to_quoted!(~S|"foo\nbar"|, unescape: false) "foo\\nbar" iex> Code.string_to_quoted!(~S|"foo ...> bar"|, unescape: false) "foo\nbar" Note that, with `unescape: false`, there is a difference between the strings.
There were a few commonalities in all existing usage of `CursorSupport`: 1. In almost all usage, `cursor_position` and `strip_cursor` are used together. 2. In all usage, the returned `{line, column}` tuple could/should be a `Lexical.Document.Position`. 3. In many cases, the stripped text is then used to create a `Lexical.Document`. To address these, a higher level `pop_cursor/2` has been added, returning `{%Position{}, stripped_text}` by default. However, by using the `:as` or `:document` options, `pop_cursor/2` can return `{%Position{}, %Document{}}`, reducing a significant amount of boilerplate. This change also has the effect of reducing the number of tests requiring `PositionSupport`.
When resolving modules, we'll exclude segments trailing the cursor so that you can walk up a module to better understand its context. For instance, for `My.|Nested.Module`, we resolve to `My.Nested`. Using the same behavior for structs makes less sense. If you're hovering a module being used as a struct, often only the full alias is actually a struct; the parents might not be. For instance, with `%My.|Nested.Struct{}`, resolving to `My.Nested` will not yield useful results. Because of this, the full alias is now always resolved in a struct context, so the above would resolve to the struct `My.Nested.Struct`.
Draft squash commit:
This allows editors to highlight the token that the hover content applies to. For instance, if you hovered
Behaviours (modules that have
This applies to modules that use
When hovering a module being used as a struct, e.g.
Qualified calls are those that begin with a module, e.g. For functions with multiple arities, the arity currently being used will try to be inferred and those definitions/docs shown first, followed by those for any higher arities.
This version bump includes more complete and resilient APIs for getting ranges, as well as a new |
This PR builds on #331, adding hover support for additional entities:
Improvements:
Notes:
Future.Code.Typespec
, which includes required fixes in@spec
to make Dialyzer happy.:sourceror
dep to the latest commit until a new release is cut. This latest version updates Sourceror's zippers to be a%Zipper{}
struct and includes fixes/improvements toSourceror.get_range/1
.Lexical.Ast
now uses theunescape: false
option when parsing code. See ba4223d for more context.