11/* @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 {
4816 const deletedNodes : Node [ ] = [ ] ;
49- const deletes : ( ( ) => any ) [ ] = [ ] ;
17+ const deletes : ( ( ) => void ) [ ] = [ ] ;
18+ const ctorSymbol = checker . getSymbolAtLocation ( getTokenAtPosition ( sourceFile , position , /*includeJsDocComment*/ false ) ) ;
5019
51- if ( ! ( ctorSymbol . flags & ( SymbolFlags . Function | SymbolFlags . Variable ) ) ) {
20+ if ( ! ctorSymbol || ! ( ctorSymbol . flags & ( SymbolFlags . Function | SymbolFlags . Variable ) ) ) {
21+ // Bad input
5222 return undefined ;
5323 }
5424
5525 const ctorDeclaration = ctorSymbol . valueDeclaration ;
56- const changeTracker = textChanges . ChangeTracker . fromContext ( context ) ;
5726
5827 let precedingNode : Node ;
5928 let newClassDeclaration : ClassDeclaration ;
@@ -81,28 +50,22 @@ namespace ts.refactor.convertFunctionToES6Class {
8150 }
8251
8352 // 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 ) ;
8554 for ( const deleteCallback of deletes ) {
8655 deleteCallback ( ) ;
8756 }
8857
89- return {
90- edits : changeTracker . getChanges ( ) ,
91- renameFilename : undefined ,
92- renameLocation : undefined ,
93- } ;
94-
9558 function deleteNode ( node : Node , inList = false ) {
9659 if ( deletedNodes . some ( n => isNodeDescendantOf ( node , n ) ) ) {
9760 // Parent node has already been deleted; do nothing
9861 return ;
9962 }
10063 deletedNodes . push ( node ) ;
10164 if ( inList ) {
102- deletes . push ( ( ) => changeTracker . deleteNodeInList ( sourceFile , node ) ) ;
65+ deletes . push ( ( ) => changes . deleteNodeInList ( sourceFile , node ) ) ;
10366 }
10467 else {
105- deletes . push ( ( ) => changeTracker . deleteNode ( sourceFile , node ) ) ;
68+ deletes . push ( ( ) => changes . deleteNode ( sourceFile , node ) ) ;
10669 }
10770 }
10871
@@ -165,7 +128,7 @@ namespace ts.refactor.convertFunctionToES6Class {
165128 const fullModifiers = concatenate ( modifiers , getModifierKindFromSource ( functionExpression , SyntaxKind . AsyncKeyword ) ) ;
166129 const method = createMethod ( /*decorators*/ undefined , fullModifiers , /*asteriskToken*/ undefined , memberDeclaration . name , /*questionToken*/ undefined ,
167130 /*typeParameters*/ undefined , functionExpression . parameters , /*type*/ undefined , functionExpression . body ) ;
168- copyComments ( assignmentBinaryExpression , method ) ;
131+ copyComments ( assignmentBinaryExpression , method , sourceFile ) ;
169132 return method ;
170133 }
171134
@@ -185,7 +148,7 @@ namespace ts.refactor.convertFunctionToES6Class {
185148 const fullModifiers = concatenate ( modifiers , getModifierKindFromSource ( arrowFunction , SyntaxKind . AsyncKeyword ) ) ;
186149 const method = createMethod ( /*decorators*/ undefined , fullModifiers , /*asteriskToken*/ undefined , memberDeclaration . name , /*questionToken*/ undefined ,
187150 /*typeParameters*/ undefined , arrowFunction . parameters , /*type*/ undefined , bodyBlock ) ;
188- copyComments ( assignmentBinaryExpression , method ) ;
151+ copyComments ( assignmentBinaryExpression , method , sourceFile ) ;
189152 return method ;
190153 }
191154
@@ -196,29 +159,13 @@ namespace ts.refactor.convertFunctionToES6Class {
196159 }
197160 const prop = createProperty ( /*decorators*/ undefined , modifiers , memberDeclaration . name , /*questionToken*/ undefined ,
198161 /*type*/ undefined , assignmentBinaryExpression . right ) ;
199- copyComments ( assignmentBinaryExpression . parent , prop ) ;
162+ copyComments ( assignmentBinaryExpression . parent , prop , sourceFile ) ;
200163 return prop ;
201164 }
202165 }
203166 }
204167 }
205168
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-
222169 function createClassFromVariableDeclaration ( node : VariableDeclaration ) : ClassDeclaration {
223170 const initializer = node . initializer as FunctionExpression ;
224171 if ( ! initializer || initializer . kind !== SyntaxKind . FunctionExpression ) {
@@ -253,15 +200,25 @@ namespace ts.refactor.convertFunctionToES6Class {
253200 // Don't call copyComments here because we'll already leave them in place
254201 return cls ;
255202 }
203+ }
256204
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+ } ) ;
260219 }
261220
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 ) ;
266223 }
267224}
0 commit comments