1
1
/* @internal */
2
-
3
- namespace ts . refactor . convertFunctionToES6Class {
4
- const refactorName = "Convert to ES2015 class" ;
5
- const actionName = "convert" ;
6
- const description = Diagnostics . Convert_function_to_an_ES2015_class . message ;
7
- registerRefactor ( refactorName , { getEditsForAction, getAvailableActions } ) ;
8
-
9
- function getAvailableActions ( context : RefactorContext ) : ApplicableRefactorInfo [ ] | undefined {
10
- if ( ! isInJavaScriptFile ( context . file ) ) {
11
- return undefined ;
12
- }
13
-
14
- let symbol = getConstructorSymbol ( context ) ;
15
- if ( ! symbol ) {
16
- return undefined ;
17
- }
18
-
19
- if ( isDeclarationOfFunctionOrClassExpression ( symbol ) ) {
20
- symbol = ( symbol . valueDeclaration as VariableDeclaration ) . initializer . symbol ;
21
- }
22
-
23
- if ( ( symbol . flags & SymbolFlags . Function ) && symbol . members && ( symbol . members . size > 0 ) ) {
24
- return [
25
- {
26
- name : refactorName ,
27
- description,
28
- actions : [
29
- {
30
- description,
31
- name : actionName
32
- }
33
- ]
34
- }
35
- ] ;
36
- }
37
- }
38
-
39
- function getEditsForAction ( context : RefactorContext , action : string ) : RefactorEditInfo | undefined {
40
- // Somehow wrong action got invoked?
41
- if ( actionName !== action ) {
42
- return undefined ;
43
- }
44
-
45
- const { file : sourceFile } = context ;
46
- const ctorSymbol = getConstructorSymbol ( context ) ;
47
-
2
+ namespace ts . codefix {
3
+ const fixId = "convertFunctionToEs6Class" ;
4
+ const errorCodes = [ Diagnostics . This_constructor_function_may_be_converted_to_a_class_declaration . code ] ;
5
+ registerCodeFix ( {
6
+ errorCodes,
7
+ getCodeActions ( context : CodeFixContext ) {
8
+ const changes = textChanges . ChangeTracker . with ( context , t => doChange ( t , context . sourceFile , context . span . start , context . program . getTypeChecker ( ) ) ) ;
9
+ return [ { description : getLocaleSpecificMessage ( Diagnostics . Convert_function_to_an_ES2015_class ) , changes, fixId } ] ;
10
+ } ,
11
+ fixIds : [ fixId ] ,
12
+ getAllCodeActions : context => codeFixAll ( context , errorCodes , ( changes , err ) => doChange ( changes , err . file ! , err . start , context . program . getTypeChecker ( ) ) ) ,
13
+ } ) ;
14
+
15
+ function doChange ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , position : number , checker : TypeChecker ) : void {
48
16
const deletedNodes : Node [ ] = [ ] ;
49
- const deletes : ( ( ) => any ) [ ] = [ ] ;
17
+ const deletes : ( ( ) => void ) [ ] = [ ] ;
18
+ const ctorSymbol = checker . getSymbolAtLocation ( getTokenAtPosition ( sourceFile , position , /*includeJsDocComment*/ false ) ) ;
50
19
51
- if ( ! ( ctorSymbol . flags & ( SymbolFlags . Function | SymbolFlags . Variable ) ) ) {
20
+ if ( ! ctorSymbol || ! ( ctorSymbol . flags & ( SymbolFlags . Function | SymbolFlags . Variable ) ) ) {
21
+ // Bad input
52
22
return undefined ;
53
23
}
54
24
55
25
const ctorDeclaration = ctorSymbol . valueDeclaration ;
56
- const changeTracker = textChanges . ChangeTracker . fromContext ( context ) ;
57
26
58
27
let precedingNode : Node ;
59
28
let newClassDeclaration : ClassDeclaration ;
@@ -81,28 +50,22 @@ namespace ts.refactor.convertFunctionToES6Class {
81
50
}
82
51
83
52
// Because the preceding node could be touched, we need to insert nodes before delete nodes.
84
- changeTracker . insertNodeAfter ( sourceFile , precedingNode , newClassDeclaration ) ;
53
+ changes . insertNodeAfter ( sourceFile , precedingNode , newClassDeclaration ) ;
85
54
for ( const deleteCallback of deletes ) {
86
55
deleteCallback ( ) ;
87
56
}
88
57
89
- return {
90
- edits : changeTracker . getChanges ( ) ,
91
- renameFilename : undefined ,
92
- renameLocation : undefined ,
93
- } ;
94
-
95
58
function deleteNode ( node : Node , inList = false ) {
96
59
if ( deletedNodes . some ( n => isNodeDescendantOf ( node , n ) ) ) {
97
60
// Parent node has already been deleted; do nothing
98
61
return ;
99
62
}
100
63
deletedNodes . push ( node ) ;
101
64
if ( inList ) {
102
- deletes . push ( ( ) => changeTracker . deleteNodeInList ( sourceFile , node ) ) ;
65
+ deletes . push ( ( ) => changes . deleteNodeInList ( sourceFile , node ) ) ;
103
66
}
104
67
else {
105
- deletes . push ( ( ) => changeTracker . deleteNode ( sourceFile , node ) ) ;
68
+ deletes . push ( ( ) => changes . deleteNode ( sourceFile , node ) ) ;
106
69
}
107
70
}
108
71
@@ -165,7 +128,7 @@ namespace ts.refactor.convertFunctionToES6Class {
165
128
const fullModifiers = concatenate ( modifiers , getModifierKindFromSource ( functionExpression , SyntaxKind . AsyncKeyword ) ) ;
166
129
const method = createMethod ( /*decorators*/ undefined , fullModifiers , /*asteriskToken*/ undefined , memberDeclaration . name , /*questionToken*/ undefined ,
167
130
/*typeParameters*/ undefined , functionExpression . parameters , /*type*/ undefined , functionExpression . body ) ;
168
- copyComments ( assignmentBinaryExpression , method ) ;
131
+ copyComments ( assignmentBinaryExpression , method , sourceFile ) ;
169
132
return method ;
170
133
}
171
134
@@ -185,7 +148,7 @@ namespace ts.refactor.convertFunctionToES6Class {
185
148
const fullModifiers = concatenate ( modifiers , getModifierKindFromSource ( arrowFunction , SyntaxKind . AsyncKeyword ) ) ;
186
149
const method = createMethod ( /*decorators*/ undefined , fullModifiers , /*asteriskToken*/ undefined , memberDeclaration . name , /*questionToken*/ undefined ,
187
150
/*typeParameters*/ undefined , arrowFunction . parameters , /*type*/ undefined , bodyBlock ) ;
188
- copyComments ( assignmentBinaryExpression , method ) ;
151
+ copyComments ( assignmentBinaryExpression , method , sourceFile ) ;
189
152
return method ;
190
153
}
191
154
@@ -196,29 +159,13 @@ namespace ts.refactor.convertFunctionToES6Class {
196
159
}
197
160
const prop = createProperty ( /*decorators*/ undefined , modifiers , memberDeclaration . name , /*questionToken*/ undefined ,
198
161
/*type*/ undefined , assignmentBinaryExpression . right ) ;
199
- copyComments ( assignmentBinaryExpression . parent , prop ) ;
162
+ copyComments ( assignmentBinaryExpression . parent , prop , sourceFile ) ;
200
163
return prop ;
201
164
}
202
165
}
203
166
}
204
167
}
205
168
206
- function copyComments ( sourceNode : Node , targetNode : Node ) {
207
- forEachLeadingCommentRange ( sourceFile . text , sourceNode . pos , ( pos , end , kind , htnl ) => {
208
- if ( kind === SyntaxKind . MultiLineCommentTrivia ) {
209
- // Remove leading /*
210
- pos += 2 ;
211
- // Remove trailing */
212
- end -= 2 ;
213
- }
214
- else {
215
- // Remove leading //
216
- pos += 2 ;
217
- }
218
- addSyntheticLeadingComment ( targetNode , kind , sourceFile . text . slice ( pos , end ) , htnl ) ;
219
- } ) ;
220
- }
221
-
222
169
function createClassFromVariableDeclaration ( node : VariableDeclaration ) : ClassDeclaration {
223
170
const initializer = node . initializer as FunctionExpression ;
224
171
if ( ! initializer || initializer . kind !== SyntaxKind . FunctionExpression ) {
@@ -253,15 +200,25 @@ namespace ts.refactor.convertFunctionToES6Class {
253
200
// Don't call copyComments here because we'll already leave them in place
254
201
return cls ;
255
202
}
203
+ }
256
204
257
- function getModifierKindFromSource ( source : Node , kind : SyntaxKind ) {
258
- return filter ( source . modifiers , modifier => modifier . kind === kind ) ;
259
- }
205
+ function copyComments ( sourceNode : Node , targetNode : Node , sourceFile : SourceFile ) {
206
+ forEachLeadingCommentRange ( sourceFile . text , sourceNode . pos , ( pos , end , kind , htnl ) => {
207
+ if ( kind === SyntaxKind . MultiLineCommentTrivia ) {
208
+ // Remove leading /*
209
+ pos += 2 ;
210
+ // Remove trailing */
211
+ end -= 2 ;
212
+ }
213
+ else {
214
+ // Remove leading //
215
+ pos += 2 ;
216
+ }
217
+ addSyntheticLeadingComment ( targetNode , kind , sourceFile . text . slice ( pos , end ) , htnl ) ;
218
+ } ) ;
260
219
}
261
220
262
- function getConstructorSymbol ( { startPosition, file, program } : RefactorContext ) : Symbol {
263
- const checker = program . getTypeChecker ( ) ;
264
- const token = getTokenAtPosition ( file , startPosition , /*includeJsDocComment*/ false ) ;
265
- return checker . getSymbolAtLocation ( token ) ;
221
+ function getModifierKindFromSource ( source : Node , kind : SyntaxKind ) : ReadonlyArray < Modifier > {
222
+ return filter ( source . modifiers , modifier => modifier . kind === kind ) ;
266
223
}
267
224
}
0 commit comments