@@ -11,43 +11,60 @@ namespace ts.codefix {
1111 getCodeActions ( context ) {
1212 const info = getInfo ( context . sourceFile , context . span . start , context . program . getTypeChecker ( ) ) ;
1313 if ( ! info ) return undefined ;
14- const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info ;
15- const methodCodeAction = call && getActionForMethodDeclaration ( context , classDeclarationSourceFile , classDeclaration , token , call , makeStatic , inJs , context . preferences ) ;
14+
15+ if ( isEnumInfo ( info ) ) {
16+ return singleElementArray ( getActionForEnumMemberDeclaration ( context , info . enumDeclarationSourceFile , info . declaration , info . token ) ) ;
17+ }
18+ const { declaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info ;
19+ const methodCodeAction = call && getActionForMethodDeclaration ( context , classDeclarationSourceFile , declaration , token , call , makeStatic , inJs , context . preferences ) ;
1620 const addMember = inJs ?
17- singleElementArray ( getActionsForAddMissingMemberInJavaScriptFile ( context , classDeclarationSourceFile , classDeclaration , token . text , makeStatic ) ) :
18- getActionsForAddMissingMemberInTypeScriptFile ( context , classDeclarationSourceFile , classDeclaration , token , makeStatic ) ;
21+ singleElementArray ( getActionsForAddMissingMemberInJavaScriptFile ( context , classDeclarationSourceFile , declaration , token . text , makeStatic ) ) :
22+ getActionsForAddMissingMemberInTypeScriptFile ( context , classDeclarationSourceFile , declaration , token , makeStatic ) ;
1923 return concatenate ( singleElementArray ( methodCodeAction ) , addMember ) ;
2024 } ,
2125 fixIds : [ fixId ] ,
2226 getAllCodeActions : context => {
2327 const seenNames = createMap < true > ( ) ;
2428 return codeFixAll ( context , errorCodes , ( changes , diag ) => {
2529 const { program, preferences } = context ;
26- const info = getInfo ( diag . file , diag . start , program . getTypeChecker ( ) ) ;
27- if ( ! info ) return ;
28- const { classDeclaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info ;
29- if ( ! addToSeen ( seenNames , token . text ) ) {
30+ const checker = program . getTypeChecker ( ) ;
31+ const info = getInfo ( diag . file , diag . start , checker ) ;
32+ if ( ! info || ! addToSeen ( seenNames , info . token . text ) ) {
3033 return ;
3134 }
3235
33- // Always prefer to add a method declaration if possible.
34- if ( call ) {
35- addMethodDeclaration ( context , changes , classDeclarationSourceFile , classDeclaration , token , call , makeStatic , inJs , preferences ) ;
36+ if ( isEnumInfo ( info ) ) {
37+ const { token , declaration , enumDeclarationSourceFile } = info ;
38+ addEnumMemberDeclaration ( changes , checker , token , declaration , enumDeclarationSourceFile ) ;
3639 }
3740 else {
38- if ( inJs ) {
39- addMissingMemberInJs ( changes , classDeclarationSourceFile , classDeclaration , token . text , makeStatic ) ;
41+ const { declaration, classDeclarationSourceFile, inJs, makeStatic, token, call } = info ;
42+ // Always prefer to add a method declaration if possible.
43+ if ( call ) {
44+ addMethodDeclaration ( context , changes , classDeclarationSourceFile , declaration , token , call , makeStatic , inJs , preferences ) ;
4045 }
4146 else {
42- const typeNode = getTypeNode ( program . getTypeChecker ( ) , classDeclaration , token ) ;
43- addPropertyDeclaration ( changes , classDeclarationSourceFile , classDeclaration , token . text , typeNode , makeStatic ) ;
47+ if ( inJs ) {
48+ addMissingMemberInJs ( changes , classDeclarationSourceFile , declaration , token . text , makeStatic ) ;
49+ }
50+ else {
51+ const typeNode = getTypeNode ( program . getTypeChecker ( ) , declaration , token ) ;
52+ addPropertyDeclaration ( changes , classDeclarationSourceFile , declaration , token . text , typeNode , makeStatic ) ;
53+ }
4454 }
4555 }
4656 } ) ;
4757 } ,
4858 } ) ;
4959
50- interface Info { token : Identifier ; classDeclaration : ClassLikeDeclaration ; makeStatic : boolean ; classDeclarationSourceFile : SourceFile ; inJs : boolean ; call : CallExpression | undefined ; }
60+ interface EnumInfo { token : Identifier ; declaration : EnumDeclaration ; enumDeclarationSourceFile : SourceFile ; }
61+ interface ClassInfo { token : Identifier ; declaration : ClassLikeDeclaration ; makeStatic : boolean ; classDeclarationSourceFile : SourceFile ; inJs : boolean ; call : CallExpression | undefined ; }
62+ type Info = EnumInfo | ClassInfo ;
63+
64+ function isEnumInfo ( info : Info ) : info is EnumInfo {
65+ return isEnumDeclaration ( info . declaration ) ;
66+ }
67+
5168 function getInfo ( tokenSourceFile : SourceFile , tokenPos : number , checker : TypeChecker ) : Info | undefined {
5269 // The identifier of the missing property. eg:
5370 // this.missing = 1;
@@ -62,15 +79,21 @@ namespace ts.codefix {
6279
6380 const leftExpressionType = skipConstraint ( checker . getTypeAtLocation ( parent . expression ) ! ) ;
6481 const { symbol } = leftExpressionType ;
65- const classDeclaration = symbol && symbol . declarations && find ( symbol . declarations , isClassLike ) ;
66- if ( ! classDeclaration ) return undefined ;
82+ if ( ! symbol || ! symbol . declarations ) return undefined ;
6783
68- const makeStatic = ( leftExpressionType as TypeReference ) . target !== checker . getDeclaredTypeOfSymbol ( symbol ) ;
69- const classDeclarationSourceFile = classDeclaration . getSourceFile ( ) ;
70- const inJs = isSourceFileJavaScript ( classDeclarationSourceFile ) ;
71- const call = tryCast ( parent . parent , isCallExpression ) ;
72-
73- return { token, classDeclaration, makeStatic, classDeclarationSourceFile, inJs, call } ;
84+ const classDeclaration = find ( symbol . declarations , isClassLike ) ;
85+ if ( classDeclaration ) {
86+ const makeStatic = ( leftExpressionType as TypeReference ) . target !== checker . getDeclaredTypeOfSymbol ( symbol ) ;
87+ const classDeclarationSourceFile = classDeclaration . getSourceFile ( ) ;
88+ const inJs = isSourceFileJavaScript ( classDeclarationSourceFile ) ;
89+ const call = tryCast ( parent . parent , isCallExpression ) ;
90+ return { token, declaration : classDeclaration , makeStatic, classDeclarationSourceFile, inJs, call } ;
91+ }
92+ const enumDeclaration = find ( symbol . declarations , isEnumDeclaration ) ;
93+ if ( enumDeclaration ) {
94+ return { token, declaration : enumDeclaration , enumDeclarationSourceFile : enumDeclaration . getSourceFile ( ) } ;
95+ }
96+ return undefined ;
7497 }
7598
7699 function getActionsForAddMissingMemberInJavaScriptFile ( context : CodeFixContext , classDeclarationSourceFile : SourceFile , classDeclaration : ClassLikeDeclaration , tokenName : string , makeStatic : boolean ) : CodeFixAction | undefined {
@@ -188,6 +211,16 @@ namespace ts.codefix {
188211 return createCodeFixAction ( fixName , changes , [ makeStatic ? Diagnostics . Declare_static_method_0 : Diagnostics . Declare_method_0 , token . text ] , fixId , Diagnostics . Add_all_missing_members ) ;
189212 }
190213
214+ function getActionForEnumMemberDeclaration (
215+ context : CodeFixContext ,
216+ enumDeclarationSourceFile : SourceFile ,
217+ enumDeclaration : EnumDeclaration ,
218+ token : Identifier
219+ ) : CodeFixAction | undefined {
220+ const changes = textChanges . ChangeTracker . with ( context , t => addEnumMemberDeclaration ( t , context . program . getTypeChecker ( ) , token , enumDeclaration , enumDeclarationSourceFile ) ) ;
221+ return createCodeFixAction ( fixName , changes , [ Diagnostics . Add_missing_enum_member_0 , token . text ] , fixId , Diagnostics . Add_all_missing_enum_members ) ;
222+ }
223+
191224 function addMethodDeclaration (
192225 context : CodeFixContextBase ,
193226 changeTracker : textChanges . ChangeTracker ,
@@ -209,4 +242,32 @@ namespace ts.codefix {
209242 changeTracker . insertNodeAtClassStart ( classDeclarationSourceFile , classDeclaration , methodDeclaration ) ;
210243 }
211244 }
245+
246+ function createEnumMemberFromEnumDeclaration ( checker : TypeChecker , token : Identifier , enumDeclaration : EnumDeclaration ) {
247+ /**
248+ * create initializer only string enum.
249+ * value of initializer is a string literal that equal to name of enum member.
250+ * literal enum or empty enum will not create initializer.
251+ */
252+ const firstMember = firstOrUndefined ( enumDeclaration . members ) ;
253+ let enumMemberInitializer : Expression | undefined ;
254+ if ( firstMember && firstMember . initializer ) {
255+ const memberType = checker . getTypeAtLocation ( firstMember . initializer ) ;
256+ if ( memberType && memberType . flags & TypeFlags . StringLike ) {
257+ enumMemberInitializer = createStringLiteral ( token . text ) ;
258+ }
259+ }
260+ return createEnumMember ( token , enumMemberInitializer ) ;
261+ }
262+
263+ function addEnumMemberDeclaration ( changes : textChanges . ChangeTracker , checker : TypeChecker , token : Identifier , enumDeclaration : EnumDeclaration , file : SourceFile ) {
264+ const enumMember = createEnumMemberFromEnumDeclaration ( checker , token , enumDeclaration ) ;
265+ changes . replaceNode ( file , enumDeclaration , updateEnumDeclaration (
266+ enumDeclaration ,
267+ enumDeclaration . decorators ,
268+ enumDeclaration . modifiers ,
269+ enumDeclaration . name ,
270+ concatenate ( enumDeclaration . members , singleElementArray ( enumMember ) )
271+ ) ) ;
272+ }
212273}
0 commit comments