Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 90 additions & 48 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
/** Sourcemap data that will get encoded */
let sourceMapData: SourceMapData;

/** If removeComments is true, no leading-comments needed to be emitted **/
let emitLeadingCommentsOfPosition = compilerOptions.removeComments ? function (pos: number) { } : emitLeadingCommentsOfPositionWorker;

if (compilerOptions.sourceMap || compilerOptions.inlineSourceMap) {
initializeEmitterWithSourceMaps();
}
Expand Down Expand Up @@ -3664,7 +3667,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi

function emitFunctionDeclaration(node: FunctionLikeDeclaration) {
if (nodeIsMissing(node.body)) {
return emitOnlyPinnedOrTripleSlashComments(node);
return emitCommentsOnNotEmittedNode(node);
}

// TODO (yuisu) : we should not have special cases to condition emitting comments
Expand Down Expand Up @@ -4141,7 +4144,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
else if (member.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) {
if (!(<MethodDeclaration>member).body) {
return emitOnlyPinnedOrTripleSlashComments(member);
return emitCommentsOnNotEmittedNode(member);
}

writeLine();
Expand Down Expand Up @@ -4208,7 +4211,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
function emitMemberFunctionsForES6AndHigher(node: ClassLikeDeclaration) {
for (let member of node.members) {
if ((member.kind === SyntaxKind.MethodDeclaration || node.kind === SyntaxKind.MethodSignature) && !(<MethodDeclaration>member).body) {
emitOnlyPinnedOrTripleSlashComments(member);
emitCommentsOnNotEmittedNode(member);
}
else if (member.kind === SyntaxKind.MethodDeclaration ||
member.kind === SyntaxKind.GetAccessor ||
Expand Down Expand Up @@ -4265,7 +4268,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
// Emit the constructor overload pinned comments
forEach(node.members, member => {
if (member.kind === SyntaxKind.Constructor && !(<ConstructorDeclaration>member).body) {
emitOnlyPinnedOrTripleSlashComments(member);
emitCommentsOnNotEmittedNode(member);
}
// Check if there is any non-static property assignment
if (member.kind === SyntaxKind.PropertyDeclaration && (<PropertyDeclaration>member).initializer && (member.flags & NodeFlags.Static) === 0) {
Expand Down Expand Up @@ -5166,7 +5169,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}

function emitInterfaceDeclaration(node: InterfaceDeclaration) {
emitOnlyPinnedOrTripleSlashComments(node);
emitCommentsOnNotEmittedNode(node);
}

function shouldEmitEnumDeclaration(node: EnumDeclaration) {
Expand Down Expand Up @@ -5288,7 +5291,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
let shouldEmit = shouldEmitModuleDeclaration(node);

if (!shouldEmit) {
return emitOnlyPinnedOrTripleSlashComments(node);
return emitCommentsOnNotEmittedNode(node);
}
let hoistedInDeclarationScope = shouldHoistDeclarationInSystemJsModule(node);
let emitVarForModule = !hoistedInDeclarationScope && !isModuleMergedWithES6Class(node);
Expand Down Expand Up @@ -6718,7 +6721,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
function emitNodeConsideringCommentsOption(node: Node, emitNodeConsideringSourcemap: (node: Node) => void): void {
if (node) {
if (node.flags & NodeFlags.Ambient) {
return emitOnlyPinnedOrTripleSlashComments(node);
return emitCommentsOnNotEmittedNode(node);
}

if (isSpecializedCommentHandling(node)) {
Expand Down Expand Up @@ -6985,22 +6988,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
return leadingComments;
}

function isPinnedComments(comment: CommentRange) {
return currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation;
}

/**
* Removes all but the pinned or triple slash comments.
* @param ranges The array to be filtered
* @param onlyPinnedOrTripleSlashComments whether the filtering should be performed.
*/
function filterComments(ranges: CommentRange[], onlyPinnedOrTripleSlashComments: boolean): CommentRange[] {
// If we're removing comments, then we want to strip out all but the pinned or
// triple slash comments.
if (ranges && onlyPinnedOrTripleSlashComments) {
ranges = filter(ranges, isPinnedOrTripleSlashComment);
if (ranges.length === 0) {
return undefined;
}
* Determine if the given comment is a triple-slash
*
* @return true if the comment is a triple-slash comment else false
**/
function isTripleSlashComment(comment: CommentRange) {
// Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
// so that we don't end up computing comment string and doing match for all // comments
if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.slash &&
comment.pos + 2 < comment.end &&
currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.slash) {
let textSubStr = currentSourceFile.text.substring(comment.pos, comment.end);
return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ?
true : false;
}

return ranges;
return false;
}

function getLeadingCommentsToEmit(node: Node) {
Expand Down Expand Up @@ -7028,28 +7037,53 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
}
}

function emitOnlyPinnedOrTripleSlashComments(node: Node) {
emitLeadingCommentsWorker(node, /*onlyPinnedOrTripleSlashComments:*/ true);
/**
* Emit comments associated with node that will not be emitted into JS file
*/
function emitCommentsOnNotEmittedNode(node: Node) {
emitLeadingCommentsWorker(node, /*isEmittedNode:*/ false);
}

function emitLeadingComments(node: Node) {
return emitLeadingCommentsWorker(node, /*onlyPinnedOrTripleSlashComments:*/ compilerOptions.removeComments);
return emitLeadingCommentsWorker(node, /*isEmittedNode:*/ true);
}

function emitLeadingCommentsWorker(node: Node, onlyPinnedOrTripleSlashComments: boolean) {
// If the caller only wants pinned or triple slash comments, then always filter
// down to that set. Otherwise, filter based on the current compiler options.
let leadingComments = filterComments(getLeadingCommentsToEmit(node), onlyPinnedOrTripleSlashComments);
function emitLeadingCommentsWorker(node: Node, isEmittedNode: boolean) {
if (compilerOptions.removeComments) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is concerning.

return;
}

let leadingComments: CommentRange[];
if (isEmittedNode) {
leadingComments = getLeadingCommentsToEmit(node);
}
else {
// If the node will not be emitted in JS, remove all the comments(normal, pinned and ///) associated with the node,
// unless it is a triple slash comment at the top of the file.
// For Example:
// /// <reference-path ...>
// declare var x;
// /// <reference-path ...>
// interface F {}
// The first /// will NOT be removed while the second one will be removed eventhough both node will not be emitted
if (node.pos === 0) {
leadingComments = filter(getLeadingCommentsToEmit(node), isTripleSlashComment);
}
}

emitNewLineBeforeLeadingComments(currentSourceFile, writer, node, leadingComments);

// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
emitComments(currentSourceFile, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment);
emitComments(currentSourceFile, writer, leadingComments, /*trailingSeparator:*/ true, newLine, writeComment);
}

function emitTrailingComments(node: Node) {
if (compilerOptions.removeComments) {
return;
}

// Emit the trailing comments only if the parent's end doesn't match
let trailingComments = filterComments(getTrailingCommentsToEmit(node), /*onlyPinnedOrTripleSlashComments:*/ compilerOptions.removeComments);
let trailingComments = getTrailingCommentsToEmit(node);

// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
emitComments(currentSourceFile, writer, trailingComments, /*trailingSeparator*/ false, newLine, writeComment);
Expand All @@ -7061,13 +7095,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
* ^ => pos; the function will emit "comment1" in the emitJS
*/
function emitTrailingCommentsOfPosition(pos: number) {
let trailingComments = filterComments(getTrailingCommentRanges(currentSourceFile.text, pos), /*onlyPinnedOrTripleSlashComments:*/ compilerOptions.removeComments);
if (compilerOptions.removeComments) {
return;
}

let trailingComments = getTrailingCommentRanges(currentSourceFile.text, pos);

// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
emitComments(currentSourceFile, writer, trailingComments, /*trailingSeparator*/ true, newLine, writeComment);
}

function emitLeadingCommentsOfPosition(pos: number) {
function emitLeadingCommentsOfPositionWorker(pos: number) {
if (compilerOptions.removeComments) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing this can you make emitLeadingCommentsOfPosition a function pointer. It helps with the perf since that check happens just once. Do this for other functions that do nothing when remove comments too.

emitLeadingCommentsOfPosition = compilerOptions.removeComments ? function (pos: number) { } : emitLeadingOCmmentsOfPositionWorker

return;
}

let leadingComments: CommentRange[];
if (hasDetachedComments(pos)) {
// get comments without detached comments
Expand All @@ -7078,15 +7120,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
leadingComments = getLeadingCommentRanges(currentSourceFile.text, pos);
}

leadingComments = filterComments(leadingComments, compilerOptions.removeComments);
emitNewLineBeforeLeadingComments(currentSourceFile, writer, { pos: pos, end: pos }, leadingComments);

// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
emitComments(currentSourceFile, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment);
}

function emitDetachedComments(node: TextRange) {
let leadingComments = getLeadingCommentRanges(currentSourceFile.text, node.pos);
let leadingComments: CommentRange[];
if (compilerOptions.removeComments) {
// removeComments is true, only reserve pinned comment at the top of file
// For example:
// /*! Pinned Comment */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems wrong (and seems like a very strange thing to explain to people using the product). "We will preserve pinned comments. But only at the very top of a file".

It also seems like we'll be breaking people who use pinned comments today (a feature we added precisely for people that wanted to use --removeComments, but who wanted some comments to remain).

//
// var x = 10;
if (node.pos === 0) {
leadingComments = filter(getLeadingCommentRanges(currentSourceFile.text, node.pos), isPinnedComments);
}
}
else {
// removeComments is false, just get detached as normal and bypass the process to filter comment
leadingComments = getLeadingCommentRanges(currentSourceFile.text, node.pos);
}

if (leadingComments) {
let detachedComments: CommentRange[] = [];
let lastComment: CommentRange;
Expand Down Expand Up @@ -7136,20 +7192,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
write(shebang);
}
}

function isPinnedOrTripleSlashComment(comment: CommentRange) {
if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk) {
return currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation;
}
// Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
// so that we don't end up computing comment string and doing match for all // comments
else if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.slash &&
comment.pos + 2 < comment.end &&
currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.slash &&
currentSourceFile.text.substring(comment.pos, comment.end).match(fullTripleSlashReferencePathRegEx)) {
return true;
}
}
}

function emitFile(jsFilePath: string, sourceFile?: SourceFile) {
Expand Down
1 change: 1 addition & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ namespace ts {
}

export let fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
export let fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;

export function isTypeNode(node: Node): boolean {
if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) {
Expand Down
12 changes: 10 additions & 2 deletions tests/baselines/reference/commentOnAmbientClass1.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
//// [tests/cases/compiler/commentOnAmbientClass1.ts] ////

//// [a.ts]
/*! Keep this pinned comment */
/*!=========
Keep this pinned comment
=========
*/

/*! Don't keep this pinned comment */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks interesting. probably needs to update the test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you mean by update the test?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the comment text in all tests says "Don't keep this comment" but it shows. is this intended?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this is the ts file. The Js file below shows as expected

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we not keep the pinned comment. The /*! is explicitly there to say "keep me" :) It is much simpler if we just keep pinned comments. Also, it means we can treat any case where we don't keep a pinned comment around as a bug :)

declare class C {
}

Expand All @@ -15,6 +20,9 @@ declare class E extends C {
}

//// [a.js]
/*! Keep this pinned comment */
/*!=========
Keep this pinned comment
=========
*/
//// [b.js]
///<reference path="a.ts"/>
9 changes: 7 additions & 2 deletions tests/baselines/reference/commentOnAmbientClass1.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ declare class E extends C {
>C : Symbol(C, Decl(a.ts, 0, 0))
}
=== tests/cases/compiler/a.ts ===
/*! Keep this pinned comment */
/*!=========
Keep this pinned comment
=========
*/

/*! Don't keep this pinned comment */
declare class C {
>C : Symbol(C, Decl(a.ts, 0, 0))
}

// Don't keep this comment.
declare class D {
>D : Symbol(D, Decl(a.ts, 2, 1))
>D : Symbol(D, Decl(a.ts, 7, 1))
}

7 changes: 6 additions & 1 deletion tests/baselines/reference/commentOnAmbientClass1.types
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ declare class E extends C {
>C : C
}
=== tests/cases/compiler/a.ts ===
/*! Keep this pinned comment */
/*!=========
Keep this pinned comment
=========
*/

/*! Don't keep this pinned comment */
declare class C {
>C : C
}
Expand Down
12 changes: 10 additions & 2 deletions tests/baselines/reference/commentOnAmbientEnum.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
//// [tests/cases/compiler/commentOnAmbientEnum.ts] ////

//// [a.ts]
/*! Keep this pinned comment */
/*!=========
Keep this pinned comment
=========
*/

/*! Don't keep this pinned comment */
declare enum C {
a,
b,
Expand All @@ -18,6 +23,9 @@ declare enum E {
}

//// [a.js]
/*! Keep this pinned comment */
/*!=========
Keep this pinned comment
=========
*/
//// [b.js]
///<reference path="a.ts"/>
15 changes: 10 additions & 5 deletions tests/baselines/reference/commentOnAmbientEnum.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,27 @@ declare enum E {
>E : Symbol(E, Decl(b.ts, 0, 0))
}
=== tests/cases/compiler/a.ts ===
/*! Keep this pinned comment */
/*!=========
Keep this pinned comment
=========
*/

/*! Don't keep this pinned comment */
declare enum C {
>C : Symbol(C, Decl(a.ts, 0, 0))

a,
>a : Symbol(C.a, Decl(a.ts, 1, 16))
>a : Symbol(C.a, Decl(a.ts, 6, 16))

b,
>b : Symbol(C.b, Decl(a.ts, 2, 6))
>b : Symbol(C.b, Decl(a.ts, 7, 6))

c
>c : Symbol(C.c, Decl(a.ts, 3, 6))
>c : Symbol(C.c, Decl(a.ts, 8, 6))
}

// Don't keep this comment.
declare enum D {
>D : Symbol(D, Decl(a.ts, 5, 1))
>D : Symbol(D, Decl(a.ts, 10, 1))
}

7 changes: 6 additions & 1 deletion tests/baselines/reference/commentOnAmbientEnum.types
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ declare enum E {
>E : E
}
=== tests/cases/compiler/a.ts ===
/*! Keep this pinned comment */
/*!=========
Keep this pinned comment
=========
*/

/*! Don't keep this pinned comment */
declare enum C {
>C : C

Expand Down
Loading