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 |
scottming
left a comment
There was a problem hiding this comment.
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. |
scottming
left a comment
There was a problem hiding this comment.
Only a few minor comments were left. Overall, I feel that this improvement is excellent.
|
@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. |
scottming
left a comment
There was a problem hiding this comment.
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 :) |
scohen
left a comment
There was a problem hiding this comment.
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@specto make Dialyzer happy.:sourcerordep 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.Astnow uses theunescape: falseoption when parsing code. See ba4223d for more context.