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