From a893187d51494ac8ec41b700359e372f11c2092a Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 16 Dec 2020 14:39:49 -0800 Subject: [PATCH] refactor(language-service): Add type to `TemplateTarget` that can indicate multi-node targets (#40185) The current template target implementation only allows a way to represent the template position as targeting a single node in the template AST. However, there is at least one case (banana-in-a-box) where a given template position refers to two template targets. This commit expands the contexts that the `TemplateTarget` can return to include support for the banana-in-a-box syntax, which has two logically targetted AST nodes given a position within the `keySpan` of the binding. PR Close #40185 --- packages/language-service/ivy/definitions.ts | 12 +- .../language-service/ivy/language_service.ts | 21 +- packages/language-service/ivy/references.ts | 13 +- .../language-service/ivy/template_target.ts | 39 ++- .../ivy/test/legacy/template_target_spec.ts | 294 +++++++++--------- 5 files changed, 204 insertions(+), 175 deletions(-) diff --git a/packages/language-service/ivy/definitions.ts b/packages/language-service/ivy/definitions.ts index e6f6667386..c1b61d7da4 100644 --- a/packages/language-service/ivy/definitions.ts +++ b/packages/language-service/ivy/definitions.ts @@ -12,7 +12,7 @@ import {isExternalResource} from '@angular/compiler-cli/src/ngtsc/metadata'; import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ShimLocation, Symbol, SymbolKind, TemplateSymbol} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; import * as ts from 'typescript'; -import {getTargetAtPosition} from './template_target'; +import {getTargetAtPosition, TargetNodeKind} from './template_target'; import {findTightestNode, getParentClassDeclaration} from './ts_utils'; import {flatMap, getDirectiveMatchesForAttribute, getDirectiveMatchesForElementTag, getTemplateInfoAtPosition, getTextSpanOfNode, isDollarEvent, isTypeScriptFile, TemplateInfo, toTextSpan} from './utils'; @@ -226,14 +226,16 @@ export class DefinitionBuilder { if (target === null) { return undefined; } - const {nodeInContext, parent} = target; + const {context, parent} = target; - const symbol = - this.compiler.getTemplateTypeChecker().getSymbolOfNode(nodeInContext.node, component); + const node = + context.kind === TargetNodeKind.TwoWayBindingContext ? context.nodes[0] : context.node; + + const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(node, component); if (symbol === null) { return undefined; } - return {node: nodeInContext.node, parent, symbol}; + return {node, parent, symbol}; } } diff --git a/packages/language-service/ivy/language_service.ts b/packages/language-service/ivy/language_service.ts index 8d8989f958..3771192c4f 100644 --- a/packages/language-service/ivy/language_service.ts +++ b/packages/language-service/ivy/language_service.ts @@ -19,7 +19,7 @@ import {CompletionBuilder, CompletionNodeContext} from './completions'; import {DefinitionBuilder} from './definitions'; import {QuickInfoBuilder} from './quick_info'; import {ReferenceBuilder} from './references'; -import {getTargetAtPosition, TargetNode, TargetNodeKind} from './template_target'; +import {getTargetAtPosition, TargetContext, TargetNodeKind} from './template_target'; import {getTemplateInfoAtPosition, isTypeScriptFile} from './utils'; export class LanguageService { @@ -104,10 +104,11 @@ export class LanguageService { if (positionDetails === null) { return undefined; } - const results = - new QuickInfoBuilder( - this.tsLS, compiler, templateInfo.component, positionDetails.nodeInContext.node) - .get(); + + const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ? + positionDetails.context.nodes[0] : + positionDetails.context.node; + const results = new QuickInfoBuilder(this.tsLS, compiler, templateInfo.component, node).get(); this.compilerFactory.registerLastKnownProgram(); return results; } @@ -131,9 +132,13 @@ export class LanguageService { if (positionDetails === null) { return null; } + + const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ? + positionDetails.context.nodes[0] : + positionDetails.context.node; return new CompletionBuilder( - this.tsLS, compiler, templateInfo.component, positionDetails.nodeInContext.node, - nodeContextFromTarget(positionDetails.nodeInContext), positionDetails.parent, + this.tsLS, compiler, templateInfo.component, node, + nodeContextFromTarget(positionDetails.context), positionDetails.parent, positionDetails.template); } @@ -260,7 +265,7 @@ function getOrCreateTypeCheckScriptInfo( return scriptInfo; } -function nodeContextFromTarget(target: TargetNode): CompletionNodeContext { +function nodeContextFromTarget(target: TargetContext): CompletionNodeContext { switch (target.kind) { case TargetNodeKind.ElementInTagContext: return CompletionNodeContext.ElementTag; diff --git a/packages/language-service/ivy/references.ts b/packages/language-service/ivy/references.ts index 13a79ee1be..81ba73a9c5 100644 --- a/packages/language-service/ivy/references.ts +++ b/packages/language-service/ivy/references.ts @@ -12,7 +12,7 @@ import {DirectiveSymbol, SymbolKind, TemplateTypeChecker, TypeCheckingProgramStr import {ExpressionIdentifier, hasExpressionIdentifier} from '@angular/compiler-cli/src/ngtsc/typecheck/src/comments'; import * as ts from 'typescript'; -import {getTargetAtPosition} from './template_target'; +import {getTargetAtPosition, TargetNodeKind} from './template_target'; import {findTightestNode} from './ts_utils'; import {getDirectiveMatchesForAttribute, getDirectiveMatchesForElementTag, getTemplateInfoAtPosition, isWithin, TemplateInfo, toTextSpan} from './utils'; @@ -39,7 +39,9 @@ export class ReferenceBuilder { return undefined; } - const node = positionDetails.nodeInContext.node; + const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ? + positionDetails.context.nodes[0] : + positionDetails.context.node; // Get the information about the TCB at the template position. const symbol = this.ttc.getSymbolOfNode(node, component); @@ -74,13 +76,12 @@ export class ReferenceBuilder { case SymbolKind.Variable: { const {positionInShimFile: initializerPosition, shimPath} = symbol.initializerLocation; const localVarPosition = symbol.localVarLocation.positionInShimFile; - const templateNode = positionDetails.nodeInContext.node; - if ((templateNode instanceof TmplAstVariable)) { - if (templateNode.valueSpan !== undefined && isWithin(position, templateNode.valueSpan)) { + if ((node instanceof TmplAstVariable)) { + if (node.valueSpan !== undefined && isWithin(position, node.valueSpan)) { // In the valueSpan of the variable, we want to get the reference of the initializer. return this.getReferencesAtTypescriptPosition(shimPath, initializerPosition); - } else if (isWithin(position, templateNode.keySpan)) { + } else if (isWithin(position, node.keySpan)) { // In the keySpan of the variable, we want to get the reference of the local variable. return this.getReferencesAtTypescriptPosition(shimPath, localVarPosition); } else { diff --git a/packages/language-service/ivy/template_target.ts b/packages/language-service/ivy/template_target.ts index eff933a3d4..6c94a2b544 100644 --- a/packages/language-service/ivy/template_target.ts +++ b/packages/language-service/ivy/template_target.ts @@ -10,7 +10,7 @@ import {ParseSpan, TmplAstBoundEvent} from '@angular/compiler'; import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expression AST import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST -import {isTemplateNode, isTemplateNodeWithKeyAndValue, isWithin, isWithinKeyValue} from './utils'; +import {isTemplateNodeWithKeyAndValue, isWithin, isWithinKeyValue} from './utils'; /** * Contextual information for a target position within the template. @@ -22,9 +22,9 @@ export interface TemplateTarget { position: number; /** - * The template node (or AST expression) closest to the search position. + * The template (or AST expression) node or nodes closest to the search position. */ - nodeInContext: TargetNode; + context: TargetContext; /** * The `t.Template` which contains the found node or expression (or `null` if in the root @@ -39,15 +39,26 @@ export interface TemplateTarget { } /** - * A node targeted at a given position in the template, including potential contextual information - * about the specific aspect of the node being referenced. + * A node or nodes targeted at a given position in the template, including potential contextual + * information about the specific aspect of the node being referenced. * * Some nodes have multiple interior contexts. For example, `t.Element` nodes have both a tag name * as well as a body, and a given position definitively points to one or the other. `TargetNode` * captures the node itself, as well as this additional contextual disambiguation. */ -export type TargetNode = RawExpression|RawTemplateNode|ElementInBodyContext|ElementInTagContext| - AttributeInKeyContext|AttributeInValueContext; +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; + +/** + * Contexts which logically target multiple nodes in the template AST, which cannot be + * disambiguated given a single position because they are all equally relavent. For example, in the + * banana-in-a-box syntax `[(ngModel)]="formValues.person"`, the position in the template for the + * key `ngModel` refers to both the bound event `ngModelChange` and the input `ngModel`. + */ +export type MultiNodeTarget = TwoWayBindingContext; /** * Differentiates the various kinds of `TargetNode`s. @@ -59,6 +70,7 @@ export enum TargetNodeKind { ElementInBodyContext, AttributeInKeyContext, AttributeInValueContext, + TwoWayBindingContext, } /** @@ -105,6 +117,15 @@ export interface AttributeInValueContext { node: t.TextAttribute|t.BoundAttribute|t.BoundEvent; } +/** + * A `t.BoundAttribute` and `t.BoundEvent` pair that are targeted, where the given position is + * within the key span of both. + */ +export interface TwoWayBindingContext { + kind: TargetNodeKind.TwoWayBindingContext; + nodes: [t.BoundAttribute, t.BoundEvent]; +} + /** * This special marker is added to the path when the cursor is within the sourceSpan but not the key * or value span of a node with key/value spans. @@ -141,7 +162,7 @@ export function getTargetAtPosition(template: t.Node[], position: number): Templ } // Given the candidate node, determine the full targeted context. - let nodeInContext: TargetNode; + let nodeInContext: TargetContext; if (candidate instanceof e.AST) { nodeInContext = { kind: TargetNodeKind.RawExpression, @@ -188,7 +209,7 @@ export function getTargetAtPosition(template: t.Node[], position: number): Templ }; } - return {position, nodeInContext, template: context, parent}; + return {position, context: nodeInContext, template: context, parent}; } /** diff --git a/packages/language-service/ivy/test/legacy/template_target_spec.ts b/packages/language-service/ivy/test/legacy/template_target_spec.ts index 047cf508c3..52e58a7903 100644 --- a/packages/language-service/ivy/test/legacy/template_target_spec.ts +++ b/packages/language-service/ivy/test/legacy/template_target_spec.ts @@ -10,7 +10,7 @@ import {ParseError, parseTemplate} from '@angular/compiler'; import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expression AST import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST -import {getTargetAtPosition, TargetNodeKind} from '../../template_target'; +import {getTargetAtPosition, SingleNodeTarget, TargetNodeKind} from '../../template_target'; import {isExpressionNode, isTemplateNode} from '../../utils'; interface ParseResult { @@ -36,8 +36,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate element in opening tag', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Element); }); @@ -45,8 +45,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate element in closing tag', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Element); }); @@ -54,8 +54,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate element when cursor is at the beginning', () => { const {errors, nodes, position} = parse(`<¦div>
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Element); }); @@ -63,8 +63,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate element when cursor is at the end', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Element); }); @@ -72,8 +72,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate attribute key', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.TextAttribute); }); @@ -81,8 +81,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate attribute value', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); // TODO: Note that we do not have the ability to detect the RHS (yet) expect(node).toBeInstanceOf(t.TextAttribute); @@ -91,8 +91,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate bound attribute key', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.BoundAttribute); }); @@ -100,8 +100,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate bound attribute value', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); }); @@ -116,8 +116,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate bound event key', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.BoundEvent); }); @@ -125,8 +125,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate bound event value', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.MethodCall); }); @@ -134,8 +134,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate element children', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Element); expect((node as t.Element).name).toBe('span'); @@ -144,8 +144,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate element reference', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Reference); }); @@ -153,8 +153,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template text attribute', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.TextAttribute); }); @@ -162,8 +162,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template bound attribute key', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.BoundAttribute); }); @@ -171,8 +171,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template bound attribute value', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); }); @@ -180,8 +180,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template bound attribute key in two-way binding', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.BoundAttribute); expect((node as t.BoundAttribute).name).toBe('foo'); @@ -190,8 +190,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template bound attribute value in two-way binding', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); expect((node as e.PropertyRead).name).toBe('bar'); @@ -200,8 +200,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template bound event key', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.BoundEvent); }); @@ -209,16 +209,16 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template bound event value', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(node).toBeInstanceOf(e.MethodCall); }); it('should locate template attribute key', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.TextAttribute); }); @@ -226,8 +226,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template attribute value', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); // TODO: Note that we do not have the ability to detect the RHS (yet) expect(node).toBeInstanceOf(t.TextAttribute); @@ -236,8 +236,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template reference key via the # notation', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Reference); expect((node as t.Reference).name).toBe('foo'); @@ -246,8 +246,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template reference key via the ref- notation', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Reference); expect((node as t.Reference).name).toBe('foo'); @@ -256,8 +256,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template reference value via the # notation', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Reference); expect((node as t.Reference).value).toBe('exportAs'); @@ -267,8 +267,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template reference value via the ref- notation', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Reference); expect((node as t.Reference).value).toBe('exportAs'); @@ -278,8 +278,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template variable key', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Variable); }); @@ -287,8 +287,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template variable value', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Variable); }); @@ -296,8 +296,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate template children', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Element); }); @@ -305,8 +305,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate ng-content', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Content); }); @@ -314,8 +314,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate ng-content attribute key', () => { const {errors, nodes, position} = parse(''); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.TextAttribute); }); @@ -323,8 +323,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate ng-content attribute value', () => { const {errors, nodes, position} = parse(''); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; // TODO: Note that we do not have the ability to detect the RHS (yet) expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.TextAttribute); @@ -333,8 +333,8 @@ describe('getTargetAtPosition for template AST', () => { it('should not locate implicit receiver', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); }); @@ -342,8 +342,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate bound attribute key in two-way binding', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.BoundAttribute); expect((node as t.BoundAttribute).name).toBe('foo'); @@ -352,8 +352,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate bound attribute value in two-way binding', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); expect((node as e.PropertyRead).name).toBe('bar'); @@ -362,8 +362,8 @@ describe('getTargetAtPosition for template AST', () => { it('should locate switch value in ICUs', () => { const {errors, nodes, position} = parse(`{sw¦itch, plural, other {text}}">`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); expect((node as e.PropertyRead).name).toBe('switch'); @@ -373,8 +373,8 @@ describe('getTargetAtPosition for template AST', () => { const {errors, nodes, position} = parse( `{expr, plural, other { {ne¦sted, plural, =1 { {{nestedInterpolation}} }} }}">`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); expect((node as e.PropertyRead).name).toBe('nested'); @@ -384,8 +384,8 @@ describe('getTargetAtPosition for template AST', () => { const {errors, nodes, position} = parse(`{expr, plural, other { {{ i¦nterpolation }} }}">`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); expect((node as e.PropertyRead).name).toBe('interpolation'); @@ -395,8 +395,8 @@ describe('getTargetAtPosition for template AST', () => { const {errors, nodes, position} = parse( `{expr, plural, other { {nested, plural, =1 { {{n¦estedInterpolation}} }} }}">`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); expect((node as e.PropertyRead).name).toBe('nestedInterpolation'); @@ -407,8 +407,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should not locate implicit receiver', () => { const {errors, nodes, position} = parse(`{{ ¦title }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); expect((node as e.PropertyRead).name).toBe('title'); @@ -417,8 +417,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate property read', () => { const {errors, nodes, position} = parse(`{{ ti¦tle }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); expect((node as e.PropertyRead).name).toBe('title'); @@ -427,8 +427,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate safe property read', () => { const {errors, nodes, position} = parse(`{{ foo?¦.bar }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.SafePropertyRead); expect((node as e.SafePropertyRead).name).toBe('bar'); @@ -437,8 +437,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate keyed read', () => { const {errors, nodes, position} = parse(`{{ foo['bar']¦ }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.KeyedRead); }); @@ -446,8 +446,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate property write', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyWrite); }); @@ -455,8 +455,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate keyed write', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.KeyedWrite); }); @@ -464,8 +464,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate binary', () => { const {errors, nodes, position} = parse(`{{ 1 +¦ 2 }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.Binary); }); @@ -473,8 +473,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate binding pipe with an identifier', () => { const {errors, nodes, position} = parse(`{{ title | p¦ }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.BindingPipe); }); @@ -485,8 +485,8 @@ describe('getTargetAtPosition for expression AST', () => { expect(errors![0].toString()) .toContain( 'Unexpected end of input, expected identifier or keyword at the end of the expression'); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); // TODO: We want this to be a BindingPipe. expect(node).toBeInstanceOf(e.Interpolation); @@ -498,7 +498,7 @@ describe('getTargetAtPosition for expression AST', () => { // parser throws an error. This case is important for autocomplete. // const {errors, nodes, position} = parse(`{{ title | ¦ }}`); // expect(errors).toBe(null); - // const {nodeInContext} = findNodeAtPosition(nodes, position)!; + // const {context} = findNodeAtPosition(nodes, position)!; // expect(isExpressionNode(node!)).toBe(true); // expect(node).toBeInstanceOf(e.BindingPipe); }); @@ -507,8 +507,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate method call', () => { const {errors, nodes, position} = parse(`{{ title.toString(¦) }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.MethodCall); }); @@ -516,8 +516,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate safe method call', () => { const {errors, nodes, position} = parse(`{{ title?.toString(¦) }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.SafeMethodCall); }); @@ -525,8 +525,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate literal primitive in interpolation', () => { const {errors, nodes, position} = parse(`{{ title.indexOf('t¦') }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.LiteralPrimitive); expect((node as e.LiteralPrimitive).value).toBe('t'); @@ -535,8 +535,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate literal primitive in binding', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.LiteralPrimitive); expect((node as e.LiteralPrimitive).value).toBe('t'); @@ -545,8 +545,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate empty expression', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.EmptyExpr); }); @@ -554,8 +554,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate literal array', () => { const {errors, nodes, position} = parse(`{{ [1, 2,¦ 3] }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.LiteralArray); }); @@ -563,8 +563,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate literal map', () => { const {errors, nodes, position} = parse(`{{ { hello:¦ "world" } }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.LiteralMap); }); @@ -572,8 +572,8 @@ describe('getTargetAtPosition for expression AST', () => { it('should locate conditional', () => { const {errors, nodes, position} = parse(`{{ cond ?¦ true : false }}`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.Conditional); }); @@ -583,8 +583,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { it('should locate template key', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.BoundAttribute); }); @@ -592,8 +592,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { it('should locate template value', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); }); @@ -601,8 +601,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { it('should locate property read next to variable in structural directive syntax', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); }); @@ -612,8 +612,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { // ngFor is a text attribute because the desugared form is // expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBeTrue(); expect(node).toBeInstanceOf(t.TextAttribute); expect((node as t.TextAttribute).name).toBe('ngFor'); @@ -629,8 +629,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { it('should locate let variable', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Variable); expect((node as t.Variable).name).toBe('item'); @@ -639,8 +639,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { it('should locate bound attribute key', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.BoundAttribute); expect((node as t.BoundAttribute).name).toBe('ngForOf'); @@ -649,8 +649,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { it('should locate bound attribute key when cursor is at the start', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const node = nodeInContext.node; + const {context} = getTargetAtPosition(nodes, position)!; + const node = (context as SingleNodeTarget).node; expect(isTemplateNode(node)).toBe(true); expect(node).toBeInstanceOf(t.BoundAttribute); expect((node as t.BoundAttribute).name).toBe('ngForOf'); @@ -660,8 +660,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.BoundAttribute); expect((node as t.BoundAttribute).name).toBe('ngForTrackBy'); @@ -674,8 +674,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.BoundAttribute); expect((node as t.BoundAttribute).name).toBe('ngForOf'); @@ -684,8 +684,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { it('should locate bound attribute value', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); expect((node as e.PropertyRead).name).toBe('items'); @@ -694,12 +694,12 @@ describe('findNodeAtPosition for microsyntax expression', () => { it('should locate template children', () => { const {errors, nodes, position} = parse(``); expect(errors).toBe(null); - const {nodeInContext, template: context} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context, template} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Element); expect((node as t.Element).name).toBe('div'); - expect(context).toBeInstanceOf(t.Template); + expect(template).toBeInstanceOf(t.Template); }); it('should locate property read of variable declared within template', () => { @@ -708,8 +708,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { {{ i¦ }} `); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isExpressionNode(node!)).toBe(true); expect(node).toBeInstanceOf(e.PropertyRead); }); @@ -717,8 +717,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { it('should locate LHS of variable declaration', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Variable); // TODO: Currently there is no way to distinguish LHS from RHS @@ -728,8 +728,8 @@ describe('findNodeAtPosition for microsyntax expression', () => { it('should locate RHS of variable declaration', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - const {node} = nodeInContext; + const {context} = getTargetAtPosition(nodes, position)!; + const {node} = context as SingleNodeTarget; expect(isTemplateNode(node!)).toBe(true); expect(node).toBeInstanceOf(t.Variable); // TODO: Currently there is no way to distinguish LHS from RHS @@ -739,16 +739,16 @@ describe('findNodeAtPosition for microsyntax expression', () => { it('should locate an element in its tag context', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - expect(nodeInContext.kind).toBe(TargetNodeKind.ElementInTagContext); - expect(nodeInContext.node).toBeInstanceOf(t.Element); + const {context} = getTargetAtPosition(nodes, position)!; + expect(context.kind).toBe(TargetNodeKind.ElementInTagContext); + expect((context as SingleNodeTarget).node).toBeInstanceOf(t.Element); }); it('should locate an element in its body context', () => { const {errors, nodes, position} = parse(`
`); expect(errors).toBe(null); - const {nodeInContext} = getTargetAtPosition(nodes, position)!; - expect(nodeInContext.kind).toBe(TargetNodeKind.ElementInBodyContext); - expect(nodeInContext.node).toBeInstanceOf(t.Element); + const {context} = getTargetAtPosition(nodes, position)!; + expect(context.kind).toBe(TargetNodeKind.ElementInBodyContext); + expect((context as SingleNodeTarget).node).toBeInstanceOf(t.Element); }); });