1
+ /// <reference path="sourcemap.ts" />
2
+
3
+ /* @internal */
4
+ namespace ts {
5
+ export interface CommentWriter {
6
+ reset ( ) : void ;
7
+ setSourceFile ( sourceFile : SourceFile ) : void ;
8
+ getLeadingComments ( range : Node , getAdditionalRange ?: ( range : Node ) => Node ) : CommentRange [ ] ;
9
+ getLeadingComments ( range : TextRange ) : CommentRange [ ] ;
10
+ getLeadingCommentsOfPosition ( pos : number ) : CommentRange [ ] ;
11
+ getTrailingComments ( range : Node , getAdditionalRange ?: ( range : Node ) => Node ) : CommentRange [ ] ;
12
+ getTrailingComments ( range : TextRange ) : CommentRange [ ] ;
13
+ getTrailingCommentsOfPosition ( pos : number ) : CommentRange [ ] ;
14
+ emitLeadingComments ( range : TextRange , comments ?: CommentRange [ ] ) : void ;
15
+ emitTrailingComments ( range : TextRange , comments ?: CommentRange [ ] ) : void ;
16
+ emitDetachedComments ( range : TextRange ) : void ;
17
+ }
18
+
19
+ export function createCommentWriter ( host : EmitHost , writer : EmitTextWriter , sourceMap : SourceMapWriter ) : CommentWriter {
20
+ const compilerOptions = host . getCompilerOptions ( ) ;
21
+ const newLine = host . getNewLine ( ) ;
22
+ const { emitPos } = sourceMap ;
23
+
24
+ let currentSourceFile : SourceFile ;
25
+ let currentText : string ;
26
+ let currentLineMap : number [ ] ;
27
+ let detachedCommentsInfo : { nodePos : number , detachedCommentEndPos : number } [ ] ;
28
+
29
+ // This maps start->end for a comment range. See `hasConsumedCommentRange` and
30
+ // `consumeCommentRange` for usage.
31
+ let consumedCommentRanges : number [ ] ;
32
+ let leadingCommentRangePositions : boolean [ ] ;
33
+ let trailingCommentRangePositions : boolean [ ] ;
34
+
35
+ return compilerOptions . removeComments
36
+ ? createCommentRemovingWriter ( )
37
+ : createCommentPreservingWriter ( ) ;
38
+
39
+ function createCommentRemovingWriter ( ) : CommentWriter {
40
+ return {
41
+ reset,
42
+ setSourceFile,
43
+ getLeadingComments ( range : TextRange , getAdditionalRange ?: ( range : TextRange ) => TextRange ) : CommentRange [ ] { return undefined ; } ,
44
+ getLeadingCommentsOfPosition ( pos : number ) : CommentRange [ ] { return undefined ; } ,
45
+ getTrailingComments ( range : TextRange , getAdditionalRange ?: ( range : TextRange ) => TextRange ) : CommentRange [ ] { return undefined ; } ,
46
+ getTrailingCommentsOfPosition ( pos : number ) : CommentRange [ ] { return undefined ; } ,
47
+ emitLeadingComments ( range : TextRange , comments ?: CommentRange [ ] ) : void { } ,
48
+ emitTrailingComments ( range : TextRange , comments ?: CommentRange [ ] ) : void { } ,
49
+ emitDetachedComments,
50
+ } ;
51
+
52
+ function emitDetachedComments ( node : TextRange ) : void {
53
+ emitDetachedCommentsAndUpdateCommentsInfo ( node , /*removeComments*/ true ) ;
54
+ }
55
+ }
56
+
57
+ function createCommentPreservingWriter ( ) : CommentWriter {
58
+ const noComments : CommentRange [ ] = [ ] ;
59
+ return {
60
+ reset,
61
+ setSourceFile,
62
+ getLeadingComments,
63
+ getLeadingCommentsOfPosition,
64
+ getTrailingComments,
65
+ getTrailingCommentsOfPosition,
66
+ emitLeadingComments,
67
+ emitTrailingComments,
68
+ emitDetachedComments,
69
+ } ;
70
+
71
+ function getLeadingComments ( range : TextRange | Node , getAdditionalRange ?: ( range : Node ) => Node ) {
72
+ let comments = getLeadingCommentsOfPosition ( range . pos ) ;
73
+ if ( getAdditionalRange ) {
74
+ let additionalRange = getAdditionalRange ( < Node > range ) ;
75
+ while ( additionalRange ) {
76
+ comments = concatenate (
77
+ getLeadingCommentsOfPosition ( additionalRange . pos ) ,
78
+ comments
79
+ ) ;
80
+
81
+ additionalRange = getAdditionalRange ( additionalRange ) ;
82
+ }
83
+ }
84
+
85
+ return comments ;
86
+ }
87
+
88
+ function getTrailingComments ( range : TextRange | Node , getAdditionalRange ?: ( range : Node ) => Node ) {
89
+ let comments = getTrailingCommentsOfPosition ( range . end ) ;
90
+ if ( getAdditionalRange ) {
91
+ let additionalRange = getAdditionalRange ( < Node > range ) ;
92
+ while ( additionalRange ) {
93
+ comments = concatenate (
94
+ comments ,
95
+ getTrailingCommentsOfPosition ( additionalRange . end )
96
+ ) ;
97
+
98
+ additionalRange = getAdditionalRange ( additionalRange ) ;
99
+ }
100
+ }
101
+
102
+ return comments ;
103
+ }
104
+
105
+ function getLeadingCommentsOfPosition ( pos : number ) {
106
+ if ( positionIsSynthesized ( pos ) || leadingCommentRangePositions [ pos ] ) {
107
+ return undefined ;
108
+ }
109
+
110
+ leadingCommentRangePositions [ pos ] = true ;
111
+ const comments = hasDetachedComments ( pos )
112
+ ? getLeadingCommentsWithoutDetachedComments ( )
113
+ : getLeadingCommentRanges ( currentText , pos ) ;
114
+ return consumeCommentRanges ( comments ) ;
115
+ }
116
+
117
+ function getTrailingCommentsOfPosition ( pos : number ) {
118
+ if ( positionIsSynthesized ( pos ) || trailingCommentRangePositions [ pos ] ) {
119
+ return undefined ;
120
+ }
121
+
122
+ trailingCommentRangePositions [ pos ] = true ;
123
+ const comments = getTrailingCommentRanges ( currentText , pos ) ;
124
+ return consumeCommentRanges ( comments ) ;
125
+ }
126
+
127
+ function emitLeadingComments ( range : TextRange , comments = getLeadingComments ( range ) ) {
128
+ emitNewLineBeforeLeadingComments ( currentLineMap , writer , range , comments ) ;
129
+
130
+ // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
131
+ emitComments ( currentText , currentLineMap , writer , comments , /*leadingSeparator*/ false , /*trailingSeparator*/ true , newLine , writeComment ) ;
132
+ }
133
+
134
+ function emitTrailingComments ( range : TextRange , comments = getTrailingComments ( range ) ) {
135
+ // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
136
+ emitComments ( currentText , currentLineMap , writer , comments , /*leadingSeparator*/ true , /*trailingSeparator*/ false , newLine , writeComment ) ;
137
+ }
138
+
139
+ function emitDetachedComments ( range : TextRange ) {
140
+ emitDetachedCommentsAndUpdateCommentsInfo ( range , /*removeComments*/ false ) ;
141
+ }
142
+
143
+ function hasConsumedCommentRange ( comment : CommentRange ) {
144
+ return comment . end === consumedCommentRanges [ comment . pos ] ;
145
+ }
146
+
147
+ function consumeCommentRange ( comment : CommentRange ) {
148
+ if ( ! hasConsumedCommentRange ( comment ) ) {
149
+ consumedCommentRanges [ comment . pos ] = comment . end ;
150
+ return true ;
151
+ }
152
+
153
+ return false ;
154
+ }
155
+
156
+ function consumeCommentRanges ( comments : CommentRange [ ] ) {
157
+ let consumed : CommentRange [ ] ;
158
+ if ( comments ) {
159
+ let commentsSkipped = 0 ;
160
+ let commentsConsumed = 0 ;
161
+ for ( let i = 0 ; i < comments . length ; i ++ ) {
162
+ const comment = comments [ i ] ;
163
+ if ( consumeCommentRange ( comment ) ) {
164
+ commentsConsumed ++ ;
165
+ if ( commentsSkipped !== 0 ) {
166
+ if ( consumed === undefined ) {
167
+ consumed = [ comment ] ;
168
+ }
169
+ else {
170
+ consumed . push ( comment ) ;
171
+ }
172
+ }
173
+ }
174
+ else {
175
+ commentsSkipped ++ ;
176
+ if ( commentsConsumed !== 0 && consumed === undefined ) {
177
+ consumed = comments . slice ( 0 , i ) ;
178
+ }
179
+ }
180
+ }
181
+
182
+ if ( commentsConsumed ) {
183
+ return consumed || comments ;
184
+ }
185
+ }
186
+
187
+ return noComments ;
188
+ }
189
+ }
190
+
191
+ function reset ( ) {
192
+ currentSourceFile = undefined ;
193
+ currentText = undefined ;
194
+ currentLineMap = undefined ;
195
+ detachedCommentsInfo = undefined ;
196
+ consumedCommentRanges = undefined ;
197
+ trailingCommentRangePositions = undefined ;
198
+ leadingCommentRangePositions = undefined ;
199
+ }
200
+
201
+ function setSourceFile ( sourceFile : SourceFile ) {
202
+ currentSourceFile = sourceFile ;
203
+ currentText = sourceFile . text ;
204
+ currentLineMap = getLineStarts ( sourceFile ) ;
205
+ detachedCommentsInfo = undefined ;
206
+ consumedCommentRanges = [ ] ;
207
+ leadingCommentRangePositions = [ ] ;
208
+ trailingCommentRangePositions = [ ] ;
209
+ }
210
+
211
+ function hasDetachedComments ( pos : number ) {
212
+ return detachedCommentsInfo !== undefined && lastOrUndefined ( detachedCommentsInfo ) . nodePos === pos ;
213
+ }
214
+
215
+ function getLeadingCommentsWithoutDetachedComments ( ) {
216
+ // get the leading comments from detachedPos
217
+ const pos = lastOrUndefined ( detachedCommentsInfo ) . detachedCommentEndPos ;
218
+ const leadingComments = getLeadingCommentRanges ( currentText , pos ) ;
219
+ if ( detachedCommentsInfo . length - 1 ) {
220
+ detachedCommentsInfo . pop ( ) ;
221
+ }
222
+ else {
223
+ detachedCommentsInfo = undefined ;
224
+ }
225
+
226
+ return leadingComments ;
227
+ }
228
+
229
+ function emitDetachedCommentsAndUpdateCommentsInfo ( node : TextRange , removeComments : boolean ) {
230
+ const currentDetachedCommentInfo = emitDetachedComments ( currentText , currentLineMap , writer , writeComment , node , newLine , removeComments ) ;
231
+
232
+ if ( currentDetachedCommentInfo ) {
233
+ if ( detachedCommentsInfo ) {
234
+ detachedCommentsInfo . push ( currentDetachedCommentInfo ) ;
235
+ }
236
+ else {
237
+ detachedCommentsInfo = [ currentDetachedCommentInfo ] ;
238
+ }
239
+ }
240
+ }
241
+
242
+ function writeComment ( text : string , lineMap : number [ ] , writer : EmitTextWriter , comment : CommentRange , newLine : string ) {
243
+ emitPos ( comment . pos ) ;
244
+ writeCommentRange ( text , lineMap , writer , comment , newLine ) ;
245
+ emitPos ( comment . end ) ;
246
+ }
247
+ }
248
+ }
0 commit comments