diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 198adc4c37e..deedb693205 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -336,6 +336,9 @@ module ts { /** Emit detached comments of the node */ var emitDetachedComments = compilerOptions.removeComments ? (node: TextRange) => { } : emitDetachedCommentsAtPosition; + /** Emits /// or pinned which is comment starting with /*! comments */ + var emitPinnedOrTripleSlashComments = compilerOptions.removeComments ? (node: Node) => { } : emitPinnedOrTripleSlashCommentsOfNode; + var writeComment = writeCommentRange; /** Emit a node */ @@ -1318,7 +1321,10 @@ module ts { } function emitFunctionDeclaration(node: FunctionDeclaration) { - if (!node.body) return; + if (!node.body) { + return emitPinnedOrTripleSlashComments(node); + } + if (node.kind !== SyntaxKind.Method) { // Methods will emit the comments as part of emitting method declaration emitLeadingComments(node); @@ -1488,7 +1494,10 @@ module ts { function emitMemberFunctions(node: ClassDeclaration) { forEach(node.members, member => { if (member.kind === SyntaxKind.Method) { - if (!(member).body) return; + if (!(member).body) { + return emitPinnedOrTripleSlashComments(member); + } + writeLine(); emitLeadingComments(member); emitStart(member); @@ -1611,6 +1620,13 @@ module ts { emitTrailingComments(node); function emitConstructorOfClass() { + // Emit the constructor overload pinned comments + forEach(node.members, member => { + if (member.kind === SyntaxKind.Constructor && !(member).body) { + emitPinnedOrTripleSlashComments(member); + } + }); + var ctor = getFirstConstructorWithBody(node); if (ctor) { emitLeadingComments(ctor); @@ -2101,7 +2117,7 @@ module ts { return leadingComments; } - function emitLeadingDeclarationComments(node: Node) { + function getLeadingCommentsToEmit(node: Node) { // Emit the leading comments only if the parent's pos doesnt match because parent should take care of emitting these comments if (node.parent.kind === SyntaxKind.SourceFile || node.pos !== node.parent.pos) { var leadingComments: Comment[]; @@ -2113,12 +2129,17 @@ module ts { // get the leading comments from the node leadingComments = getLeadingCommentsOfNode(node, currentSourceFile); } - emitNewLineBeforeLeadingComments(node, leadingComments, writer); - // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space - emitComments(leadingComments, /*trailingSeparator*/ true, writer, writeComment); + return leadingComments; } } + function emitLeadingDeclarationComments(node: Node) { + var leadingComments = getLeadingCommentsToEmit(node); + emitNewLineBeforeLeadingComments(node, leadingComments, writer); + // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space + emitComments(leadingComments, /*trailingSeparator*/ true, writer, writeComment); + } + function emitTrailingDeclarationComments(node: Node) { // Emit the trailing comments only if the parent's end doesnt match if (node.parent.kind === SyntaxKind.SourceFile || node.end !== node.parent.end) { @@ -2166,7 +2187,7 @@ module ts { lastComment = comment; }); - if (detachedComments && detachedComments.length) { + if (detachedComments.length) { // All comments look like they could have been part of the copyright header. Make // sure there is at least one blank line between it and the node. If not, it's not // a copyright header. @@ -2188,6 +2209,28 @@ module ts { } } + function emitPinnedOrTripleSlashCommentsOfNode(node: Node) { + var pinnedComments = ts.filter(getLeadingCommentsToEmit(node), isPinnedOrTripleSlashComment); + + function isPinnedOrTripleSlashComment(comment: Comment) { + 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 dont 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; + } + } + + emitNewLineBeforeLeadingComments(node, pinnedComments, writer); + // Leading comments are emitted at /*leading comment1 */space/*leading comment*/space + emitComments(pinnedComments, /*trailingSeparator*/ true, writer, writeComment); + } + if (compilerOptions.sourceMap) { initializeEmitterWithSourceMaps(); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 4f90973ce98..f6a9506542a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -165,6 +165,8 @@ module ts { } } + export var fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*/ + // Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes // stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise, // embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns @@ -3506,8 +3508,7 @@ module ts { file.hasNoDefaultLib = true; } else { - var fullReferenceRegEx = /^(\/\/\/\s*/; - var matchResult = fullReferenceRegEx.exec(comment); + var matchResult = fullTripleSlashReferencePathRegEx.exec(comment); if (!matchResult) { var start = range.pos; var length = range.end - start; diff --git a/tests/baselines/reference/commentOnSignature1.js b/tests/baselines/reference/commentOnSignature1.js index fac146e4e6c..1b2327f9861 100644 --- a/tests/baselines/reference/commentOnSignature1.js +++ b/tests/baselines/reference/commentOnSignature1.js @@ -1,11 +1,51 @@ -//// [commentOnSignature1.ts] +//// [tests/cases/compiler/commentOnSignature1.ts] //// + +//// [a.ts] /*! Keep this pinned comment */ function foo(n: number): void; // Don't keep this comment. function foo(s: string): void; function foo(a: any): void { +} + +class c { + // dont keep this comment + constructor(a: string); + /*! keep this pinned comment */ + constructor(a: number); + constructor(a: any) { + } + + // dont keep this comment + foo(a: string); + /*! keep this pinned comment */ + foo(a: number); + foo(a: any) { + } +} + +//// [b.ts] +/// +function foo2(n: number): void; +// Don't keep this comment. +function foo2(s: string): void; +function foo2(a: any): void { } -//// [commentOnSignature1.js] +//// [a.js] +/*! Keep this pinned comment */ function foo(a) { } +var c = (function () { + /*! keep this pinned comment */ + function c(a) { + } + /*! keep this pinned comment */ + c.prototype.foo = function (a) { + }; + return c; +})(); +//// [b.js] +/// +function foo2(a) { +} diff --git a/tests/baselines/reference/commentOnSignature1.types b/tests/baselines/reference/commentOnSignature1.types index 6fd4516d4e9..790aaaf7523 100644 --- a/tests/baselines/reference/commentOnSignature1.types +++ b/tests/baselines/reference/commentOnSignature1.types @@ -1,4 +1,19 @@ -=== tests/cases/compiler/commentOnSignature1.ts === +=== tests/cases/compiler/b.ts === +/// +function foo2(n: number): void; +>foo2 : { (n: number): void; (s: string): void; } +>n : number + +// Don't keep this comment. +function foo2(s: string): void; +>foo2 : { (n: number): void; (s: string): void; } +>s : string + +function foo2(a: any): void { +>foo2 : { (n: number): void; (s: string): void; } +>a : any +} +=== tests/cases/compiler/a.ts === /*! Keep this pinned comment */ function foo(n: number): void; >foo : { (n: number): void; (s: string): void; } @@ -13,3 +28,35 @@ function foo(a: any): void { >foo : { (n: number): void; (s: string): void; } >a : any } + +class c { +>c : c + + // dont keep this comment + constructor(a: string); +>a : string + + /*! keep this pinned comment */ + constructor(a: number); +>a : number + + constructor(a: any) { +>a : any + } + + // dont keep this comment + foo(a: string); +>foo : { (a: string): any; (a: number): any; } +>a : string + + /*! keep this pinned comment */ + foo(a: number); +>foo : { (a: string): any; (a: number): any; } +>a : number + + foo(a: any) { +>foo : { (a: string): any; (a: number): any; } +>a : any + } +} + diff --git a/tests/cases/compiler/commentOnSignature1.ts b/tests/cases/compiler/commentOnSignature1.ts index 820a0f89036..770c103a9fc 100644 --- a/tests/cases/compiler/commentOnSignature1.ts +++ b/tests/cases/compiler/commentOnSignature1.ts @@ -1,6 +1,31 @@ +// @filename: a.ts /*! Keep this pinned comment */ function foo(n: number): void; // Don't keep this comment. function foo(s: string): void; function foo(a: any): void { +} + +class c { + // dont keep this comment + constructor(a: string); + /*! keep this pinned comment */ + constructor(a: number); + constructor(a: any) { + } + + // dont keep this comment + foo(a: string); + /*! keep this pinned comment */ + foo(a: number); + foo(a: any) { + } +} + +//@filename:b.ts +/// +function foo2(n: number): void; +// Don't keep this comment. +function foo2(s: string): void; +function foo2(a: any): void { } \ No newline at end of file