diff --git a/packages/language-service/ivy/template_target.ts b/packages/language-service/ivy/template_target.ts index 3207cdcd42..4a18374a3b 100644 --- a/packages/language-service/ivy/template_target.ts +++ b/packages/language-service/ivy/template_target.ts @@ -100,6 +100,15 @@ class TemplateTargetVisitor implements t.Visitor { private constructor(private readonly position: number) {} visit(node: t.Node) { + const last: t.Node|e.AST|undefined = this.path[this.path.length - 1]; + if (last && isTemplateNodeWithKeyAndValue(last) && isWithin(this.position, last.keySpan)) { + // We've already identified that we are within a `keySpan` of a node. + // We should stop processing nodes at this point to prevent matching + // any other nodes. This can happen when the end span of a different node + // touches the start of the keySpan for the candidate node. Because + // our `isWithin` logic is inclusive on both ends, we can match both nodes. + return; + } const {start, end} = getSpanIncludingEndTag(node); if (isWithin(this.position, {start, end})) { this.path.push(node); 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 f54a374edc..a2ac29fa81 100644 --- a/packages/language-service/ivy/test/legacy/template_target_spec.ts +++ b/packages/language-service/ivy/test/legacy/template_target_spec.ts @@ -580,13 +580,10 @@ 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); - // TODO(atscott): Fix this - we throw away the result because we match the variable node, after - // the attribute binding, then throw away the result because we aren't in the variable key - expect(getTargetAtPosition(nodes, position)).toBeNull(); - // const {node} = getTargetAtPosition(nodes, position)!; - // expect(isTemplateNode(node!)).toBe(true); - // expect(node).toBeInstanceOf(t.BoundAttribute); - // expect((node as t.BoundAttribute).name).toBe('ngForOf'); + const {node} = getTargetAtPosition(nodes, position)!; + expect(isTemplateNode(node!)).toBe(true); + expect(node).toBeInstanceOf(t.BoundAttribute); + expect((node as t.BoundAttribute).name).toBe('ngForOf'); }); it('should locate bound attribute key for trackBy', () => {