-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[ty] IDE: add support for object.<CURSOR> completions
#18468
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
Conversation
|
you mean astral-sh/ty#86 ;) |
|
Yup. That's a hard one to get right for me haha. |
AlexWaygood
left a comment
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.
Nice! I haven't reviewed the AST/tokenizing stuff in depth, but this looks reasonable to me!
|
I am planning to review this tomorrow. One thing that surprised me when testing this locally in the playground: in a scenario like the following, I get no completions at all. I need to type one additional letter before I start to see the completions on class C:
foo: int
bar: str
C.<CURSOR> |
This is because the server capabilities needs to be updated to include the diff --git a/crates/ty_server/src/server.rs b/crates/ty_server/src/server.rs
index 84093d6ffa..4846e0219c 100644
--- a/crates/ty_server/src/server.rs
+++ b/crates/ty_server/src/server.rs
@@ -180,6 +180,7 @@ impl Server {
completion_provider: experimental
.is_some_and(Experimental::is_completions_enabled)
.then_some(lsp_types::CompletionOptions {
+ trigger_characters: Some(vec!['.'.to_string()]),
..Default::default()
}),
..Default::default()
diff --git a/playground/ty/src/Editor/Editor.tsx b/playground/ty/src/Editor/Editor.tsx
index fca03cd08b..a25e3b4ddb 100644
--- a/playground/ty/src/Editor/Editor.tsx
+++ b/playground/ty/src/Editor/Editor.tsx
@@ -179,7 +179,7 @@ class PlaygroundServer
monaco.languages.registerDocumentFormattingEditProvider("python", this);
}
- triggerCharacters: undefined;
+ triggerCharacters: string[] = ["."];
provideCompletionItems(
model: editor.ITextModel,This will then inform the client / playground to request completions on For reference, this is Pyright's trigger characters: For context, the client needs to know when it should request completions which by default is usually |
dhruvmanila
left a comment
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.
Thank you! I think this is a good start, I've a few suggestions on what could be done next.
It's a bit unfortunate that we don't yet have E2E test infrastructure for the server, so when testing any server related features that require editor interaction, we usually add a short video demonstrating the functionality in the PR description. I'd recommend to include that whenever you think it'd be useful which I think would be useful in this PR.
| static OBJECT_DOT_EMPTY: [TokenKind; 2] = [TokenKind::Name, TokenKind::Dot]; | ||
| static OBJECT_DOT_NON_EMPTY: [TokenKind; 3] = | ||
| [TokenKind::Name, TokenKind::Dot, TokenKind::Name]; |
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.
I think this is a great start but it seems to have a few limitations. I don't think these needs to be addressed here but I thought to write it down as a note. For example, doing [].<CURSOR> falls back to providing completions from the scope. Maybe we shouldn't use the fallback behavior when we know that the completion was triggered from a . character? I'm not sure, you might want to play around with this in an editor context.
A follow-up to this would be to expand the token kind that can occur before the . character so that completions on objects other than a variable work like list, dictionary, strings, etc. For example, in [].<CURSOR> we would provide completions for list attributes. We might have to be careful here because we can't just look at the previous token as it's only [].<CURSOR> (a syntactically valid list) should provide completion and not ].<CURSOR>. Also, the list could be arbitrary long which means that there would could be n number of previous tokens to look before we see the [ corresponding to the ] in which case maybe we should rely on the AST instead.
Another example is when an attribute access happens directly on an instantiated class like A().<CURSOR>.
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.
Another one: Anything parenthesized: (a). or { a: True }....
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.
I had a similar thought while playing with this and reviewing the code. There are not that many places in Python's syntax where a . can appear(?). The ellipsis ..., float literals 3.14, and relative imports such as from .mod import x come to mind. This is very hand-wavy, but maybe this suggests that an "opt out" approach could work? That is, instead of explicitly matching on a set of patterns, maybe we should always try to find an enclosing attribute-expression if we see DOT or DOT NAME? This would mean we would also start the enclosing-node search for things like a float literal (3.<CURSOR>), but maybe that's not a problem?
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.
Yeah I like that idea @sharkdp!
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.
(I'll work on this in another PR.)
MichaReiser
left a comment
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.
Nice!
I think we need to set the completion options in the playground and the LSP setup because . isn't a trigger character by default. From the LSP specification
/**
* The additional characters, beyond the defaults provided by the client (typically
* [a-zA-Z]), that should automatically trigger a completion request. For example
* `.` in JavaScript represents the beginning of an object property or method and is
* thus a good candidate for triggering a completion request.
*
* Most tools trigger a completion request automatically without explicitly
* requesting it using a keyboard shortcut (e.g. Ctrl+Space). Typically they
* do so when the user starts to type an identifier. For example if the user
* types `c` in a JavaScript file code complete will automatically pop up
* present `console` besides others as a completion item. Characters that
* make up identifiers don't need to be listed here.
*/
triggerCharacters?: string[];
| use crate::find_node::covering_node; | ||
|
|
||
| #[derive(Debug, Clone)] | ||
| pub struct Completion { |
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.
Maybe something for another PR but It would be great if we could extend Completion here with a kind that tells VS code what the completion is (is it a variable, a field, etc...). This is currently hard coded in the playground to always be Variable but that will be incorrect with this PR.
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.
Yeah @dhruvmanila has mentioned this a few times, but I've been focusing on breadth (making completions work in more places) over depth (improving completion quality). Maybe I should pause on breadth for the moment and look at depth.
| static OBJECT_DOT_EMPTY: [TokenKind; 2] = [TokenKind::Name, TokenKind::Dot]; | ||
| static OBJECT_DOT_NON_EMPTY: [TokenKind; 3] = | ||
| [TokenKind::Name, TokenKind::Dot, TokenKind::Name]; |
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.
Another one: Anything parenthesized: (a). or { a: True }....
|
For the trigger characters, nice catch @sharkdp and @dhruvmanila. I didn't notice that because I've been testing this by specifically opting into auto-completions. My editor doesn't show them automatically. I'll look into changing that. |
c5fd576 to
5addef3
Compare
|
Thanks all for the feedback! I've addressed what I think is easy to address in this PR. Otherwise, in follow-up work, I would like to focus on improving the somewhat naive token-based detection here with something more flexible. i.e., To make things like |
5addef3 to
51608ce
Compare
This PR adds logic for detecting `Name Dot [Name]` token patterns, finding the corresponding `ExprAttribute`, getting the type of the object and returning the members available on that object. Ref #86
51608ce to
7ba780f
Compare
Yes, thank you! That's a good idea. I've added a video to the top-level comment here. In speaking with @MichaReiser, it seems like it would be a good idea for me to be testing this in VS Code as well. So I'll look into setting that up. |
Thank you!
Sounds good! The README over at https://github.com/astral-sh/ty-vscode should be good on how to do it. Feel free to provide any feedback if anything was confusing :) |
This PR adds logic for detecting
Name Dot [Name]token patterns,finding the corresponding
ExprAttribute, getting the type of theobject and returning the members available on that object.
Here's a video demonstrating this working:
class-attr-completions-work.mp4
Ref astral-sh/ty#86