@@ -208,8 +208,7 @@ namespace ts.textChanges {
208
208
export class ChangeTracker {
209
209
private readonly changes : Change [ ] = [ ] ;
210
210
private readonly deletedNodesInLists : true [ ] = [ ] ; // Stores ids of nodes in lists that we already deleted. Used to avoid deleting `, ` twice in `a, b`.
211
- // Map from class id to nodes to insert at the start
212
- private readonly nodesInsertedAtClassStarts = createMap < { sourceFile : SourceFile , cls : ClassLikeDeclaration , members : ClassElement [ ] } > ( ) ;
211
+ private readonly classesWithNodesInsertedAtStart = createMap < ClassDeclaration > ( ) ; // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
213
212
214
213
public static fromContext ( context : TextChangesContext ) : ChangeTracker {
215
214
return new ChangeTracker ( getNewLineOrDefaultFromHost ( context . host , context . formatContext . options ) , context . formatContext ) ;
@@ -339,8 +338,7 @@ namespace ts.textChanges {
339
338
}
340
339
341
340
public insertNodeBefore ( sourceFile : SourceFile , before : Node , newNode : Node , blankLineBetween = false ) {
342
- const pos = getAdjustedStartPosition ( sourceFile , before , { } , Position . Start ) ;
343
- return this . replaceRange ( sourceFile , { pos, end : pos } , newNode , this . getOptionsForInsertNodeBefore ( before , blankLineBetween ) ) ;
341
+ this . insertNodeAt ( sourceFile , getAdjustedStartPosition ( sourceFile , before , { } , Position . Start ) , newNode , this . getOptionsForInsertNodeBefore ( before , blankLineBetween ) ) ;
344
342
}
345
343
346
344
public insertModifierBefore ( sourceFile : SourceFile , modifier : SyntaxKind , before : Node ) : void {
@@ -435,21 +433,20 @@ namespace ts.textChanges {
435
433
}
436
434
437
435
public insertNodeAtClassStart ( sourceFile : SourceFile , cls : ClassLikeDeclaration , newElement : ClassElement ) : void {
438
- const firstMember = firstOrUndefined ( cls . members ) ;
439
- if ( ! firstMember ) {
440
- const id = getNodeId ( cls ) . toString ( ) ;
441
- const newMembers = this . nodesInsertedAtClassStarts . get ( id ) ;
442
- if ( newMembers ) {
443
- Debug . assert ( newMembers . sourceFile === sourceFile && newMembers . cls === cls ) ;
444
- newMembers . members . push ( newElement ) ;
436
+ const clsStart = cls . getStart ( sourceFile ) ;
437
+ let prefix = "" ;
438
+ let suffix = this . newLineCharacter ;
439
+ if ( addToSeen ( this . classesWithNodesInsertedAtStart , getNodeId ( cls ) , cls ) ) {
440
+ prefix = this . newLineCharacter ;
441
+ // For `class C {\n}`, don't add the trailing "\n"
442
+ if ( cls . members . length === 0 && ! ( positionsAreOnSameLine as any ) ( ...getClassBraceEnds ( cls , sourceFile ) , sourceFile ) ) { // TODO: GH#4130 remove 'as any'
443
+ suffix = "" ;
445
444
}
446
- else {
447
- this . nodesInsertedAtClassStarts . set ( id , { sourceFile, cls, members : [ newElement ] } ) ;
448
- }
449
- }
450
- else {
451
- this . insertNodeBefore ( sourceFile , firstMember , newElement ) ;
452
445
}
446
+
447
+ const indentation = formatting . SmartIndenter . findFirstNonWhitespaceColumn ( getLineStartPositionForPosition ( clsStart , sourceFile ) , clsStart , sourceFile , this . formatContext . options )
448
+ + this . formatContext . options . indentSize ;
449
+ this . insertNodeAt ( sourceFile , cls . members . pos , newElement , { indentation, prefix, suffix } ) ;
453
450
}
454
451
455
452
public insertNodeAfter ( sourceFile : SourceFile , after : Node , newNode : Node ) : this {
@@ -601,12 +598,14 @@ namespace ts.textChanges {
601
598
return this ;
602
599
}
603
600
604
- private finishInsertNodeAtClassStart ( ) : void {
605
- this . nodesInsertedAtClassStarts . forEach ( ( { sourceFile, cls, members } ) => {
606
- const newCls = cls . kind === SyntaxKind . ClassDeclaration
607
- ? updateClassDeclaration ( cls , cls . decorators , cls . modifiers , cls . name , cls . typeParameters , cls . heritageClauses , members )
608
- : updateClassExpression ( cls , cls . modifiers , cls . name , cls . typeParameters , cls . heritageClauses , members ) ;
609
- this . replaceNode ( sourceFile , cls , newCls ) ;
601
+ private finishClassesWithNodesInsertedAtStart ( ) : void {
602
+ this . classesWithNodesInsertedAtStart . forEach ( cls => {
603
+ const sourceFile = cls . getSourceFile ( ) ;
604
+ const [ openBraceEnd , closeBraceEnd ] = getClassBraceEnds ( cls , sourceFile ) ;
605
+ // For `class C { }` remove the whitespace inside the braces.
606
+ if ( positionsAreOnSameLine ( openBraceEnd , closeBraceEnd , sourceFile ) && openBraceEnd !== closeBraceEnd - 1 ) {
607
+ this . deleteRange ( sourceFile , createTextRange ( openBraceEnd , closeBraceEnd - 1 ) ) ;
608
+ }
610
609
} ) ;
611
610
}
612
611
@@ -617,11 +616,15 @@ namespace ts.textChanges {
617
616
* so we can only call this once and can't get the non-formatted text separately.
618
617
*/
619
618
public getChanges ( validate ?: ValidateNonFormattedText ) : FileTextChanges [ ] {
620
- this . finishInsertNodeAtClassStart ( ) ;
619
+ this . finishClassesWithNodesInsertedAtStart ( ) ;
621
620
return changesToText . getTextChangesFromChanges ( this . changes , this . newLineCharacter , this . formatContext , validate ) ;
622
621
}
623
622
}
624
623
624
+ function getClassBraceEnds ( cls : ClassLikeDeclaration , sourceFile : SourceFile ) : [ number , number ] {
625
+ return [ findChildOfKind ( cls , SyntaxKind . OpenBraceToken , sourceFile ) . end , findChildOfKind ( cls , SyntaxKind . CloseBraceToken , sourceFile ) . end ] ;
626
+ }
627
+
625
628
export type ValidateNonFormattedText = ( node : Node , text : string ) => void ;
626
629
627
630
namespace changesToText {
0 commit comments