@@ -54,7 +54,7 @@ namespace ts {
5454 public jsDoc : JSDoc [ ] ;
5555 public original : Node ;
5656 public transformFlags : TransformFlags ;
57- private _children : Node [ ] ;
57+ private _children : Node [ ] | undefined ;
5858
5959 constructor ( kind : SyntaxKind , pos : number , end : number ) {
6060 this . pos = pos ;
@@ -117,106 +117,17 @@ namespace ts {
117117 return sourceFile . text . substring ( this . getStart ( sourceFile ) , this . getEnd ( ) ) ;
118118 }
119119
120- private addSyntheticNodes ( nodes : Push < Node > , pos : number , end : number ) : number {
121- scanner . setTextPos ( pos ) ;
122- while ( pos < end ) {
123- const token = scanner . scan ( ) ;
124- const textPos = scanner . getTextPos ( ) ;
125- if ( textPos <= end ) {
126- if ( token === SyntaxKind . Identifier ) {
127- Debug . fail ( `Did not expect ${ Debug . showSyntaxKind ( this ) } to have an Identifier in its trivia` ) ;
128- }
129- nodes . push ( createNode ( token , pos , textPos , this ) ) ;
130- }
131- pos = textPos ;
132- if ( token === SyntaxKind . EndOfFileToken ) {
133- break ;
134- }
135- }
136- return pos ;
137- }
138-
139- private createSyntaxList ( nodes : NodeArray < Node > ) : Node {
140- const list = < NodeObject > createNode ( SyntaxKind . SyntaxList , nodes . pos , nodes . end , this ) ;
141- list . _children = [ ] ;
142- let pos = nodes . pos ;
143-
144- for ( const node of nodes ) {
145- if ( pos < node . pos ) {
146- pos = this . addSyntheticNodes ( list . _children , pos , node . pos ) ;
147- }
148- list . _children . push ( node ) ;
149- pos = node . end ;
150- }
151- if ( pos < nodes . end ) {
152- this . addSyntheticNodes ( list . _children , pos , nodes . end ) ;
153- }
154- return list ;
155- }
156-
157- private createChildren ( sourceFile ?: SourceFileLike ) {
158- if ( ! isNodeKind ( this . kind ) ) {
159- this . _children = emptyArray ;
160- return ;
161- }
162-
163- if ( isJSDocCommentContainingNode ( this ) ) {
164- /** Don't add trivia for "tokens" since this is in a comment. */
165- const children : Node [ ] = [ ] ;
166- this . forEachChild ( child => { children . push ( child ) ; } ) ;
167- this . _children = children ;
168- return ;
169- }
170-
171- const children : Node [ ] = [ ] ;
172- scanner . setText ( ( sourceFile || this . getSourceFile ( ) ) . text ) ;
173- let pos = this . pos ;
174- const processNode = ( node : Node ) => {
175- pos = this . addSyntheticNodes ( children , pos , node . pos ) ;
176- children . push ( node ) ;
177- pos = node . end ;
178- } ;
179- const processNodes = ( nodes : NodeArray < Node > ) => {
180- if ( pos < nodes . pos ) {
181- pos = this . addSyntheticNodes ( children , pos , nodes . pos ) ;
182- }
183- children . push ( this . createSyntaxList ( nodes ) ) ;
184- pos = nodes . end ;
185- } ;
186- // jsDocComments need to be the first children
187- if ( this . jsDoc ) {
188- for ( const jsDocComment of this . jsDoc ) {
189- processNode ( jsDocComment ) ;
190- }
191- }
192- // For syntactic classifications, all trivia are classcified together, including jsdoc comments.
193- // For that to work, the jsdoc comments should still be the leading trivia of the first child.
194- // Restoring the scanner position ensures that.
195- pos = this . pos ;
196- forEachChild ( this , processNode , processNodes ) ;
197- if ( pos < this . end ) {
198- this . addSyntheticNodes ( children , pos , this . end ) ;
199- }
200- scanner . setText ( undefined ) ;
201- this . _children = children ;
202- }
203-
204120 public getChildCount ( sourceFile ?: SourceFile ) : number {
205- this . assertHasRealPosition ( ) ;
206- if ( ! this . _children ) this . createChildren ( sourceFile ) ;
207- return this . _children . length ;
121+ return this . getChildren ( sourceFile ) . length ;
208122 }
209123
210124 public getChildAt ( index : number , sourceFile ?: SourceFile ) : Node {
211- this . assertHasRealPosition ( ) ;
212- if ( ! this . _children ) this . createChildren ( sourceFile ) ;
213- return this . _children [ index ] ;
125+ return this . getChildren ( sourceFile ) [ index ] ;
214126 }
215127
216128 public getChildren ( sourceFile ?: SourceFileLike ) : Node [ ] {
217129 this . assertHasRealPosition ( "Node without a real position cannot be scanned and thus has no token nodes - use forEachChild and collect the result if that's fine" ) ;
218- if ( ! this . _children ) this . createChildren ( sourceFile ) ;
219- return this . _children ;
130+ return this . _children || ( this . _children = createChildren ( this , sourceFile ) ) ;
220131 }
221132
222133 public getFirstToken ( sourceFile ?: SourceFile ) : Node {
@@ -249,6 +160,74 @@ namespace ts {
249160 }
250161 }
251162
163+ function createChildren ( node : Node , sourceFile : SourceFileLike | undefined ) : Node [ ] {
164+ if ( ! isNodeKind ( node . kind ) ) {
165+ return emptyArray ;
166+ }
167+
168+ const children : Node [ ] = [ ] ;
169+
170+ if ( isJSDocCommentContainingNode ( node ) ) {
171+ /** Don't add trivia for "tokens" since this is in a comment. */
172+ node . forEachChild ( child => { children . push ( child ) ; } ) ;
173+ return children ;
174+ }
175+
176+ scanner . setText ( ( sourceFile || node . getSourceFile ( ) ) . text ) ;
177+ let pos = node . pos ;
178+ const processNode = ( child : Node ) => {
179+ addSyntheticNodes ( children , pos , child . pos , node ) ;
180+ children . push ( child ) ;
181+ pos = child . end ;
182+ } ;
183+ const processNodes = ( nodes : NodeArray < Node > ) => {
184+ addSyntheticNodes ( children , pos , nodes . pos , node ) ;
185+ children . push ( createSyntaxList ( nodes , node ) ) ;
186+ pos = nodes . end ;
187+ } ;
188+ // jsDocComments need to be the first children
189+ forEach ( ( node as JSDocContainer ) . jsDoc , processNode ) ;
190+ // For syntactic classifications, all trivia are classified together, including jsdoc comments.
191+ // For that to work, the jsdoc comments should still be the leading trivia of the first child.
192+ // Restoring the scanner position ensures that.
193+ pos = node . pos ;
194+ node . forEachChild ( processNode , processNodes ) ;
195+ addSyntheticNodes ( children , pos , node . end , node ) ;
196+ scanner . setText ( undefined ) ;
197+ return children ;
198+ }
199+
200+ function addSyntheticNodes ( nodes : Push < Node > , pos : number , end : number , parent : Node ) : void {
201+ scanner . setTextPos ( pos ) ;
202+ while ( pos < end ) {
203+ const token = scanner . scan ( ) ;
204+ const textPos = scanner . getTextPos ( ) ;
205+ if ( textPos <= end ) {
206+ if ( token === SyntaxKind . Identifier ) {
207+ Debug . fail ( `Did not expect ${ Debug . showSyntaxKind ( parent ) } to have an Identifier in its trivia` ) ;
208+ }
209+ nodes . push ( createNode ( token , pos , textPos , parent ) ) ;
210+ }
211+ pos = textPos ;
212+ if ( token === SyntaxKind . EndOfFileToken ) {
213+ break ;
214+ }
215+ }
216+ }
217+
218+ function createSyntaxList ( nodes : NodeArray < Node > , parent : Node ) : Node {
219+ const list = createNode ( SyntaxKind . SyntaxList , nodes . pos , nodes . end , parent ) as any as SyntaxList ;
220+ list . _children = [ ] ;
221+ let pos = nodes . pos ;
222+ for ( const node of nodes ) {
223+ addSyntheticNodes ( list . _children , pos , node . pos , parent ) ;
224+ list . _children . push ( node ) ;
225+ pos = node . end ;
226+ }
227+ addSyntheticNodes ( list . _children , pos , nodes . end , parent ) ;
228+ return list ;
229+ }
230+
252231 class TokenOrIdentifierObject implements Node {
253232 public kind : SyntaxKind ;
254233 public pos : number ;
0 commit comments