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
Autocompletion in expression contexts #39727
Conversation
04691c8
to
199194e
Compare
fc65434
to
62f9518
Compare
62f9518
to
ea24631
Compare
ea24631
to
a8c7c87
Compare
* autocompletion at that point in the expression, if such a location exists. | ||
*/ | ||
getExpressionCompletionLocation( | ||
expr: PropertyRead|SafePropertyRead|MethodCall|SafeMethodCall, |
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.
What about KeyedRead
/KeyedWrite
/PropertyWrite
?
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.
KeyedRead
and KeyedWrite
are non-trivial - the key is itself an expression, meaning we won't target the KeyedRead
but rather a LiteralPrimitive
(if a string literal key is used) within the KeyedRead
. It's possible to support these forms, but I want to get the 99% case working first.
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 added support / a test for PropertyWrite
, since it's nearly identical to PropertyRead
.
@@ -79,4 +83,52 @@ export class CompletionEngine { | |||
this.globalCompletionCache.set(context, completion); | |||
return completion; | |||
} | |||
|
|||
getExpressionCompletionLocation(expr: PropertyRead|PropertyWrite|MethodCall| |
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'm actually a bit curious about this method in general. Shouldn't TTC#getSymbolAtLocation
return this same information?
Edit: Ah, I see that you use getEnd
whereas the getSymbolAtLocation
uses getStart
. Maybe there's some more nuance to this as well that I don't see immediately.
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 don't think there's any nuance with getStart()
vs getEnd()
. TTC#getSymbolOfNode()
would almost certainly work in the cases supported by this PR. But something more complex liked KeyedRead
may require a completion location that's specifically inside of the quoted key string, for example, which getSymbolOfNode()
isn't obligated to provide.
We could add to getSymbolOfNode()
's contract that it provides autocompletion-compatible locations where necessary, but I think a separate method is probably preferable.
// Safe navigation operations are a little more complex, and involve a ternary. Completion | ||
// happens in the "true" case of the ternary. | ||
const ternaryExpr = findFirstMatchingNode(this.tcb, { | ||
filter: ts.isParenthesizedExpression, |
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.
It's a little weird that you're using parenthesized expression as the filter here. I think this should actually be ts.isConditionalExpression
based on the check below.
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.
getSymbolOfTemplateExpression
does something similar - it looks for any node with the given span, then unwraps any parenthesized expression. I'm relying on the fact that all ternaries are parenthesized.
@@ -28,6 +29,9 @@ export class CompletionEngine { | |||
*/ | |||
private globalCompletionCache = new Map<TmplAstTemplate|null, GlobalCompletion>(); | |||
|
|||
private expressionCompletionCache = |
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.
Do you have any concerns about memory "leak" since we are not invalidating cache entries?
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.
No, because the entire CompletionEngine
is thrown away on a template change.
/** | ||
* Analogue for `ts.LanguageService.getCompletionEntrySymbol`. | ||
*/ | ||
getCompletionEntrySymbol(name: string): ts.Symbol|undefined { |
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 we can omit this method. I looked at the TypeScript language service,getCompletionEntryDetails
is the main API for completions. Besides, I don't think we have a use case for the ts.Symbol
right now. The symbol will be discarded at the LSP boundary.
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'm going to leave it in here, but can do a separate PR to remove all the getCompletionSymbol
operations.
const {ngLS, fileName, cursor} = | ||
setup(`{{name.f¦}}`, `name!: {first: string; last: string;};`); | ||
const completions = ngLS.getCompletionsAtPosition(fileName, cursor, /* options */ undefined); | ||
expectContain(completions, ts.ScriptElementKind.memberVariableElement, ['first']); |
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 have a test that lists out all the results.
In the past, we've had unexpected completion results because we only check what's expected to be there, but not results that are not supposed to be there.
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.
Done - switched to expectAll
.
52eb6a2
to
defa59b
Compare
This commit adds support to the Language Service for autocompletion within expression contexts. Specifically, this is auto completion of property reads and method calls, both in normal and safe-navigational forms.
defa59b
to
7fc2616
Compare
…gular#39727) This commit adds support to the Language Service for autocompletion within expression contexts. Specifically, this is auto completion of property reads and method calls, both in normal and safe-navigational forms. PR Close angular#39727
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
No description provided.