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