From 1703972dd2a3980af42c1d4423ad02b8498b92f9 Mon Sep 17 00:00:00 2001 From: Martin Vseticka Date: Fri, 30 Oct 2015 20:43:22 +0100 Subject: [PATCH] Preserve copyright comments when generating d.ts files --- src/compiler/declarationEmitter.ts | 1 + src/compiler/emitter.ts | 76 +++-------------- src/compiler/utilities.ts | 68 +++++++++++++++ .../declarationEmitDetachedComment1.js | 85 +++++++++++++++++++ .../declarationEmitDetachedComment1.symbols | 34 ++++++++ .../declarationEmitDetachedComment1.types | 34 ++++++++ .../declarationEmitDetachedComment2.js | 65 ++++++++++++++ .../declarationEmitDetachedComment2.symbols | 34 ++++++++ .../declarationEmitDetachedComment2.types | 34 ++++++++ .../declarationEmitDetachedComment1.ts | 34 ++++++++ .../declarationEmitDetachedComment2.ts | 34 ++++++++ 11 files changed, 435 insertions(+), 64 deletions(-) create mode 100644 tests/baselines/reference/declarationEmitDetachedComment1.js create mode 100644 tests/baselines/reference/declarationEmitDetachedComment1.symbols create mode 100644 tests/baselines/reference/declarationEmitDetachedComment1.types create mode 100644 tests/baselines/reference/declarationEmitDetachedComment2.js create mode 100644 tests/baselines/reference/declarationEmitDetachedComment2.symbols create mode 100644 tests/baselines/reference/declarationEmitDetachedComment2.types create mode 100644 tests/cases/compiler/declarationEmitDetachedComment1.ts create mode 100644 tests/cases/compiler/declarationEmitDetachedComment2.ts diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 00085f16086..719085f7f6a 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -468,6 +468,7 @@ namespace ts { function emitSourceFile(node: SourceFile) { currentSourceFile = node; enclosingDeclaration = node; + emitDetachedComments(currentSourceFile, writer, writeCommentRange, node, newLine, true /* remove comments */); emitLines(node.statements); } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c3933451a7a..2e2ae290c37 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -4907,7 +4907,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi increaseIndent(); let outPos = writer.getTextPos(); - emitDetachedComments(node.body); + emitDetachedCommentsAndUpdateCommentsInfo(node.body); emitFunctionBodyPreamble(node); let preambleEmitted = writer.getTextPos() !== outPos; decreaseIndent(); @@ -4952,7 +4952,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi let initialTextPos = writer.getTextPos(); increaseIndent(); - emitDetachedComments(body.statements); + emitDetachedCommentsAndUpdateCommentsInfo(body.statements); // Emit all the directive prologues (like "use strict"). These have to come before // any other preamble code we write (like parameter initializers). @@ -5274,7 +5274,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // Emit all the directive prologues (like "use strict"). These have to come before // any other preamble code we write (like parameter initializers). startIndex = emitDirectivePrologues(ctor.body.statements, /*startWithNewLine*/ true); - emitDetachedComments(ctor.body.statements); + emitDetachedCommentsAndUpdateCommentsInfo(ctor.body.statements); } emitCaptureThisForNodeIfNecessary(node); let superCall: ExpressionStatement; @@ -7652,7 +7652,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi // Start new file on new line writeLine(); emitShebang(); - emitDetachedComments(node); + emitDetachedCommentsAndUpdateCommentsInfo(node); if (isExternalModule(node) || compilerOptions.isolatedModules) { let emitModule = moduleEmitDelegates[modulekind] || moduleEmitDelegates[ModuleKind.CommonJS]; @@ -7948,11 +7948,6 @@ 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; - } - /** * Determine if the given comment is a triple-slash * @@ -8086,62 +8081,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi emitComments(currentSourceFile, writer, leadingComments, /*trailingSeparator*/ true, newLine, writeComment); } - function emitDetachedComments(node: TextRange) { - let leadingComments: CommentRange[]; - if (compilerOptions.removeComments) { - // removeComments is true, only reserve pinned comment at the top of file - // For example: - // /*! Pinned Comment */ - // - // var x = 10; - if (node.pos === 0) { - leadingComments = filter(getLeadingCommentRanges(currentSourceFile.text, node.pos), isPinnedComments); + function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange) { + let currentDetachedCommentInfo = emitDetachedComments(currentSourceFile, writer, writeComment, node, newLine, compilerOptions.removeComments); + + if (currentDetachedCommentInfo) { + if (detachedCommentsInfo) { + detachedCommentsInfo.push(currentDetachedCommentInfo); } - } - 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; - - forEach(leadingComments, comment => { - if (lastComment) { - let lastCommentLine = getLineOfLocalPosition(currentSourceFile, lastComment.end); - let commentLine = getLineOfLocalPosition(currentSourceFile, comment.pos); - - if (commentLine >= lastCommentLine + 2) { - // There was a blank line between the last comment and this comment. This - // comment is not part of the copyright comments. Return what we have so - // far. - return detachedComments; - } - } - - detachedComments.push(comment); - lastComment = comment; - }); - - 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. - let lastCommentLine = getLineOfLocalPosition(currentSourceFile, lastOrUndefined(detachedComments).end); - let nodeLine = getLineOfLocalPosition(currentSourceFile, skipTrivia(currentSourceFile.text, node.pos)); - if (nodeLine >= lastCommentLine + 2) { - // Valid detachedComments - emitNewLineBeforeLeadingComments(currentSourceFile, writer, node, leadingComments); - emitComments(currentSourceFile, writer, detachedComments, /*trailingSeparator*/ true, newLine, writeComment); - let currentDetachedCommentInfo = { nodePos: node.pos, detachedCommentEndPos: lastOrUndefined(detachedComments).end }; - if (detachedCommentsInfo) { - detachedCommentsInfo.push(currentDetachedCommentInfo); - } - else { - detachedCommentsInfo = [currentDetachedCommentInfo]; - } - } + else { + detachedCommentsInfo = [currentDetachedCommentInfo]; } } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index fd991039d3e..4e0b5d125ab 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1904,6 +1904,74 @@ namespace ts { }); } + /** + * Detached comment is a comment at the top of file or function body that is separated from + * the next statement by space. + */ + export function emitDetachedComments(currentSourceFile: SourceFile, writer: EmitTextWriter, + writeComment: (currentSourceFile: SourceFile, writer: EmitTextWriter, comment: CommentRange, newLine: string) => void, + node: TextRange, newLine: string, removeComments: boolean) { + let leadingComments: CommentRange[]; + let currentDetachedCommentInfo: {nodePos: number, detachedCommentEndPos: number}; + if (removeComments) { + // removeComments is true, only reserve pinned comment at the top of file + // For example: + // /*! Pinned Comment */ + // + // var x = 10; + if (node.pos === 0) { + leadingComments = filter(getLeadingCommentRanges(currentSourceFile.text, node.pos), isPinnedComment); + } + } + 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; + + for (let comment of leadingComments) { + if (lastComment) { + let lastCommentLine = getLineOfLocalPosition(currentSourceFile, lastComment.end); + let commentLine = getLineOfLocalPosition(currentSourceFile, comment.pos); + + if (commentLine >= lastCommentLine + 2) { + // There was a blank line between the last comment and this comment. This + // comment is not part of the copyright comments. Return what we have so + // far. + break; + } + } + + detachedComments.push(comment); + lastComment = comment; + } + + 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. + let lastCommentLine = getLineOfLocalPosition(currentSourceFile, lastOrUndefined(detachedComments).end); + let nodeLine = getLineOfLocalPosition(currentSourceFile, skipTrivia(currentSourceFile.text, node.pos)); + if (nodeLine >= lastCommentLine + 2) { + // Valid detachedComments + emitNewLineBeforeLeadingComments(currentSourceFile, writer, node, leadingComments); + emitComments(currentSourceFile, writer, detachedComments, /*trailingSeparator*/ true, newLine, writeComment); + currentDetachedCommentInfo = { nodePos: node.pos, detachedCommentEndPos: lastOrUndefined(detachedComments).end }; + } + } + } + + return currentDetachedCommentInfo; + + function isPinnedComment(comment: CommentRange) { + return currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk && + currentSourceFile.text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation; + } + } + export function writeCommentRange(currentSourceFile: SourceFile, writer: EmitTextWriter, comment: CommentRange, newLine: string) { if (currentSourceFile.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk) { let firstCommentLineAndCharacter = getLineAndCharacterOfPosition(currentSourceFile, comment.pos); diff --git a/tests/baselines/reference/declarationEmitDetachedComment1.js b/tests/baselines/reference/declarationEmitDetachedComment1.js new file mode 100644 index 00000000000..38c7e60406d --- /dev/null +++ b/tests/baselines/reference/declarationEmitDetachedComment1.js @@ -0,0 +1,85 @@ +//// [tests/cases/compiler/declarationEmitDetachedComment1.ts] //// + +//// [test1.ts] + +/*! Copyright 2015 MyCompany Inc. */ + +/** + * Hello class + */ +class Hello { + +} + +//// [test2.ts] +/* A comment at the top of the file. */ + +/** + * Hi class + */ +class Hi { + +} + +//// [test3.ts] +// A one-line comment at the top of the file. + +/** + * Hola class + */ +class Hola { + +} + + +//// [test1.js] +/*! Copyright 2015 MyCompany Inc. */ +/** + * Hello class + */ +var Hello = (function () { + function Hello() { + } + return Hello; +})(); +//// [test2.js] +/* A comment at the top of the file. */ +/** + * Hi class + */ +var Hi = (function () { + function Hi() { + } + return Hi; +})(); +//// [test3.js] +// A one-line comment at the top of the file. +/** + * Hola class + */ +var Hola = (function () { + function Hola() { + } + return Hola; +})(); + + +//// [test1.d.ts] +/*! Copyright 2015 MyCompany Inc. */ +/** + * Hello class + */ +declare class Hello { +} +//// [test2.d.ts] +/** + * Hi class + */ +declare class Hi { +} +//// [test3.d.ts] +/** + * Hola class + */ +declare class Hola { +} diff --git a/tests/baselines/reference/declarationEmitDetachedComment1.symbols b/tests/baselines/reference/declarationEmitDetachedComment1.symbols new file mode 100644 index 00000000000..c466716ca44 --- /dev/null +++ b/tests/baselines/reference/declarationEmitDetachedComment1.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/test1.ts === + +/*! Copyright 2015 MyCompany Inc. */ + +/** + * Hello class + */ +class Hello { +>Hello : Symbol(Hello, Decl(test1.ts, 0, 0)) + +} + +=== tests/cases/compiler/test2.ts === +/* A comment at the top of the file. */ + +/** + * Hi class + */ +class Hi { +>Hi : Symbol(Hi, Decl(test2.ts, 0, 0)) + +} + +=== tests/cases/compiler/test3.ts === +// A one-line comment at the top of the file. + +/** + * Hola class + */ +class Hola { +>Hola : Symbol(Hola, Decl(test3.ts, 0, 0)) + +} + diff --git a/tests/baselines/reference/declarationEmitDetachedComment1.types b/tests/baselines/reference/declarationEmitDetachedComment1.types new file mode 100644 index 00000000000..6d1a87b5151 --- /dev/null +++ b/tests/baselines/reference/declarationEmitDetachedComment1.types @@ -0,0 +1,34 @@ +=== tests/cases/compiler/test1.ts === + +/*! Copyright 2015 MyCompany Inc. */ + +/** + * Hello class + */ +class Hello { +>Hello : Hello + +} + +=== tests/cases/compiler/test2.ts === +/* A comment at the top of the file. */ + +/** + * Hi class + */ +class Hi { +>Hi : Hi + +} + +=== tests/cases/compiler/test3.ts === +// A one-line comment at the top of the file. + +/** + * Hola class + */ +class Hola { +>Hola : Hola + +} + diff --git a/tests/baselines/reference/declarationEmitDetachedComment2.js b/tests/baselines/reference/declarationEmitDetachedComment2.js new file mode 100644 index 00000000000..d4d176da12c --- /dev/null +++ b/tests/baselines/reference/declarationEmitDetachedComment2.js @@ -0,0 +1,65 @@ +//// [tests/cases/compiler/declarationEmitDetachedComment2.ts] //// + +//// [test1.ts] + +/*! Copyright 2015 MyCompany Inc. */ + +/** + * Hello class + */ +class Hello { + +} + +//// [test2.ts] +/* A comment at the top of the file. */ + +/** + * Hi class + */ +class Hi { + +} + +//// [test3.ts] +// A one-line comment at the top of the file. + +/** + * Hola class + */ +class Hola { + +} + + +//// [test1.js] +/*! Copyright 2015 MyCompany Inc. */ +var Hello = (function () { + function Hello() { + } + return Hello; +})(); +//// [test2.js] +var Hi = (function () { + function Hi() { + } + return Hi; +})(); +//// [test3.js] +var Hola = (function () { + function Hola() { + } + return Hola; +})(); + + +//// [test1.d.ts] +/*! Copyright 2015 MyCompany Inc. */ +declare class Hello { +} +//// [test2.d.ts] +declare class Hi { +} +//// [test3.d.ts] +declare class Hola { +} diff --git a/tests/baselines/reference/declarationEmitDetachedComment2.symbols b/tests/baselines/reference/declarationEmitDetachedComment2.symbols new file mode 100644 index 00000000000..c466716ca44 --- /dev/null +++ b/tests/baselines/reference/declarationEmitDetachedComment2.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/test1.ts === + +/*! Copyright 2015 MyCompany Inc. */ + +/** + * Hello class + */ +class Hello { +>Hello : Symbol(Hello, Decl(test1.ts, 0, 0)) + +} + +=== tests/cases/compiler/test2.ts === +/* A comment at the top of the file. */ + +/** + * Hi class + */ +class Hi { +>Hi : Symbol(Hi, Decl(test2.ts, 0, 0)) + +} + +=== tests/cases/compiler/test3.ts === +// A one-line comment at the top of the file. + +/** + * Hola class + */ +class Hola { +>Hola : Symbol(Hola, Decl(test3.ts, 0, 0)) + +} + diff --git a/tests/baselines/reference/declarationEmitDetachedComment2.types b/tests/baselines/reference/declarationEmitDetachedComment2.types new file mode 100644 index 00000000000..6d1a87b5151 --- /dev/null +++ b/tests/baselines/reference/declarationEmitDetachedComment2.types @@ -0,0 +1,34 @@ +=== tests/cases/compiler/test1.ts === + +/*! Copyright 2015 MyCompany Inc. */ + +/** + * Hello class + */ +class Hello { +>Hello : Hello + +} + +=== tests/cases/compiler/test2.ts === +/* A comment at the top of the file. */ + +/** + * Hi class + */ +class Hi { +>Hi : Hi + +} + +=== tests/cases/compiler/test3.ts === +// A one-line comment at the top of the file. + +/** + * Hola class + */ +class Hola { +>Hola : Hola + +} + diff --git a/tests/cases/compiler/declarationEmitDetachedComment1.ts b/tests/cases/compiler/declarationEmitDetachedComment1.ts new file mode 100644 index 00000000000..8d49482f3cc --- /dev/null +++ b/tests/cases/compiler/declarationEmitDetachedComment1.ts @@ -0,0 +1,34 @@ +// @target: es5 +// @module: commonjs +// @declaration: true +// @removeComments: false + +// @filename: test1.ts +/*! Copyright 2015 MyCompany Inc. */ + +/** + * Hello class + */ +class Hello { + +} + +// @filename: test2.ts +/* A comment at the top of the file. */ + +/** + * Hi class + */ +class Hi { + +} + +// @filename: test3.ts +// A one-line comment at the top of the file. + +/** + * Hola class + */ +class Hola { + +} diff --git a/tests/cases/compiler/declarationEmitDetachedComment2.ts b/tests/cases/compiler/declarationEmitDetachedComment2.ts new file mode 100644 index 00000000000..1eed803c625 --- /dev/null +++ b/tests/cases/compiler/declarationEmitDetachedComment2.ts @@ -0,0 +1,34 @@ +// @target: es5 +// @module: commonjs +// @declaration: true +// @removeComments: true + +// @filename: test1.ts +/*! Copyright 2015 MyCompany Inc. */ + +/** + * Hello class + */ +class Hello { + +} + +// @filename: test2.ts +/* A comment at the top of the file. */ + +/** + * Hi class + */ +class Hi { + +} + +// @filename: test3.ts +// A one-line comment at the top of the file. + +/** + * Hola class + */ +class Hola { + +}