66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import { AST , ASTWithSource , BindingPipe , Call , EmptyExpr , ImplicitReceiver , LiteralPrimitive , ParseSourceSpan , PropertyRead , PropertyWrite , SafePropertyRead , TmplAstBoundAttribute , TmplAstBoundEvent , TmplAstElement , TmplAstNode , TmplAstReference , TmplAstTemplate , TmplAstText , TmplAstTextAttribute , TmplAstVariable } from '@angular/compiler' ;
9+ 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' ;
1010import { NgCompiler } from '@angular/compiler-cli/src/ngtsc/core' ;
1111import { CompletionKind , DirectiveInScope , SymbolKind , TemplateDeclarationSymbol } from '@angular/compiler-cli/src/ngtsc/typecheck/api' ;
1212import { BoundEvent , TextAttribute } from '@angular/compiler/src/render3/r3_ast' ;
1313import ts from 'typescript' ;
1414
15- import { addAttributeCompletionEntries , AttributeCompletionKind , buildAttributeCompletionTable , getAttributeCompletionSymbol } from './attribute_completions' ;
15+ import { addAttributeCompletionEntries , AttributeCompletionKind , buildAnimationCompletionEntries , buildAttributeCompletionTable , getAttributeCompletionSymbol } from './attribute_completions' ;
1616import { DisplayInfo , DisplayInfoKind , getDirectiveDisplayInfo , getSymbolDisplayInfo , getTsSymbolDisplayInfo , unsafeCastDisplayInfoKindToScriptElementKind } from './display_parts' ;
1717import { TargetContext , TargetNodeKind , TemplateTarget } from './template_target' ;
18- import { filterAliasImports , isBoundEventWithSyntheticHandler } from './utils' ;
18+ import { filterAliasImports , isBoundEventWithSyntheticHandler , isWithin } from './utils' ;
1919
2020type PropertyExpressionCompletionBuilder =
2121 CompletionBuilder < PropertyRead | PropertyWrite | EmptyExpr | SafePropertyRead | TmplAstBoundEvent > ;
@@ -27,6 +27,8 @@ type PipeCompletionBuilder = CompletionBuilder<BindingPipe>;
2727
2828type LiteralCompletionBuilder = CompletionBuilder < LiteralPrimitive | TextAttribute > ;
2929
30+ type ElementAnimationCompletionBuilder = CompletionBuilder < TmplAstBoundAttribute | TmplAstBoundEvent > ;
31+
3032export enum CompletionNodeContext {
3133 None ,
3234 ElementTag ,
@@ -36,6 +38,8 @@ export enum CompletionNodeContext {
3638 TwoWayBinding ,
3739}
3840
41+ const ANIMATION_PHASES = [ 'start' , 'done' ] ;
42+
3943/**
4044 * Performs autocompletion operations on a given node in the template.
4145 *
@@ -70,7 +74,11 @@ export class CompletionBuilder<N extends TmplAstNode|AST> {
7074 } else if ( this . isElementTagCompletion ( ) ) {
7175 return this . getElementTagCompletion ( ) ;
7276 } else if ( this . isElementAttributeCompletion ( ) ) {
73- return this . getElementAttributeCompletions ( options ) ;
77+ if ( this . isAnimationCompletion ( ) ) {
78+ return this . getAnimationCompletions ( ) ;
79+ } else {
80+ return this . getElementAttributeCompletions ( options ) ;
81+ }
7482 } else if ( this . isPipeCompletion ( ) ) {
7583 return this . getPipeCompletions ( ) ;
7684 } else if ( this . isLiteralCompletion ( ) ) {
@@ -531,6 +539,68 @@ export class CompletionBuilder<N extends TmplAstNode|AST> {
531539 return directive ?. tsSymbol ;
532540 }
533541
542+ private isAnimationCompletion ( ) : this is ElementAnimationCompletionBuilder {
543+ return ( this . node instanceof TmplAstBoundAttribute &&
544+ this . node . type === BindingType . Animation ) ||
545+ ( this . node instanceof TmplAstBoundEvent && this . node . type === ParsedEventType . Animation ) ;
546+ }
547+
548+ private getAnimationCompletions ( this : ElementAnimationCompletionBuilder ) :
549+ ts . WithMetadata < ts . CompletionInfo > | undefined {
550+ if ( this . node instanceof TmplAstBoundAttribute ) {
551+ const animations = this . compiler . getTemplateTypeChecker ( )
552+ . getDirectiveMetadata ( this . component )
553+ ?. animationTriggerNames ?. staticTriggerNames ;
554+ const replacementSpan = makeReplacementSpanFromParseSourceSpan ( this . node . keySpan ) ;
555+
556+ if ( animations === undefined ) {
557+ return undefined ;
558+ }
559+
560+ const entries = buildAnimationCompletionEntries (
561+ [ ...animations , '.disabled' ] , replacementSpan , DisplayInfoKind . ATTRIBUTE ) ;
562+ return {
563+ entries,
564+ isGlobalCompletion : false ,
565+ isMemberCompletion : false ,
566+ isNewIdentifierLocation : true ,
567+ } ;
568+ } else {
569+ const animationNameSpan = buildAnimationNameSpan ( this . node ) ;
570+ const phaseSpan = buildAnimationPhaseSpan ( this . node ) ;
571+ if ( isWithin ( this . position , animationNameSpan ) ) {
572+ const animations = this . compiler . getTemplateTypeChecker ( )
573+ . getDirectiveMetadata ( this . component )
574+ ?. animationTriggerNames ?. staticTriggerNames ;
575+ const replacementSpan = makeReplacementSpanFromParseSourceSpan ( animationNameSpan ) ;
576+
577+ if ( animations === undefined ) {
578+ return undefined ;
579+ }
580+
581+ const entries =
582+ buildAnimationCompletionEntries ( animations , replacementSpan , DisplayInfoKind . EVENT ) ;
583+ return {
584+ entries,
585+ isGlobalCompletion : false ,
586+ isMemberCompletion : false ,
587+ isNewIdentifierLocation : true ,
588+ } ;
589+ }
590+ if ( phaseSpan !== null && isWithin ( this . position , phaseSpan ) ) {
591+ const replacementSpan = makeReplacementSpanFromParseSourceSpan ( phaseSpan ) ;
592+ const entries = buildAnimationCompletionEntries (
593+ ANIMATION_PHASES , replacementSpan , DisplayInfoKind . EVENT ) ;
594+ return {
595+ entries,
596+ isGlobalCompletion : false ,
597+ isMemberCompletion : false ,
598+ isNewIdentifierLocation : true ,
599+ } ;
600+ }
601+ }
602+ }
603+
534604 private isElementAttributeCompletion ( ) : this is ElementAttributeCompletionBuilder {
535605 return ( this . nodeContext === CompletionNodeContext . ElementAttributeKey ||
536606 this . nodeContext === CompletionNodeContext . TwoWayBinding ) &&
@@ -884,3 +954,14 @@ function nodeContextFromTarget(target: TargetContext): CompletionNodeContext {
884954 return CompletionNodeContext . None ;
885955 }
886956}
957+
958+ function buildAnimationNameSpan ( node : TmplAstBoundEvent ) : ParseSourceSpan {
959+ return new ParseSourceSpan ( node . keySpan . start , node . keySpan . start . moveBy ( node . name . length ) ) ;
960+ }
961+
962+ function buildAnimationPhaseSpan ( node : TmplAstBoundEvent ) : ParseSourceSpan | null {
963+ if ( node . phase !== null ) {
964+ return new ParseSourceSpan ( node . keySpan . end . moveBy ( - node . phase . length ) , node . keySpan . end ) ;
965+ }
966+ return null ;
967+ }
0 commit comments