refactor(language-service): specifically identify empty argument positions (#41581)
This commit changes `getTemplateAtTarget` to be able to identify when a cursor position is specifically within the argument span of a `MethodCall` or `SafeMethodCall` with no arguments. If the call had arguments, one of the argument expressions would be returned instead, but in a call with no arguments the tightest node _is_ the `MethodCall`. Adding the additional argument context will allow for functionality that relies on tracking argument positions, like `getSignatureHelpItems`. PR Close #41581
This commit is contained in:
parent
e1a2930893
commit
d85e74e05c
|
@ -49,8 +49,8 @@ export interface TemplateTarget {
|
|||
export type TargetContext = SingleNodeTarget|MultiNodeTarget;
|
||||
|
||||
/** Contexts which logically target only a single node in the template AST. */
|
||||
export type SingleNodeTarget = RawExpression|RawTemplateNode|ElementInBodyContext|
|
||||
ElementInTagContext|AttributeInKeyContext|AttributeInValueContext;
|
||||
export type SingleNodeTarget = RawExpression|MethodCallExpressionInArgContext|RawTemplateNode|
|
||||
ElementInBodyContext|ElementInTagContext|AttributeInKeyContext|AttributeInValueContext;
|
||||
|
||||
/**
|
||||
* Contexts which logically target multiple nodes in the template AST, which cannot be
|
||||
|
@ -65,6 +65,7 @@ export type MultiNodeTarget = TwoWayBindingContext;
|
|||
*/
|
||||
export enum TargetNodeKind {
|
||||
RawExpression,
|
||||
MethodCallExpressionInArgContext,
|
||||
RawTemplateNode,
|
||||
ElementInTagContext,
|
||||
ElementInBodyContext,
|
||||
|
@ -79,6 +80,21 @@ export enum TargetNodeKind {
|
|||
export interface RawExpression {
|
||||
kind: TargetNodeKind.RawExpression;
|
||||
node: e.AST;
|
||||
parents: e.AST[];
|
||||
}
|
||||
|
||||
/**
|
||||
* An `e.MethodCall` or `e.SafeMethodCall` expression with the cursor in a position where an
|
||||
* argument could appear.
|
||||
*
|
||||
* This is returned when the only matching node is the method call expression, but the cursor is
|
||||
* within the method call parentheses. For example, in the expression `foo(|)` there is no argument
|
||||
* expression that the cursor could be targeting, but the cursor is in a position where one could
|
||||
* appear.
|
||||
*/
|
||||
export interface MethodCallExpressionInArgContext {
|
||||
kind: TargetNodeKind.MethodCallExpressionInArgContext;
|
||||
node: e.MethodCall|e.SafeMethodCall;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,10 +174,21 @@ export function getTargetAtPosition(template: t.Node[], position: number): Templ
|
|||
|
||||
// Given the candidate node, determine the full targeted context.
|
||||
let nodeInContext: TargetContext;
|
||||
if (candidate instanceof e.AST) {
|
||||
if ((candidate instanceof e.MethodCall || candidate instanceof e.SafeMethodCall) &&
|
||||
isWithin(position, candidate.argumentSpan)) {
|
||||
nodeInContext = {
|
||||
kind: TargetNodeKind.MethodCallExpressionInArgContext,
|
||||
node: candidate,
|
||||
};
|
||||
} else if (candidate instanceof e.AST) {
|
||||
const parents = path.filter((value: e.AST|t.Node): value is e.AST => value instanceof e.AST);
|
||||
// Remove the current node from the parents list.
|
||||
parents.pop();
|
||||
|
||||
nodeInContext = {
|
||||
kind: TargetNodeKind.RawExpression,
|
||||
node: candidate,
|
||||
parents,
|
||||
};
|
||||
} else if (candidate instanceof t.Element) {
|
||||
// Elements have two contexts: the tag context (position is within the element tag) or the
|
||||
|
|
|
@ -549,6 +549,15 @@ describe('getTargetAtPosition for expression AST', () => {
|
|||
expect(node).toBeInstanceOf(e.SafeMethodCall);
|
||||
});
|
||||
|
||||
it('should identify when in the argument position in a no-arg method call', () => {
|
||||
const {errors, nodes, position} = parse(`{{ title.toString(¦) }}`);
|
||||
expect(errors).toBe(null);
|
||||
const {context} = getTargetAtPosition(nodes, position)!;
|
||||
expect(context.kind).toEqual(TargetNodeKind.MethodCallExpressionInArgContext);
|
||||
const {node} = context as SingleNodeTarget;
|
||||
expect(node).toBeInstanceOf(e.MethodCall);
|
||||
});
|
||||
|
||||
it('should locate literal primitive in interpolation', () => {
|
||||
const {errors, nodes, position} = parse(`{{ title.indexOf('t¦') }}`);
|
||||
expect(errors).toBe(null);
|
||||
|
|
Loading…
Reference in New Issue