@@ -23,9 +23,9 @@ namespace ts {
2323 let currentText : string ;
2424 let currentLineMap : number [ ] ;
2525 let detachedCommentsInfo : { nodePos : number , detachedCommentEndPos : number } [ ] ;
26-
27- // Tracks comment ranges that have already been consumed.
28- let consumedCommentRanges : Map < boolean > ;
26+ let hasWrittenComment = false ;
27+ let hasLastComment : boolean ;
28+ let lastCommentEnd : number ;
2929
3030 return {
3131 reset,
@@ -122,7 +122,7 @@ namespace ts {
122122 const skipTrailingComments = end < 0 || ( emitFlags & NodeEmitFlags . NoTrailingComments ) !== 0 || compilerOptions . removeComments ;
123123
124124 if ( ! skipLeadingComments ) {
125- emitDetachedCommentsAndUpdateCommentsInfo ( detachedRange , compilerOptions . removeComments ) ;
125+ emitDetachedCommentsAndUpdateCommentsInfo ( detachedRange ) ;
126126 }
127127
128128 if ( extendedDiagnostics ) {
@@ -144,11 +144,12 @@ namespace ts {
144144 }
145145
146146 function emitLeadingComments ( pos : number , isEmittedNode : boolean ) {
147- let leadingComments : CommentRange [ ] ;
147+ hasWrittenComment = false ;
148+
148149 if ( isEmittedNode ) {
149- leadingComments = getLeadingCommentsToEmit ( pos ) ;
150+ forEachLeadingCommentToEmit ( pos , emitLeadingComment ) ;
150151 }
151- else {
152+ else if ( pos === 0 ) {
152153 // If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
153154 // unless it is a triple slash comment at the top of the file.
154155 // For Example:
@@ -157,22 +158,52 @@ namespace ts {
157158 // /// <reference-path ...>
158159 // interface F {}
159160 // The first /// will NOT be removed while the second one will be removed even though both node will not be emitted
160- if ( pos === 0 ) {
161- leadingComments = filter ( getLeadingCommentsToEmit ( pos ) , isTripleSlashComment ) ;
162- }
161+ forEachLeadingCommentToEmit ( pos , emitTripleSlashLeadingComment ) ;
163162 }
163+ }
164164
165- emitNewLineBeforeLeadingCommentsOfPosition ( currentLineMap , writer , pos , leadingComments ) ;
165+ function emitTripleSlashLeadingComment ( commentPos : number , commentEnd : number , kind : SyntaxKind , hasTrailingNewLine : boolean , rangePos : number ) {
166+ if ( isTripleSlashComment ( commentPos , commentEnd ) ) {
167+ emitLeadingComment ( commentPos , commentEnd , kind , hasTrailingNewLine , rangePos ) ;
168+ }
169+ }
170+
171+ function emitLeadingComment ( commentPos : number , commentEnd : number , kind : SyntaxKind , hasTrailingNewLine : boolean , rangePos : number ) {
172+ if ( ! hasWrittenComment ) {
173+ emitNewLineBeforeLeadingCommentOfPosition ( currentLineMap , writer , rangePos , commentPos ) ;
174+ hasWrittenComment = true ;
175+ }
166176
167177 // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
168- emitComments ( currentText , currentLineMap , writer , leadingComments , /*leadingSeparator*/ false , /*trailingSeparator*/ true , newLine , writeComment ) ;
178+ emitPos ( commentPos ) ;
179+ writeCommentRange ( currentText , currentLineMap , writer , commentPos , commentEnd , newLine ) ;
180+ emitPos ( commentEnd ) ;
181+
182+ if ( hasTrailingNewLine ) {
183+ writer . writeLine ( ) ;
184+ }
185+ else {
186+ writer . write ( " " ) ;
187+ }
169188 }
170189
171190 function emitTrailingComments ( pos : number ) {
172- const trailingComments = getTrailingCommentsToEmit ( pos ) ;
191+ forEachTrailingCommentToEmit ( pos , emitTrailingComment ) ;
192+ }
193+
194+ function emitTrailingComment ( commentPos : number , commentEnd : number , kind : SyntaxKind , hasTrailingNewLine : boolean ) {
195+ // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/
196+ if ( ! writer . isAtStartOfLine ( ) ) {
197+ writer . write ( " " ) ;
198+ }
199+
200+ emitPos ( commentPos ) ;
201+ writeCommentRange ( currentText , currentLineMap , writer , commentPos , commentEnd , newLine ) ;
202+ emitPos ( commentEnd ) ;
173203
174- // trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
175- emitComments ( currentText , currentLineMap , writer , trailingComments , /*leadingSeparator*/ true , /*trailingSeparator*/ false , newLine , writeComment ) ;
204+ if ( hasTrailingNewLine ) {
205+ writer . writeLine ( ) ;
206+ }
176207 }
177208
178209 function emitTrailingCommentsOfPosition ( pos : number ) {
@@ -185,88 +216,80 @@ namespace ts {
185216 commentStart = performance . mark ( ) ;
186217 }
187218
188- const trailingComments = getTrailingCommentsToEmit ( pos ) ;
189-
190- // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
191- emitComments ( currentText , currentLineMap , writer , trailingComments , /*leadingSeparator*/ false , /*trailingSeparator*/ true , newLine , writeComment ) ;
219+ forEachTrailingCommentToEmit ( pos , emitTrailingCommentOfPosition ) ;
192220
193221 if ( extendedDiagnostics ) {
194222 performance . measure ( "commentTime" , commentStart ) ;
195223 }
196224 }
197225
198- function getLeadingCommentsToEmit ( pos : number ) {
226+ function emitTrailingCommentOfPosition ( commentPos : number , commentEnd : number , kind : SyntaxKind , hasTrailingNewLine : boolean ) {
227+ // trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
228+
229+ emitPos ( commentPos ) ;
230+ writeCommentRange ( currentText , currentLineMap , writer , commentPos , commentEnd , newLine ) ;
231+ emitPos ( commentEnd ) ;
232+
233+ if ( hasTrailingNewLine ) {
234+ writer . writeLine ( ) ;
235+ }
236+ else {
237+ writer . write ( " " ) ;
238+ }
239+ }
240+
241+ function forEachLeadingCommentToEmit ( pos : number , cb : ( commentPos : number , commentEnd : number , kind : SyntaxKind , hasTrailingNewLine : boolean , rangePos : number ) => void ) {
199242 // Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments
200243 if ( containerPos === - 1 || pos !== containerPos ) {
201- return hasDetachedComments ( pos )
202- ? getLeadingCommentsWithoutDetachedComments ( )
203- : getLeadingCommentRanges ( currentText , pos ) ;
244+ if ( hasDetachedComments ( pos ) ) {
245+ forEachLeadingCommentWithoutDetachedComments ( cb ) ;
246+ }
247+ else {
248+ forEachLeadingCommentRange ( currentText , pos , cb , /*state*/ pos ) ;
249+ }
204250 }
205251 }
206252
207- function getTrailingCommentsToEmit ( end : number ) {
253+ function forEachTrailingCommentToEmit ( end : number , cb : ( commentPos : number , commentEnd : number , kind : SyntaxKind , hasTrailingNewLine : boolean ) => void ) {
208254 // Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments
209255 if ( containerEnd === - 1 || ( end !== containerEnd && end !== declarationListContainerEnd ) ) {
210- return getTrailingCommentRanges ( currentText , end ) ;
256+ forEachTrailingCommentRange ( currentText , end , cb ) ;
211257 }
212258 }
213259
214- /**
215- * Determine if the given comment is a triple-slash
216- *
217- * @return true if the comment is a triple-slash comment else false
218- **/
219- function isTripleSlashComment ( comment : CommentRange ) {
220- // Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
221- // so that we don't end up computing comment string and doing match for all // comments
222- if ( currentText . charCodeAt ( comment . pos + 1 ) === CharacterCodes . slash &&
223- comment . pos + 2 < comment . end &&
224- currentText . charCodeAt ( comment . pos + 2 ) === CharacterCodes . slash ) {
225- const textSubStr = currentText . substring ( comment . pos , comment . end ) ;
226- return textSubStr . match ( fullTripleSlashReferencePathRegEx ) ||
227- textSubStr . match ( fullTripleSlashAMDReferencePathRegEx ) ?
228- true : false ;
229- }
230- return false ;
231- }
232-
233260 function reset ( ) {
234261 currentSourceFile = undefined ;
235262 currentText = undefined ;
236263 currentLineMap = undefined ;
237264 detachedCommentsInfo = undefined ;
238- consumedCommentRanges = undefined ;
239265 }
240266
241267 function setSourceFile ( sourceFile : SourceFile ) {
242268 currentSourceFile = sourceFile ;
243269 currentText = currentSourceFile . text ;
244270 currentLineMap = getLineStarts ( currentSourceFile ) ;
245271 detachedCommentsInfo = undefined ;
246- consumedCommentRanges = { } ;
247272 }
248273
249274 function hasDetachedComments ( pos : number ) {
250275 return detachedCommentsInfo !== undefined && lastOrUndefined ( detachedCommentsInfo ) . nodePos === pos ;
251276 }
252277
253- function getLeadingCommentsWithoutDetachedComments ( ) {
278+ function forEachLeadingCommentWithoutDetachedComments ( cb : ( commentPos : number , commentEnd : number , kind : SyntaxKind , hasTrailingNewLine : boolean , rangePos : number ) => void ) {
254279 // get the leading comments from detachedPos
255280 const pos = lastOrUndefined ( detachedCommentsInfo ) . detachedCommentEndPos ;
256- const leadingComments = getLeadingCommentRanges ( currentText , pos ) ;
257281 if ( detachedCommentsInfo . length - 1 ) {
258282 detachedCommentsInfo . pop ( ) ;
259283 }
260284 else {
261285 detachedCommentsInfo = undefined ;
262286 }
263287
264- return leadingComments ;
288+ forEachLeadingCommentRange ( currentText , pos , cb , /*state*/ pos ) ;
265289 }
266290
267- function emitDetachedCommentsAndUpdateCommentsInfo ( node : TextRange , removeComments : boolean ) {
268- const currentDetachedCommentInfo = emitDetachedComments ( currentText , currentLineMap , writer , writeComment , node , newLine , removeComments ) ;
269-
291+ function emitDetachedCommentsAndUpdateCommentsInfo ( range : TextRange ) {
292+ const currentDetachedCommentInfo = emitDetachedComments ( currentText , currentLineMap , writer , writeComment , range , newLine , compilerOptions . removeComments ) ;
270293 if ( currentDetachedCommentInfo ) {
271294 if ( detachedCommentsInfo ) {
272295 detachedCommentsInfo . push ( currentDetachedCommentInfo ) ;
@@ -277,10 +300,29 @@ namespace ts {
277300 }
278301 }
279302
280- function writeComment ( text : string , lineMap : number [ ] , writer : EmitTextWriter , comment : CommentRange , newLine : string ) {
281- emitPos ( comment . pos ) ;
282- writeCommentRange ( text , lineMap , writer , comment , newLine ) ;
283- emitPos ( comment . end ) ;
303+ function writeComment ( text : string , lineMap : number [ ] , writer : EmitTextWriter , commentPos : number , commentEnd : number , newLine : string ) {
304+ emitPos ( commentPos ) ;
305+ writeCommentRange ( text , lineMap , writer , commentPos , commentEnd , newLine ) ;
306+ emitPos ( commentEnd ) ;
307+ }
308+
309+ /**
310+ * Determine if the given comment is a triple-slash
311+ *
312+ * @return true if the comment is a triple-slash comment else false
313+ **/
314+ function isTripleSlashComment ( commentPos : number , commentEnd : number ) {
315+ // Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
316+ // so that we don't end up computing comment string and doing match for all // comments
317+ if ( currentText . charCodeAt ( commentPos + 1 ) === CharacterCodes . slash &&
318+ commentPos + 2 < commentEnd &&
319+ currentText . charCodeAt ( commentPos + 2 ) === CharacterCodes . slash ) {
320+ const textSubStr = currentText . substring ( commentPos , commentEnd ) ;
321+ return textSubStr . match ( fullTripleSlashReferencePathRegEx ) ||
322+ textSubStr . match ( fullTripleSlashAMDReferencePathRegEx ) ?
323+ true : false ;
324+ }
325+ return false ;
284326 }
285327 }
286328}
0 commit comments