@@ -212,8 +212,7 @@ namespace ts.textChanges {
212
212
export class ChangeTracker {
213
213
private readonly changes : Change [ ] = [ ] ;
214
214
private readonly deletedNodesInLists : true [ ] = [ ] ; // Stores ids of nodes in lists that we already deleted. Used to avoid deleting `, ` twice in `a, b`.
215
- // Map from class id to nodes to insert at the start
216
- private readonly nodesInsertedAtClassStarts = createMap < { sourceFile : SourceFile , cls : ClassLikeDeclaration , members : ClassElement [ ] } > ( ) ;
215
+ private readonly classesWithNodesInsertedAtStart = createMap < ClassDeclaration > ( ) ; // Set<ClassDeclaration> implemented as Map<node id, ClassDeclaration>
217
216
218
217
public static fromContext ( context : TextChangesContext ) : ChangeTracker {
219
218
return new ChangeTracker ( getNewLineOrDefaultFromHost ( context . host , context . formatContext . options ) , context . formatContext ) ;
@@ -343,8 +342,7 @@ namespace ts.textChanges {
343
342
}
344
343
345
344
public insertNodeBefore ( sourceFile : SourceFile , before : Node , newNode : Node , blankLineBetween = false ) {
346
- const pos = getAdjustedStartPosition ( sourceFile , before , { } , Position . Start ) ;
347
- return this . replaceRange ( sourceFile , { pos, end : pos } , newNode , this . getOptionsForInsertNodeBefore ( before , blankLineBetween ) ) ;
345
+ this . insertNodeAt ( sourceFile , getAdjustedStartPosition ( sourceFile , before , { } , Position . Start ) , newNode , this . getOptionsForInsertNodeBefore ( before , blankLineBetween ) ) ;
348
346
}
349
347
350
348
public insertModifierBefore ( sourceFile : SourceFile , modifier : SyntaxKind , before : Node ) : void {
@@ -443,21 +441,20 @@ namespace ts.textChanges {
443
441
}
444
442
445
443
public insertNodeAtClassStart ( sourceFile : SourceFile , cls : ClassLikeDeclaration , newElement : ClassElement ) : void {
446
- const firstMember = firstOrUndefined ( cls . members ) ;
447
- if ( ! firstMember ) {
448
- const id = getNodeId ( cls ) . toString ( ) ;
449
- const newMembers = this . nodesInsertedAtClassStarts . get ( id ) ;
450
- if ( newMembers ) {
451
- Debug . assert ( newMembers . sourceFile === sourceFile && newMembers . cls === cls ) ;
452
- newMembers . members . push ( newElement ) ;
444
+ const clsStart = cls . getStart ( sourceFile ) ;
445
+ let prefix = "" ;
446
+ let suffix = this . newLineCharacter ;
447
+ if ( addToSeen ( this . classesWithNodesInsertedAtStart , getNodeId ( cls ) , cls ) ) {
448
+ prefix = this . newLineCharacter ;
449
+ // For `class C {\n}`, don't add the trailing "\n"
450
+ if ( cls . members . length === 0 && ! ( positionsAreOnSameLine as any ) ( ...getClassBraceEnds ( cls , sourceFile ) , sourceFile ) ) { // TODO: GH#4130 remove 'as any'
451
+ suffix = "" ;
453
452
}
454
- else {
455
- this . nodesInsertedAtClassStarts . set ( id , { sourceFile, cls, members : [ newElement ] } ) ;
456
- }
457
- }
458
- else {
459
- this . insertNodeBefore ( sourceFile , firstMember , newElement ) ;
460
453
}
454
+
455
+ const indentation = formatting . SmartIndenter . findFirstNonWhitespaceColumn ( getLineStartPositionForPosition ( clsStart , sourceFile ) , clsStart , sourceFile , this . formatContext . options )
456
+ + this . formatContext . options . indentSize ;
457
+ this . insertNodeAt ( sourceFile , cls . members . pos , newElement , { indentation, prefix, suffix } ) ;
461
458
}
462
459
463
460
public insertNodeAfter ( sourceFile : SourceFile , after : Node , newNode : Node ) : this {
@@ -638,12 +635,14 @@ namespace ts.textChanges {
638
635
return this ;
639
636
}
640
637
641
- private finishInsertNodeAtClassStart ( ) : void {
642
- this . nodesInsertedAtClassStarts . forEach ( ( { sourceFile, cls, members } ) => {
643
- const newCls = cls . kind === SyntaxKind . ClassDeclaration
644
- ? updateClassDeclaration ( cls , cls . decorators , cls . modifiers , cls . name , cls . typeParameters , cls . heritageClauses , members )
645
- : updateClassExpression ( cls , cls . modifiers , cls . name , cls . typeParameters , cls . heritageClauses , members ) ;
646
- this . replaceNode ( sourceFile , cls , newCls ) ;
638
+ private finishClassesWithNodesInsertedAtStart ( ) : void {
639
+ this . classesWithNodesInsertedAtStart . forEach ( cls => {
640
+ const sourceFile = cls . getSourceFile ( ) ;
641
+ const [ openBraceEnd , closeBraceEnd ] = getClassBraceEnds ( cls , sourceFile ) ;
642
+ // For `class C { }` remove the whitespace inside the braces.
643
+ if ( positionsAreOnSameLine ( openBraceEnd , closeBraceEnd , sourceFile ) && openBraceEnd !== closeBraceEnd - 1 ) {
644
+ this . deleteRange ( sourceFile , createTextRange ( openBraceEnd , closeBraceEnd - 1 ) ) ;
645
+ }
647
646
} ) ;
648
647
}
649
648
@@ -654,11 +653,15 @@ namespace ts.textChanges {
654
653
* so we can only call this once and can't get the non-formatted text separately.
655
654
*/
656
655
public getChanges ( validate ?: ValidateNonFormattedText ) : FileTextChanges [ ] {
657
- this . finishInsertNodeAtClassStart ( ) ;
656
+ this . finishClassesWithNodesInsertedAtStart ( ) ;
658
657
return changesToText . getTextChangesFromChanges ( this . changes , this . newLineCharacter , this . formatContext , validate ) ;
659
658
}
660
659
}
661
660
661
+ function getClassBraceEnds ( cls : ClassLikeDeclaration , sourceFile : SourceFile ) : [ number , number ] {
662
+ return [ findChildOfKind ( cls , SyntaxKind . OpenBraceToken , sourceFile ) . end , findChildOfKind ( cls , SyntaxKind . CloseBraceToken , sourceFile ) . end ] ;
663
+ }
664
+
662
665
export type ValidateNonFormattedText = ( node : Node , text : string ) => void ;
663
666
664
667
namespace changesToText {
0 commit comments