From 58d52df7c45a6c7b7c12fbf1f0b1f341c3d61e3c Mon Sep 17 00:00:00 2001 From: Dylan Hunn Date: Tue, 10 Oct 2023 16:46:17 -0700 Subject: [PATCH] feat(language-service): Complete inside @switch We now visit the unknown nodes inside a `@switch` block, enabling completions in that context. --- packages/language-service/src/completions.ts | 23 +++++++++++-------- .../language-service/src/template_target.ts | 1 + .../language-service/test/completions_spec.ts | 9 ++++++++ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/language-service/src/completions.ts b/packages/language-service/src/completions.ts index d64cac480bdd4..006f8fcc9ed5d 100644 --- a/packages/language-service/src/completions.ts +++ b/packages/language-service/src/completions.ts @@ -9,7 +9,7 @@ import {AST, ASTWithSource, BindingPipe, BindingType, Call, EmptyExpr, ImplicitReceiver, LiteralPrimitive, ParsedEventType, ParseSourceSpan, PropertyRead, PropertyWrite, SafePropertyRead, TmplAstBoundAttribute, TmplAstBoundEvent, TmplAstElement, TmplAstNode, TmplAstReference, TmplAstTemplate, TmplAstText, TmplAstTextAttribute, TmplAstVariable} from '@angular/compiler'; import {NgCompiler} from '@angular/compiler-cli/src/ngtsc/core'; import {CompletionKind, PotentialDirective, SymbolKind, TemplateDeclarationSymbol} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; -import {BoundEvent, DeferredBlock, TextAttribute, UnknownBlock} from '@angular/compiler/src/render3/r3_ast'; +import {BoundEvent, DeferredBlock, SwitchBlock, TextAttribute, UnknownBlock} from '@angular/compiler/src/render3/r3_ast'; import ts from 'typescript'; import {addAttributeCompletionEntries, AsciiSortPriority, AttributeCompletionKind, buildAnimationCompletionEntries, buildAttributeCompletionTable, getAttributeCompletionSymbol} from './attribute_completions'; @@ -130,15 +130,18 @@ export class CompletionBuilder { length: this.node.name.length, } }; - const completionEntries: ts.CompletionEntry[] = [ - ...blocksWithParens, ...blocksWithoutParens - ].map(name => ({ - name, - sortText: `${AsciiSortPriority.First}${name}`, - insertText: buildBlockSnippet(useSnippet, name, blocksWithParens.includes(name)), - isSnippet: useSnippet || undefined, - ...partialCompletionEntryWholeBlock, - })); + let competionKeywords: string[] = [...blocksWithParens, ...blocksWithoutParens]; + if (this.nodeParent instanceof SwitchBlock) { + competionKeywords = ['case', 'default']; + } + const completionEntries: ts.CompletionEntry[] = competionKeywords.map( + name => ({ + name, + sortText: `${AsciiSortPriority.First}${name}`, + insertText: buildBlockSnippet(useSnippet, name, blocksWithParens.includes(name)), + isSnippet: useSnippet || undefined, + ...partialCompletionEntryWholeBlock, + })); // Return the completions. const completionInfo: ts.CompletionInfo = { diff --git a/packages/language-service/src/template_target.ts b/packages/language-service/src/template_target.ts index 3ddbc7da14ff8..bed3337dd7889 100644 --- a/packages/language-service/src/template_target.ts +++ b/packages/language-service/src/template_target.ts @@ -499,6 +499,7 @@ class TemplateTargetVisitor implements t.Visitor { visitSwitchBlock(block: t.SwitchBlock) { this.visitBinding(block.expression); this.visitAll(block.cases); + this.visitAll(block.unknownBlocks); } visitSwitchBlockCase(block: t.SwitchBlockCase) { diff --git a/packages/language-service/test/completions_spec.ts b/packages/language-service/test/completions_spec.ts index 47c2619bb270e..038c026b99162 100644 --- a/packages/language-service/test/completions_spec.ts +++ b/packages/language-service/test/completions_spec.ts @@ -298,6 +298,15 @@ describe('completions', () => { completions, unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.BLOCK), ['switch']); }); + + it('inside switch', () => { + const {templateFile} = setup(`@switch (1) { @c }`, ``); + templateFile.moveCursorToText('@c¦'); + const completions = templateFile.getCompletionsAtPosition(); + expectContain( + completions, unsafeCastDisplayInfoKindToScriptElementKind(DisplayInfoKind.BLOCK), + ['case']); + }); }); describe('in an expression scope', () => {