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
This commit is contained in:
Andrew Scott 2020-12-16 14:39:49 -08:00 committed by atscott
parent 46ea684351
commit a893187d51
5 changed files with 204 additions and 175 deletions

View File

@ -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};
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -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};
}
/**

View File

@ -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(`<di¦v></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);
});
@ -45,8 +45,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate element in closing tag', () => {
const {errors, nodes, position} = parse(`<div></di¦v>`);
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></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(`<div¦></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);
});
@ -72,8 +72,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate attribute key', () => {
const {errors, nodes, position} = parse(`<div cla¦ss="foo"></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.TextAttribute);
});
@ -81,8 +81,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate attribute value', () => {
const {errors, nodes, position} = parse(`<div class="fo¦o"></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);
// 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(`<test-cmp [fo¦o]="bar"></test-cmp>`);
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(`<test-cmp [foo]="b¦ar"></test-cmp>`);
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(`<test-cmp (fo¦o)="bar()"></test-cmp>`);
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(`<test-cmp (foo)="b¦ar()"></test-cmp>`);
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(`<div><sp¦an></span></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);
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(`<div #my¦div></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.Reference);
});
@ -153,8 +153,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template text attribute', () => {
const {errors, nodes, position} = parse(`<ng-template ng¦If></ng-template>`);
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(`<ng-template [ng¦If]="foo"></ng-template>`);
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(`<ng-template [ngIf]="f¦oo"></ng-template>`);
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(`<ng-template [(f¦oo)]="bar"></ng-template>`);
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(`<ng-template [(foo)]="b¦ar"></ng-template>`);
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(`<ng-template (cl¦ick)="foo()"></ng-template>`);
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(`<ng-template (click)="f¦oo()"></ng-template>`);
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(`<ng-template i¦d="foo"></ng-template>`);
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(`<ng-template id="f¦oo"></ng-template>`);
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(`<ng-template #f¦oo></ng-template>`);
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(`<ng-template ref-fo¦o></ng-template>`);
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(`<ng-template #foo="export¦As"></ng-template>`);
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(`<ng-template ref-foo="export¦As"></ng-template>`);
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(`<ng-template let-f¦oo="bar"></ng-template>`);
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(`<ng-template let-foo="b¦ar"></ng-template>`);
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(`<ng-template><d¦iv></div></ng-template>`);
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(`<ng-co¦ntent></ng-content>`);
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('<ng-content cla¦ss="red"></ng-content>');
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('<ng-content class="r¦ed"></ng-content>');
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(`<div [foo]="¦bar"></div>`);
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(`<cmp [(f¦oo)]="bar"></cmp>`);
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(`<cmp [(foo)]="b¦ar"></cmp>`);
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(`<span i18n>{sw¦itch, plural, other {text}}"></span>`);
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(
`<span i18n>{expr, plural, other { {ne¦sted, plural, =1 { {{nestedInterpolation}} }} }}"></span>`);
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(`<span i18n>{expr, plural, other { {{ i¦nterpolation }} }}"></span>`);
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(
`<span i18n>{expr, plural, other { {nested, plural, =1 { {{n¦estedInterpolation}} }} }}"></span>`);
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(`<div (foo)="b¦ar=$event"></div>`);
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(`<div (foo)="bar['baz']¦=$event"></div>`);
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(`<div [id]="'t¦'"></div>`);
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(`<div [id]="¦"></div>`);
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(`<div *ng¦If="foo"></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.BoundAttribute);
});
@ -592,8 +592,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate template value', () => {
const {errors, nodes, position} = parse(`<div *ngIf="f¦oo"></div>`);
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(`<div *ngIf="fo¦o as bar"></div>`);
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
// <ng-template ngFor let-item [ngForOf]="items">
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(`<div *ngFor="let i¦tem of items"></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.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(`<div *ngFor="let item o¦f items"></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.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(`<div *ngFor="let item ¦of items"></div>`);
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(`<div *ngFor="let item of items; trac¦kBy: trackByFn"></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.BoundAttribute);
expect((node as t.BoundAttribute).name).toBe('ngForTrackBy');
@ -674,8 +674,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
const {errors, nodes, position} =
parse(`<div *ngFor="let item o¦f items; trackBy: trackByFn"></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.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(`<div *ngFor="let item of it¦ems"></div>`);
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(`<di¦v *ngIf></div>`);
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¦ }}
</div>`);
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(`<div *ngFor="let item of items; let i¦=index">`);
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(`<div *ngFor="let item of items; let i=in¦dex">`);
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(`<div¦ attr></div>`);
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(`<div ¦ attr></div>`);
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);
});
});