mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge pull request #8906 from Microsoft/transforms-commentsPerf
[Transforms] Performance improvements in the comment emitter.
This commit is contained in:
+249
-104
@@ -757,7 +757,7 @@ function deleteTemporaryProjectOutput() {
|
||||
}
|
||||
}
|
||||
|
||||
function runTestsAndWriteOutput(file) {
|
||||
function runTestsAndWriteOutput(file, defaultSubsets) {
|
||||
cleanTestDirs();
|
||||
var tests = process.env.test || process.env.tests || process.env.t;
|
||||
var light = process.env.light || false;
|
||||
@@ -772,114 +772,238 @@ function runTestsAndWriteOutput(file) {
|
||||
testTimeout = 100000;
|
||||
}
|
||||
|
||||
var args = [];
|
||||
args.push("-R", "tap");
|
||||
args.push("--no-colors");
|
||||
args.push("-t", testTimeout);
|
||||
if (tests) {
|
||||
args.push("-g", '"' + tests + '"');
|
||||
var subsetRegexes;
|
||||
var subsets;
|
||||
if (tests || defaultSubsets.length === 0) {
|
||||
subsetRegexes = [tests];
|
||||
subsets = [tests];
|
||||
}
|
||||
args.push(run);
|
||||
else {
|
||||
subsets = [];
|
||||
subsetRegexes = [];
|
||||
negations = [];
|
||||
for (const subset of defaultSubsets) {
|
||||
subsets.push(subset.name);
|
||||
subsetRegexes.push(subset.pattern);
|
||||
negations.push(subset.pattern);
|
||||
}
|
||||
|
||||
var cmd = "mocha " + args.join(" ");
|
||||
console.log(cmd);
|
||||
var p = child_process.spawn(
|
||||
process.platform === "win32" ? "cmd" : "/bin/sh",
|
||||
process.platform === "win32" ? ["/c", cmd] : ["-c", cmd], {
|
||||
windowsVerbatimArguments: true
|
||||
});
|
||||
subsets.push("other");
|
||||
subsetRegexes.push("^(?!" + negations.join("|") + ")");
|
||||
}
|
||||
|
||||
var out = fs.createWriteStream(file);
|
||||
var outFileNames = subsetRegexes.length !== 1 ? [] : undefined;
|
||||
var tapRange = /^(\d+)\.\.(\d+)(?:$|\r\n?|\n)/;
|
||||
var tapOk = /^ok\s/;
|
||||
var tapNotOk = /^not\sok\s/;
|
||||
var tapComment = /^#/;
|
||||
var tapComment = /^#(?: (tests|pass|fail) (\d+)$)?/;
|
||||
var typeError = /^\s+TypeError:/;
|
||||
var debugError = /^\s+Error:\sDebug\sFailure\./;
|
||||
var progress = new ProgressBar("Running tests...");
|
||||
var expectedTestCount = 0;
|
||||
var testCount = 0;
|
||||
var failureCount = 0;
|
||||
var successCount = 0;
|
||||
var comments = [];
|
||||
var typeErrorCount = 0;
|
||||
var debugErrorCount = 0;
|
||||
|
||||
var rl = readline.createInterface({
|
||||
input: p.stdout,
|
||||
terminal: false
|
||||
});
|
||||
|
||||
function updateProgress(percentComplete) {
|
||||
progress.update(percentComplete,
|
||||
/*foregroundColor*/ failureCount > 0
|
||||
? "red"
|
||||
: successCount === expectedTestCount
|
||||
? "green"
|
||||
: "cyan",
|
||||
/*backgroundColor*/ "gray"
|
||||
);
|
||||
}
|
||||
|
||||
rl.on("line", function (line) {
|
||||
var m = tapRange.exec(line);
|
||||
if (m) {
|
||||
expectedTestCount = parseInt(m[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tapOk.test(line)) {
|
||||
out.write(line.replace(/^ok\s+\d+\s+/, "ok ") + os.EOL);
|
||||
successCount++;
|
||||
}
|
||||
else if (tapNotOk.test(line)) {
|
||||
out.write(line.replace(/^not\s+ok\s+\d+\s+/, "not ok ") + os.EOL);
|
||||
failureCount++;
|
||||
var progress = new ProgressBar();
|
||||
var totalTypeErrorCount = 0;
|
||||
var totalDebugErrorCount = 0;
|
||||
var totalReportedTestCount = 0;
|
||||
var totalReportedPassCount = 0;
|
||||
var totalReportedFailCount = 0;
|
||||
var counter = subsetRegexes.length;
|
||||
var errorStatus;
|
||||
subsetRegexes.forEach(function (subsetRegex, i) {
|
||||
var expectedTestCount = 0;
|
||||
var testCount = 0;
|
||||
var failureCount = 0;
|
||||
var successCount = 0;
|
||||
var reportedTestCount = 0;
|
||||
var reportedPassCount = 0;
|
||||
var reportedFailCount = 0;
|
||||
var comments = [];
|
||||
var typeErrorCount = 0;
|
||||
var debugErrorCount = 0;
|
||||
var outFileName;
|
||||
var outFile;
|
||||
if (subsetRegexes.length === 1) {
|
||||
outFile = out;
|
||||
}
|
||||
else {
|
||||
out.write(line + os.EOL);
|
||||
if (tapComment.test(line)) {
|
||||
comments.push(line);
|
||||
outFileName = path.join(os.tmpdir(), path.basename(file) + "." + i);
|
||||
outFileNames[i] = outFileName;
|
||||
outFile = fs.createWriteStream(outFileName);
|
||||
}
|
||||
|
||||
var args = [];
|
||||
args.push("-R", "tap");
|
||||
args.push("--no-colors");
|
||||
args.push("-t", testTimeout);
|
||||
if (subsetRegex) {
|
||||
args.push("-g", '"' + subsetRegex + '"');
|
||||
}
|
||||
|
||||
args.push(run);
|
||||
|
||||
var cmd = "mocha " + args.join(" ");
|
||||
if (subsetRegexes.length === 1) {
|
||||
console.log(cmd);
|
||||
}
|
||||
|
||||
updateProgress(0);
|
||||
|
||||
var p = child_process.spawn(
|
||||
process.platform === "win32" ? "cmd" : "/bin/sh",
|
||||
process.platform === "win32" ? ["/c", cmd] : ["-c", cmd], {
|
||||
windowsVerbatimArguments: true,
|
||||
env: { NODE_ENV: "development" }
|
||||
});
|
||||
|
||||
var rl = readline.createInterface({
|
||||
input: p.stdout,
|
||||
terminal: false
|
||||
});
|
||||
|
||||
var start;
|
||||
var end;
|
||||
rl.on("line", function (line) {
|
||||
if (!start) start = Date.now();
|
||||
var m = tapRange.exec(line);
|
||||
if (m) {
|
||||
expectedTestCount = parseInt(m[2]);
|
||||
return;
|
||||
}
|
||||
else if (typeError.test(line)) {
|
||||
typeErrorCount++;
|
||||
|
||||
if (tapOk.test(line)) {
|
||||
outFile.write(line.replace(/^ok\s+\d+\s+/, "ok ") + os.EOL);
|
||||
successCount++;
|
||||
}
|
||||
else if (debugError.test(line)) {
|
||||
debugErrorCount++;
|
||||
else if (tapNotOk.test(line)) {
|
||||
outFile.write(line.replace(/^not\s+ok\s+\d+\s+/, "not ok ") + os.EOL);
|
||||
failureCount++;
|
||||
}
|
||||
else {
|
||||
m = tapComment.exec(line);
|
||||
if (m) {
|
||||
if (m[1] === "tests") {
|
||||
end = Date.now();
|
||||
reportedTestCount = parseInt(m[2]);
|
||||
}
|
||||
else if (m[1] === "pass") {
|
||||
reportedPassCount = parseInt(m[2]);
|
||||
}
|
||||
else if (m[1] === "fail") {
|
||||
reportedFailCount = parseInt(m[2]);
|
||||
}
|
||||
else {
|
||||
outFile.write(line + os.EOL);
|
||||
}
|
||||
}
|
||||
else {
|
||||
outFile.write(line + os.EOL);
|
||||
if (typeError.test(line)) {
|
||||
typeErrorCount++;
|
||||
}
|
||||
else if (debugError.test(line)) {
|
||||
debugErrorCount++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
testCount++;
|
||||
|
||||
var percentComplete = testCount * 100 / expectedTestCount;
|
||||
updateProgress(percentComplete);
|
||||
});
|
||||
|
||||
p.on("exit", function (status) {
|
||||
totalReportedTestCount += reportedTestCount;
|
||||
totalReportedPassCount += reportedPassCount;
|
||||
totalReportedFailCount += reportedFailCount;
|
||||
totalTypeErrorCount += typeErrorCount;
|
||||
totalDebugErrorCount += debugErrorCount;
|
||||
|
||||
var duration = end - start;
|
||||
var summary =
|
||||
"pass: " + reportedPassCount + "/" + reportedTestCount +
|
||||
", duration: " + (duration / 1000).toFixed(2) + "s";
|
||||
|
||||
updateProgress(100, summary);
|
||||
|
||||
if (subsetRegexes.length !== 1) {
|
||||
outFile.close();
|
||||
}
|
||||
|
||||
if (status && !errorStatus) {
|
||||
errorStatus = status;
|
||||
}
|
||||
|
||||
counter--;
|
||||
if (counter === 0) {
|
||||
if (subsetRegexes.length !== 1) {
|
||||
concatenate();
|
||||
}
|
||||
else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function updateProgress(percentComplete, status) {
|
||||
var title = status || (percentComplete < 100 ? "running..." : "done");
|
||||
if (subsetRegexes.length !== 1) {
|
||||
title = "[" + subsets[i] + "] " + title;
|
||||
}
|
||||
|
||||
progress.update(percentComplete,
|
||||
/*foregroundColor*/ failureCount > 0
|
||||
? "red"
|
||||
: successCount === expectedTestCount
|
||||
? "green"
|
||||
: "cyan",
|
||||
/*backgroundColor*/ "gray",
|
||||
title,
|
||||
i
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function concatenate() {
|
||||
if (outFileNames.length > 0) {
|
||||
var outFileName = outFileNames.shift();
|
||||
var outFile = fs.createReadStream(outFileName);
|
||||
outFile.pipe(out, { end: false });
|
||||
outFile.on("end", function () {
|
||||
fs.unlinkSync(outFileName);
|
||||
concatenate();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
testCount++;
|
||||
finish();
|
||||
}
|
||||
|
||||
var percentComplete = testCount * 100 / expectedTestCount;
|
||||
updateProgress(percentComplete);
|
||||
});
|
||||
function finish() {
|
||||
out.write(
|
||||
"# tests " + totalReportedTestCount + os.EOL +
|
||||
"# pass " + totalReportedPassCount + os.EOL +
|
||||
"# fail " + totalReportedFailCount + os.EOL);
|
||||
console.log("# tests " + totalReportedTestCount);
|
||||
console.log("# pass " + totalReportedPassCount);
|
||||
console.log("# fail " + totalReportedFailCount);
|
||||
|
||||
p.on("exit", function (status) {
|
||||
if (progress.visible) {
|
||||
updateProgress(100);
|
||||
process.stdout.write("done." + os.EOL);
|
||||
if (totalTypeErrorCount) {
|
||||
console.log("# type errors " + totalTypeErrorCount);
|
||||
}
|
||||
|
||||
console.log(comments.join(os.EOL));
|
||||
|
||||
if (typeErrorCount) {
|
||||
console.log("# type errors: %s", typeErrorCount);
|
||||
}
|
||||
|
||||
if (debugErrorCount) {
|
||||
console.log("# debug errors: %s", debugErrorCount);
|
||||
if (totalDebugErrorCount) {
|
||||
console.log("# debug errors " + totalDebugErrorCount);
|
||||
}
|
||||
|
||||
deleteTemporaryProjectOutput();
|
||||
if (status) {
|
||||
fail("Process exited with code " + status);
|
||||
if (totalReportedFailCount) {
|
||||
fail("Test failures reported: " + totalReportedFailCount);
|
||||
}
|
||||
else {
|
||||
complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function runConsoleTests(defaultReporter, runInParallel, dirty) {
|
||||
@@ -1014,8 +1138,23 @@ task("runtests", ["build-rules", "tests", builtLocalDirectory], function() {
|
||||
}, {async: true});
|
||||
|
||||
task("runtests-file", ["build-rules", "tests", builtLocalDirectory], function () {
|
||||
runTestsAndWriteOutput("tests/baselines/local/testresults.tap");
|
||||
var subsets = [];
|
||||
var cores = os.cpus().length;
|
||||
if (cores > 1) {
|
||||
subsets.push({ name: "conformance", pattern: "^conformance\\b" });
|
||||
subsets.push({ name: "compiler", pattern: "^compiler\\b" });
|
||||
subsets.push({ name: "projects", pattern: "^Projects\\b" });
|
||||
if (cores > 4) {
|
||||
subsets.push({ name: "fourslash", pattern: "^fourslash\\b" });
|
||||
subsets.push({ name: "fourslash (shims, shims-pp, server)", pattern: "^fourslash-" });
|
||||
}
|
||||
else {
|
||||
subsets.push({ name: "fourslash", pattern: "^fourslash" });
|
||||
}
|
||||
}
|
||||
runTestsAndWriteOutput("tests/baselines/local/testresults.tap", subsets);
|
||||
}, { async: true });
|
||||
|
||||
task("runtests-dirty", ["build-rules", "tests", builtLocalDirectory], function () {
|
||||
runConsoleTests("mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*dirty*/ true);
|
||||
}, { async: true });
|
||||
@@ -1301,8 +1440,9 @@ task("lint-server", ["build-rules"], function() {
|
||||
}
|
||||
});
|
||||
|
||||
function ProgressBar(title) {
|
||||
this.title = title;
|
||||
function ProgressBar() {
|
||||
this._progress = [];
|
||||
this._lineCount = 0;
|
||||
}
|
||||
ProgressBar.prototype = {
|
||||
progressChars: ["\u0020", "\u2591", "\u2592", "\u2593", "\u2588"],
|
||||
@@ -1331,7 +1471,9 @@ ProgressBar.prototype = {
|
||||
},
|
||||
reset: "\u001b[0m"
|
||||
},
|
||||
update: function (percentComplete, foregroundColor, backgroundColor) {
|
||||
update: function (percentComplete, foregroundColor, backgroundColor, title, index) {
|
||||
if (index === undefined) index = 0;
|
||||
|
||||
var progress = "";
|
||||
for (var i = 0; i < 100; i += 4) {
|
||||
progress += this.progressChars[Math.floor(Math.max(0, Math.min(4, percentComplete - i)))];
|
||||
@@ -1350,20 +1492,13 @@ ProgressBar.prototype = {
|
||||
progress += this.colors.reset;
|
||||
}
|
||||
|
||||
if (this._lastProgress !== progress || !this.visible) {
|
||||
this._print(progress);
|
||||
if (title) {
|
||||
progress += " " + title;
|
||||
}
|
||||
},
|
||||
hide: function () {
|
||||
if (this.visible) {
|
||||
this._savedProgress = this._lastProgress;
|
||||
this.clear();
|
||||
}
|
||||
},
|
||||
show: function () {
|
||||
if (this._savedProgress && !this.visible) {
|
||||
this._print(this._savedProgress);
|
||||
this._savedProgress = undefined;
|
||||
|
||||
if (this._progress[index] !== progress) {
|
||||
this._progress[index] = progress;
|
||||
this._print(index);
|
||||
}
|
||||
},
|
||||
clear: function () {
|
||||
@@ -1374,12 +1509,22 @@ ProgressBar.prototype = {
|
||||
this.visible = false;
|
||||
}
|
||||
},
|
||||
_print: function (progress) {
|
||||
readline.moveCursor(process.stdout, -process.stdout.columns, 0);
|
||||
process.stdout.write(this.title ? progress + " " + this.title : progress);
|
||||
readline.clearLine(process.stdout, 1);
|
||||
this._lastProgress = progress;
|
||||
this.visible = true;
|
||||
_print: function (index) {
|
||||
readline.moveCursor(process.stdout, -process.stdout.columns, -this._lineCount);
|
||||
var lineCount = 0;
|
||||
for (var i = 0; i < this._progress.length; i++) {
|
||||
if (i === index) {
|
||||
readline.clearLine(process.stdout, 1);
|
||||
process.stdout.write(this._progress[i] + os.EOL);
|
||||
}
|
||||
else {
|
||||
readline.moveCursor(process.stdout, -process.stdout.columns, +1);
|
||||
}
|
||||
|
||||
lineCount++;
|
||||
}
|
||||
|
||||
this._lineCount = lineCount;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+271
-285
@@ -5,311 +5,274 @@ namespace ts {
|
||||
export interface CommentWriter {
|
||||
reset(): void;
|
||||
setSourceFile(sourceFile: SourceFile): void;
|
||||
getLeadingComments(range: TextRange): CommentRange[];
|
||||
getLeadingComments(range: TextRange, contextNode: Node, ignoreNodeCallback: (contextNode: Node) => boolean, getTextRangeCallback: (contextNode: Node) => TextRange): CommentRange[];
|
||||
getTrailingComments(range: TextRange): CommentRange[];
|
||||
getTrailingComments(range: TextRange, contextNode: Node, ignoreNodeCallback: (contextNode: Node) => boolean, getTextRangeCallback: (contextNode: Node) => TextRange): CommentRange[];
|
||||
getTrailingCommentsOfPosition(pos: number): CommentRange[];
|
||||
emitLeadingComments(range: TextRange, comments: CommentRange[]): void;
|
||||
emitLeadingComments(range: TextRange, comments: CommentRange[], contextNode: Node, getTextRangeCallback: (contextNode: Node) => TextRange): void;
|
||||
emitTrailingComments(range: TextRange, comments: CommentRange[]): void;
|
||||
emitLeadingDetachedComments(range: TextRange): void;
|
||||
emitLeadingDetachedComments(range: TextRange, contextNode: Node, ignoreNodeCallback: (contextNode: Node) => boolean): void;
|
||||
emitTrailingDetachedComments(range: TextRange): void;
|
||||
emitTrailingDetachedComments(range: TextRange, contextNode: Node, ignoreNodeCallback: (contextNode: Node) => boolean): void;
|
||||
emitNodeWithComments(node: Node, emitCallback: (node: Node) => void): void;
|
||||
emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void;
|
||||
emitTrailingCommentsOfPosition(pos: number): void;
|
||||
}
|
||||
|
||||
export function createCommentWriter(host: EmitHost, writer: EmitTextWriter, sourceMap: SourceMapWriter): CommentWriter {
|
||||
const compilerOptions = host.getCompilerOptions();
|
||||
const extendedDiagnostics = compilerOptions.extendedDiagnostics;
|
||||
const newLine = host.getNewLine();
|
||||
const { emitPos } = sourceMap;
|
||||
|
||||
let containerPos = -1;
|
||||
let containerEnd = -1;
|
||||
let declarationListContainerEnd = -1;
|
||||
let currentSourceFile: SourceFile;
|
||||
let currentText: string;
|
||||
let currentLineMap: number[];
|
||||
let detachedCommentsInfo: { nodePos: number, detachedCommentEndPos: number}[];
|
||||
let hasWrittenComment = false;
|
||||
let hasLastComment: boolean;
|
||||
let lastCommentEnd: number;
|
||||
let disabled: boolean = compilerOptions.removeComments;
|
||||
|
||||
// This maps start->end for a comment range. See `hasConsumedCommentRange` and
|
||||
// `consumeCommentRange` for usage.
|
||||
let consumedCommentRanges: Map<number>;
|
||||
let leadingCommentRangePositions: Map<boolean>;
|
||||
let trailingCommentRangePositions: Map<boolean>;
|
||||
return {
|
||||
reset,
|
||||
setSourceFile,
|
||||
emitNodeWithComments,
|
||||
emitBodyWithDetachedComments,
|
||||
emitTrailingCommentsOfPosition,
|
||||
};
|
||||
|
||||
const commentWriter = compilerOptions.removeComments
|
||||
? createCommentRemovingWriter()
|
||||
: createCommentPreservingWriter();
|
||||
|
||||
return compilerOptions.extendedDiagnostics
|
||||
? createCommentWriterWithExtendedDiagnostics(commentWriter)
|
||||
: commentWriter;
|
||||
|
||||
function createCommentRemovingWriter(): CommentWriter {
|
||||
return {
|
||||
reset,
|
||||
setSourceFile,
|
||||
getLeadingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange): CommentRange[] { return undefined; },
|
||||
getTrailingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange): CommentRange[] { return undefined; },
|
||||
getTrailingCommentsOfPosition(pos: number): CommentRange[] { return undefined; },
|
||||
emitLeadingComments(range: TextRange, comments: CommentRange[], contextNode?: Node, getTextRangeCallback?: (contextNode: Node) => TextRange): void { },
|
||||
emitTrailingComments(range: TextRange, comments: CommentRange[]): void { },
|
||||
emitLeadingDetachedComments,
|
||||
emitTrailingDetachedComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean): void {}
|
||||
};
|
||||
|
||||
function emitLeadingDetachedComments(node: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean): void {
|
||||
if (ignoreNodeCallback && ignoreNodeCallback(contextNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
emitDetachedCommentsAndUpdateCommentsInfo(node, /*removeComments*/ true);
|
||||
}
|
||||
}
|
||||
|
||||
function createCommentPreservingWriter(): CommentWriter {
|
||||
const noComments: CommentRange[] = [];
|
||||
return {
|
||||
reset,
|
||||
setSourceFile,
|
||||
getLeadingComments,
|
||||
getTrailingComments,
|
||||
getTrailingCommentsOfPosition,
|
||||
emitLeadingComments,
|
||||
emitTrailingComments,
|
||||
emitLeadingDetachedComments,
|
||||
emitTrailingDetachedComments
|
||||
};
|
||||
|
||||
function getLeadingComments(range: TextRange): CommentRange[];
|
||||
function getLeadingComments(range: TextRange, contextNode: Node, ignoreNodeCallback: (contextNode: Node) => boolean, getTextRangeCallback: (contextNode: Node) => TextRange): CommentRange[];
|
||||
function getLeadingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange) {
|
||||
if (contextNode) {
|
||||
range = getTextRangeCallback(contextNode) || range;
|
||||
if (ignoreNodeCallback(contextNode)) {
|
||||
// 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
|
||||
// even though both nodes will not be emitted.
|
||||
if (range.pos === 0) {
|
||||
return filter(getLeadingCommentsOfPosition(0), isTripleSlashComment);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return getLeadingCommentsOfPosition(range.pos);
|
||||
function emitNodeWithComments(node: Node, emitCallback: (node: Node) => void) {
|
||||
if (disabled) {
|
||||
emitCallback(node);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given comment is a triple-slash
|
||||
**/
|
||||
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 (currentText.charCodeAt(comment.pos + 1) === CharacterCodes.slash &&
|
||||
comment.pos + 2 < comment.end &&
|
||||
currentText.charCodeAt(comment.pos + 2) === CharacterCodes.slash) {
|
||||
const textSubStr = currentText.substring(comment.pos, comment.end);
|
||||
return fullTripleSlashReferencePathRegEx.test(textSubStr)
|
||||
|| fullTripleSlashAMDReferencePathRegEx.test(textSubStr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getTrailingComments(range: TextRange): CommentRange[];
|
||||
function getTrailingComments(range: TextRange, contextNode: Node, ignoreNodeCallback: (contextNode: Node) => boolean, getTextRangeCallback: (contextNode: Node) => TextRange): CommentRange[];
|
||||
function getTrailingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange) {
|
||||
let ignored = false;
|
||||
if (contextNode) {
|
||||
if (ignoreNodeCallback(contextNode)) {
|
||||
ignored = true;
|
||||
if (node) {
|
||||
const { pos, end } = node.commentRange || node;
|
||||
const emitFlags = node.emitFlags;
|
||||
if ((pos < 0 && end < 0) || (pos === end)) {
|
||||
// Both pos and end are synthesized, so just emit the node without comments.
|
||||
if (emitFlags & NodeEmitFlags.NoNestedComments) {
|
||||
disableCommentsAndEmit(node, emitCallback);
|
||||
}
|
||||
else {
|
||||
range = getTextRangeCallback(contextNode) || range;
|
||||
emitCallback(node);
|
||||
}
|
||||
}
|
||||
|
||||
let comments: CommentRange[];
|
||||
if (!ignored) {
|
||||
comments = getTrailingCommentsOfPosition(range.end);
|
||||
}
|
||||
return comments;
|
||||
}
|
||||
|
||||
function getLeadingCommentsOfPosition(pos: number) {
|
||||
if (positionIsSynthesized(pos) || leadingCommentRangePositions[pos]) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
leadingCommentRangePositions[pos] = true;
|
||||
const comments = hasDetachedComments(pos)
|
||||
? getLeadingCommentsWithoutDetachedComments()
|
||||
: getLeadingCommentRanges(currentText, pos);
|
||||
return consumeCommentRanges(comments);
|
||||
}
|
||||
|
||||
function getTrailingCommentsOfPosition(pos: number) {
|
||||
if (positionIsSynthesized(pos) || trailingCommentRangePositions[pos]) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
trailingCommentRangePositions[pos] = true;
|
||||
const comments = getTrailingCommentRanges(currentText, pos);
|
||||
return consumeCommentRanges(comments);
|
||||
}
|
||||
|
||||
function emitLeadingComments(range: TextRange, comments: CommentRange[]): void;
|
||||
function emitLeadingComments(range: TextRange, comments: CommentRange[], contextNode: Node, getTextRangeCallback: (contextNode: Node) => TextRange): void;
|
||||
function emitLeadingComments(range: TextRange, comments: CommentRange[], contextNode?: Node, getTextRangeCallback?: (contextNode: Node) => TextRange) {
|
||||
if (comments && comments.length > 0) {
|
||||
if (contextNode) {
|
||||
range = getTextRangeCallback(contextNode) || range;
|
||||
else {
|
||||
let commentStart: number;
|
||||
if (extendedDiagnostics) {
|
||||
commentStart = performance.mark();
|
||||
}
|
||||
|
||||
emitNewLineBeforeLeadingComments(currentLineMap, writer, range, comments);
|
||||
const isEmittedNode = node.kind !== SyntaxKind.NotEmittedStatement;
|
||||
const skipLeadingComments = pos < 0 || (emitFlags & NodeEmitFlags.NoLeadingComments) !== 0;
|
||||
const skipTrailingComments = end < 0 || (emitFlags & NodeEmitFlags.NoTrailingComments) !== 0;
|
||||
|
||||
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
|
||||
emitComments(currentText, currentLineMap, writer, comments, /*leadingSeparator*/ false, /*trailingSeparator*/ true, newLine, writeComment);
|
||||
}
|
||||
}
|
||||
// Emit leading comments if the position is not synthesized and the node
|
||||
// has not opted out from emitting leading comments.
|
||||
if (!skipLeadingComments) {
|
||||
emitLeadingComments(pos, isEmittedNode);
|
||||
}
|
||||
|
||||
function emitTrailingComments(range: TextRange, comments: CommentRange[]) {
|
||||
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment*/
|
||||
emitComments(currentText, currentLineMap, writer, comments, /*leadingSeparator*/ true, /*trailingSeparator*/ false, newLine, writeComment);
|
||||
}
|
||||
// Save current container state on the stack.
|
||||
const savedContainerPos = containerPos;
|
||||
const savedContainerEnd = containerEnd;
|
||||
const savedDeclarationListContainerEnd = declarationListContainerEnd;
|
||||
|
||||
function emitLeadingDetachedComments(range: TextRange): void;
|
||||
function emitLeadingDetachedComments(range: TextRange, contextNode: Node, ignoreNodeCallback: (node: Node) => boolean): void;
|
||||
function emitLeadingDetachedComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean): void {
|
||||
if (contextNode && ignoreNodeCallback(contextNode)) {
|
||||
return;
|
||||
}
|
||||
if (!skipLeadingComments) {
|
||||
containerPos = pos;
|
||||
}
|
||||
|
||||
emitDetachedCommentsAndUpdateCommentsInfo(range, /*removeComments*/ false);
|
||||
}
|
||||
if (!skipTrailingComments) {
|
||||
containerEnd = end;
|
||||
|
||||
function emitTrailingDetachedComments(range: TextRange): void;
|
||||
function emitTrailingDetachedComments(range: TextRange, contextNode: Node, ignoreNodeCallback?: (node: Node) => boolean): void;
|
||||
function emitTrailingDetachedComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (node: Node) => boolean): void {
|
||||
if (contextNode && ignoreNodeCallback(contextNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
range = collapseRangeToEnd(range);
|
||||
emitLeadingComments(range, getLeadingComments(range));
|
||||
}
|
||||
|
||||
function hasConsumedCommentRange(comment: CommentRange) {
|
||||
return comment.end === consumedCommentRanges[comment.pos];
|
||||
}
|
||||
|
||||
function consumeCommentRange(comment: CommentRange) {
|
||||
if (!hasConsumedCommentRange(comment)) {
|
||||
consumedCommentRanges[comment.pos] = comment.end;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function consumeCommentRanges(comments: CommentRange[]) {
|
||||
let consumed: CommentRange[];
|
||||
if (comments) {
|
||||
let commentsSkipped = 0;
|
||||
let commentsConsumed = 0;
|
||||
for (let i = 0; i < comments.length; i++) {
|
||||
const comment = comments[i];
|
||||
if (consumeCommentRange(comment)) {
|
||||
commentsConsumed++;
|
||||
if (commentsSkipped !== 0) {
|
||||
if (consumed === undefined) {
|
||||
consumed = [comment];
|
||||
}
|
||||
else {
|
||||
consumed.push(comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
commentsSkipped++;
|
||||
if (commentsConsumed !== 0 && consumed === undefined) {
|
||||
consumed = comments.slice(0, i);
|
||||
}
|
||||
// To avoid invalid comment emit in a down-level binding pattern, we
|
||||
// keep track of the last declaration list container's end
|
||||
if (node.kind === SyntaxKind.VariableDeclarationList) {
|
||||
declarationListContainerEnd = end;
|
||||
}
|
||||
}
|
||||
|
||||
if (commentsConsumed) {
|
||||
return consumed || comments;
|
||||
if (extendedDiagnostics) {
|
||||
performance.measure("commentTime", commentStart);
|
||||
}
|
||||
|
||||
if (emitFlags & NodeEmitFlags.NoNestedComments) {
|
||||
disableCommentsAndEmit(node, emitCallback);
|
||||
}
|
||||
else {
|
||||
emitCallback(node);
|
||||
}
|
||||
|
||||
if (extendedDiagnostics) {
|
||||
commentStart = performance.mark();
|
||||
}
|
||||
|
||||
// Restore previous container state.
|
||||
containerPos = savedContainerPos;
|
||||
containerEnd = savedContainerEnd;
|
||||
declarationListContainerEnd = savedDeclarationListContainerEnd;
|
||||
|
||||
// Emit trailing comments if the position is not synthesized and the node
|
||||
// has not opted out from emitting leading comments and is an emitted node.
|
||||
if (!skipTrailingComments && isEmittedNode) {
|
||||
emitTrailingComments(end);
|
||||
}
|
||||
|
||||
if (extendedDiagnostics) {
|
||||
performance.measure("commentTime", commentStart);
|
||||
}
|
||||
}
|
||||
|
||||
return noComments;
|
||||
}
|
||||
}
|
||||
|
||||
function createCommentWriterWithExtendedDiagnostics(writer: CommentWriter): CommentWriter {
|
||||
const {
|
||||
reset,
|
||||
setSourceFile,
|
||||
getLeadingComments,
|
||||
getTrailingComments,
|
||||
getTrailingCommentsOfPosition,
|
||||
emitLeadingComments,
|
||||
emitTrailingComments,
|
||||
emitLeadingDetachedComments,
|
||||
emitTrailingDetachedComments
|
||||
} = writer;
|
||||
function emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void) {
|
||||
let commentStart: number;
|
||||
if (extendedDiagnostics) {
|
||||
commentStart = performance.mark();
|
||||
}
|
||||
|
||||
return {
|
||||
reset,
|
||||
setSourceFile,
|
||||
getLeadingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange): CommentRange[] {
|
||||
const commentStart = performance.mark();
|
||||
const comments = getLeadingComments(range, contextNode, ignoreNodeCallback, getTextRangeCallback);
|
||||
performance.measure("commentTime", commentStart);
|
||||
return comments;
|
||||
},
|
||||
getTrailingComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean, getTextRangeCallback?: (contextNode: Node) => TextRange): CommentRange[] {
|
||||
const commentStart = performance.mark();
|
||||
const comments = getTrailingComments(range, contextNode, ignoreNodeCallback, getTextRangeCallback);
|
||||
performance.measure("commentTime", commentStart);
|
||||
return comments;
|
||||
},
|
||||
getTrailingCommentsOfPosition(pos: number): CommentRange[] {
|
||||
const commentStart = performance.mark();
|
||||
const comments = getTrailingCommentsOfPosition(pos);
|
||||
performance.measure("commentTime", commentStart);
|
||||
return comments;
|
||||
},
|
||||
emitLeadingComments(range: TextRange, comments: CommentRange[], contextNode?: Node, getTextRangeCallback?: (contextNode: Node) => TextRange): void {
|
||||
const commentStart = performance.mark();
|
||||
emitLeadingComments(range, comments, contextNode, getTextRangeCallback);
|
||||
performance.measure("commentTime", commentStart);
|
||||
},
|
||||
emitTrailingComments(range: TextRange, comments: CommentRange[]): void {
|
||||
const commentStart = performance.mark();
|
||||
emitTrailingComments(range, comments);
|
||||
performance.measure("commentTime", commentStart);
|
||||
},
|
||||
emitLeadingDetachedComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean): void {
|
||||
const commentStart = performance.mark();
|
||||
emitLeadingDetachedComments(range, contextNode, ignoreNodeCallback);
|
||||
performance.measure("commentTime", commentStart);
|
||||
},
|
||||
emitTrailingDetachedComments(range: TextRange, contextNode?: Node, ignoreNodeCallback?: (contextNode: Node) => boolean): void {
|
||||
const commentStart = performance.mark();
|
||||
emitTrailingDetachedComments(range, contextNode, ignoreNodeCallback);
|
||||
performance.measure("commentTime", commentStart);
|
||||
const { pos, end } = detachedRange;
|
||||
const emitFlags = node.emitFlags;
|
||||
const skipLeadingComments = pos < 0 || (emitFlags & NodeEmitFlags.NoLeadingComments) !== 0;
|
||||
const skipTrailingComments = disabled || end < 0 || (emitFlags & NodeEmitFlags.NoTrailingComments) !== 0;
|
||||
|
||||
if (!skipLeadingComments) {
|
||||
emitDetachedCommentsAndUpdateCommentsInfo(detachedRange);
|
||||
}
|
||||
|
||||
if (extendedDiagnostics) {
|
||||
performance.measure("commentTime", commentStart);
|
||||
}
|
||||
|
||||
if (emitFlags & NodeEmitFlags.NoNestedComments) {
|
||||
disableCommentsAndEmit(node, emitCallback);
|
||||
}
|
||||
else {
|
||||
emitCallback(node);
|
||||
}
|
||||
|
||||
if (extendedDiagnostics) {
|
||||
commentStart = performance.mark();
|
||||
}
|
||||
|
||||
if (!skipTrailingComments) {
|
||||
emitLeadingComments(detachedRange.end, /*isEmittedNode*/ true);
|
||||
}
|
||||
|
||||
if (extendedDiagnostics) {
|
||||
performance.measure("commentTime", commentStart);
|
||||
}
|
||||
}
|
||||
|
||||
function emitLeadingComments(pos: number, isEmittedNode: boolean) {
|
||||
hasWrittenComment = false;
|
||||
|
||||
if (isEmittedNode) {
|
||||
forEachLeadingCommentToEmit(pos, emitLeadingComment);
|
||||
}
|
||||
else if (pos === 0) {
|
||||
// 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 even though both node will not be emitted
|
||||
forEachLeadingCommentToEmit(pos, emitTripleSlashLeadingComment);
|
||||
}
|
||||
}
|
||||
|
||||
function emitTripleSlashLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
|
||||
if (isTripleSlashComment(commentPos, commentEnd)) {
|
||||
emitLeadingComment(commentPos, commentEnd, kind, hasTrailingNewLine, rangePos);
|
||||
}
|
||||
}
|
||||
|
||||
function emitLeadingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) {
|
||||
if (!hasWrittenComment) {
|
||||
emitNewLineBeforeLeadingCommentOfPosition(currentLineMap, writer, rangePos, commentPos);
|
||||
hasWrittenComment = true;
|
||||
}
|
||||
|
||||
// Leading comments are emitted at /*leading comment1 */space/*leading comment*/space
|
||||
emitPos(commentPos);
|
||||
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
|
||||
emitPos(commentEnd);
|
||||
|
||||
if (hasTrailingNewLine) {
|
||||
writer.writeLine();
|
||||
}
|
||||
else {
|
||||
writer.write(" ");
|
||||
}
|
||||
}
|
||||
|
||||
function emitTrailingComments(pos: number) {
|
||||
forEachTrailingCommentToEmit(pos, emitTrailingComment);
|
||||
}
|
||||
|
||||
function emitTrailingComment(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) {
|
||||
// trailing comments are emitted at space/*trailing comment1 */space/*trailing comment2*/
|
||||
if (!writer.isAtStartOfLine()) {
|
||||
writer.write(" ");
|
||||
}
|
||||
|
||||
emitPos(commentPos);
|
||||
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
|
||||
emitPos(commentEnd);
|
||||
|
||||
if (hasTrailingNewLine) {
|
||||
writer.writeLine();
|
||||
}
|
||||
}
|
||||
|
||||
function emitTrailingCommentsOfPosition(pos: number) {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let commentStart: number;
|
||||
if (extendedDiagnostics) {
|
||||
commentStart = performance.mark();
|
||||
}
|
||||
|
||||
forEachTrailingCommentToEmit(pos, emitTrailingCommentOfPosition);
|
||||
|
||||
if (extendedDiagnostics) {
|
||||
performance.measure("commentTime", commentStart);
|
||||
}
|
||||
}
|
||||
|
||||
function emitTrailingCommentOfPosition(commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) {
|
||||
// trailing comments of a position are emitted at /*trailing comment1 */space/*trailing comment*/space
|
||||
|
||||
emitPos(commentPos);
|
||||
writeCommentRange(currentText, currentLineMap, writer, commentPos, commentEnd, newLine);
|
||||
emitPos(commentEnd);
|
||||
|
||||
if (hasTrailingNewLine) {
|
||||
writer.writeLine();
|
||||
}
|
||||
else {
|
||||
writer.write(" ");
|
||||
}
|
||||
}
|
||||
|
||||
function forEachLeadingCommentToEmit(pos: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
|
||||
// Emit the leading comments only if the container's pos doesn't match because the container should take care of emitting these comments
|
||||
if (containerPos === -1 || pos !== containerPos) {
|
||||
if (hasDetachedComments(pos)) {
|
||||
forEachLeadingCommentWithoutDetachedComments(cb);
|
||||
}
|
||||
};
|
||||
else {
|
||||
forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function forEachTrailingCommentToEmit(end: number, cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean) => void) {
|
||||
// Emit the trailing comments only if the container's end doesn't match because the container should take care of emitting these comments
|
||||
if (containerEnd === -1 || (end !== containerEnd && end !== declarationListContainerEnd)) {
|
||||
forEachTrailingCommentRange(currentText, end, cb);
|
||||
}
|
||||
}
|
||||
|
||||
function reset() {
|
||||
@@ -317,9 +280,6 @@ namespace ts {
|
||||
currentText = undefined;
|
||||
currentLineMap = undefined;
|
||||
detachedCommentsInfo = undefined;
|
||||
consumedCommentRanges = undefined;
|
||||
trailingCommentRangePositions = undefined;
|
||||
leadingCommentRangePositions = undefined;
|
||||
}
|
||||
|
||||
function setSourceFile(sourceFile: SourceFile) {
|
||||
@@ -327,19 +287,27 @@ namespace ts {
|
||||
currentText = currentSourceFile.text;
|
||||
currentLineMap = getLineStarts(currentSourceFile);
|
||||
detachedCommentsInfo = undefined;
|
||||
consumedCommentRanges = {};
|
||||
leadingCommentRangePositions = {};
|
||||
trailingCommentRangePositions = {};
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
function disableCommentsAndEmit(node: Node, emitCallback: (node: Node) => void): void {
|
||||
if (disabled) {
|
||||
emitCallback(node);
|
||||
}
|
||||
else {
|
||||
disabled = true;
|
||||
emitCallback(node);
|
||||
disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function hasDetachedComments(pos: number) {
|
||||
return detachedCommentsInfo !== undefined && lastOrUndefined(detachedCommentsInfo).nodePos === pos;
|
||||
}
|
||||
|
||||
function getLeadingCommentsWithoutDetachedComments() {
|
||||
function forEachLeadingCommentWithoutDetachedComments(cb: (commentPos: number, commentEnd: number, kind: SyntaxKind, hasTrailingNewLine: boolean, rangePos: number) => void) {
|
||||
// get the leading comments from detachedPos
|
||||
const pos = lastOrUndefined(detachedCommentsInfo).detachedCommentEndPos;
|
||||
const leadingComments = getLeadingCommentRanges(currentText, pos);
|
||||
if (detachedCommentsInfo.length - 1) {
|
||||
detachedCommentsInfo.pop();
|
||||
}
|
||||
@@ -347,12 +315,11 @@ namespace ts {
|
||||
detachedCommentsInfo = undefined;
|
||||
}
|
||||
|
||||
return leadingComments;
|
||||
forEachLeadingCommentRange(currentText, pos, cb, /*state*/ pos);
|
||||
}
|
||||
|
||||
function emitDetachedCommentsAndUpdateCommentsInfo(node: TextRange, removeComments: boolean) {
|
||||
const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, node, newLine, removeComments);
|
||||
|
||||
function emitDetachedCommentsAndUpdateCommentsInfo(range: TextRange) {
|
||||
const currentDetachedCommentInfo = emitDetachedComments(currentText, currentLineMap, writer, writeComment, range, newLine, disabled);
|
||||
if (currentDetachedCommentInfo) {
|
||||
if (detachedCommentsInfo) {
|
||||
detachedCommentsInfo.push(currentDetachedCommentInfo);
|
||||
@@ -363,10 +330,29 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) {
|
||||
emitPos(comment.pos);
|
||||
writeCommentRange(text, lineMap, writer, comment, newLine);
|
||||
emitPos(comment.end);
|
||||
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
|
||||
emitPos(commentPos);
|
||||
writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
|
||||
emitPos(commentEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given comment is a triple-slash
|
||||
*
|
||||
* @return true if the comment is a triple-slash comment else false
|
||||
**/
|
||||
function isTripleSlashComment(commentPos: number, commentEnd: number) {
|
||||
// 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 (currentText.charCodeAt(commentPos + 1) === CharacterCodes.slash &&
|
||||
commentPos + 2 < commentEnd &&
|
||||
currentText.charCodeAt(commentPos + 2) === CharacterCodes.slash) {
|
||||
const textSubStr = currentText.substring(commentPos, commentEnd);
|
||||
return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
|
||||
textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ?
|
||||
true : false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8238,10 +8238,10 @@ const _super = (function (geti, seti) {
|
||||
}
|
||||
}
|
||||
|
||||
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) {
|
||||
emitPos(comment.pos);
|
||||
writeCommentRange(text, lineMap, writer, comment, newLine);
|
||||
emitPos(comment.end);
|
||||
function writeComment(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
|
||||
emitPos(commentPos);
|
||||
writeCommentRange(text, lineMap, writer, commentPos, commentEnd, newLine);
|
||||
emitPos(commentEnd);
|
||||
}
|
||||
|
||||
function emitShebang() {
|
||||
|
||||
@@ -20,17 +20,12 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
function updateNode<T extends Node>(updated: T, original: T): T {
|
||||
updated.original = original;
|
||||
if (original.transformId) {
|
||||
updated.transformId = original.transformId;
|
||||
updated.emitFlags = original.emitFlags;
|
||||
updated.commentRange = original.commentRange;
|
||||
updated.sourceMapRange = original.sourceMapRange;
|
||||
}
|
||||
export function updateNode<T extends Node>(updated: T, original: T): T {
|
||||
setOriginalNode(updated, original);
|
||||
if (original.startsOnNewLine) {
|
||||
updated.startsOnNewLine = true;
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
@@ -1887,6 +1882,12 @@ namespace ts {
|
||||
|
||||
export function setOriginalNode<T extends Node>(node: T, original: Node): T {
|
||||
node.original = original;
|
||||
if (original) {
|
||||
const { emitFlags, commentRange, sourceMapRange } = original;
|
||||
if (emitFlags) node.emitFlags = emitFlags;
|
||||
if (commentRange) node.commentRange = commentRange;
|
||||
if (sourceMapRange) node.sourceMapRange = sourceMapRange;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
+80
-58
@@ -161,13 +161,9 @@ const _super = (function (geti, seti) {
|
||||
|
||||
const comments = createCommentWriter(host, writer, sourceMap);
|
||||
const {
|
||||
getLeadingComments,
|
||||
getTrailingComments,
|
||||
getTrailingCommentsOfPosition,
|
||||
emitLeadingComments,
|
||||
emitTrailingComments,
|
||||
emitLeadingDetachedComments,
|
||||
emitTrailingDetachedComments
|
||||
emitNodeWithComments,
|
||||
emitBodyWithDetachedComments,
|
||||
emitTrailingCommentsOfPosition
|
||||
} = comments;
|
||||
|
||||
let context: TransformationContext;
|
||||
@@ -175,7 +171,6 @@ const _super = (function (geti, seti) {
|
||||
let setNodeEmitFlags: (node: Node, flags: NodeEmitFlags) => void;
|
||||
let getSourceMapRange: (node: Node) => TextRange;
|
||||
let getTokenSourceMapRange: (node: Node, token: SyntaxKind) => TextRange;
|
||||
let getCommentRange: (node: Node) => TextRange;
|
||||
let isSubstitutionEnabled: (node: Node) => boolean;
|
||||
let isEmitNotificationEnabled: (node: Node) => boolean;
|
||||
let onSubstituteNode: (node: Node, isExpression: boolean) => Node;
|
||||
@@ -240,7 +235,6 @@ const _super = (function (geti, seti) {
|
||||
setNodeEmitFlags = undefined;
|
||||
getSourceMapRange = undefined;
|
||||
getTokenSourceMapRange = undefined;
|
||||
getCommentRange = undefined;
|
||||
isSubstitutionEnabled = undefined;
|
||||
isEmitNotificationEnabled = undefined;
|
||||
onSubstituteNode = undefined;
|
||||
@@ -262,7 +256,6 @@ const _super = (function (geti, seti) {
|
||||
setNodeEmitFlags = context.setNodeEmitFlags;
|
||||
getSourceMapRange = context.getSourceMapRange;
|
||||
getTokenSourceMapRange = context.getTokenSourceMapRange;
|
||||
getCommentRange = context.getCommentRange;
|
||||
isSubstitutionEnabled = context.isSubstitutionEnabled;
|
||||
isEmitNotificationEnabled = context.isEmitNotificationEnabled;
|
||||
onSubstituteNode = context.onSubstituteNode;
|
||||
@@ -276,7 +269,7 @@ const _super = (function (geti, seti) {
|
||||
currentFileIdentifiers = node.identifiers;
|
||||
sourceMap.setSourceFile(node);
|
||||
comments.setSourceFile(node);
|
||||
emitNodeWithNotificationOption(node, emitWorker);
|
||||
emitNodeWithNotification(node, emitWorker);
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -291,7 +284,7 @@ const _super = (function (geti, seti) {
|
||||
* Emits a node.
|
||||
*/
|
||||
function emit(node: Node) {
|
||||
emitNodeWithNotificationOption(node, emitWithoutNotificationOption);
|
||||
emitNodeWithNotification(node, emitWithComments);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,51 +308,71 @@ const _super = (function (geti, seti) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node without calling onEmitNode.
|
||||
* NOTE: Do not call this method directly.
|
||||
* Emits a node with comments.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from emit.
|
||||
*/
|
||||
function emitWithoutNotificationOption(node: Node) {
|
||||
emitNodeWithWorker(node, emitWorker);
|
||||
function emitWithComments(node: Node) {
|
||||
emitNodeWithComments(node, emitWithSourceMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node with source maps.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emit pipeline
|
||||
* and should only be called indirectly from emitWithComments.
|
||||
*/
|
||||
function emitWithSourceMap(node: Node) {
|
||||
emitNodeWithSourceMap(node, emitWorker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an expression node.
|
||||
*/
|
||||
function emitExpression(node: Expression) {
|
||||
emitNodeWithNotificationOption(node, emitExpressionWithoutNotificationOption);
|
||||
emitNodeWithNotification(node, emitExpressionWithComments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an expression without calling onEmitNode.
|
||||
* NOTE: Do not call this method directly.
|
||||
* Emits an expression with comments.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emitExpression pipeline
|
||||
* and should only be called indirectly from emitExpression.
|
||||
*/
|
||||
function emitExpressionWithoutNotificationOption(node: Expression) {
|
||||
emitNodeWithWorker(node, emitExpressionWorker);
|
||||
function emitExpressionWithComments(node: Expression) {
|
||||
emitNodeWithComments(node, emitExpressionWithSourceMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits an expression with source maps.
|
||||
*
|
||||
* NOTE: Do not call this method directly. It is part of the emitExpression pipeline
|
||||
* and should only be called indirectly from emitExpressionWithComments.
|
||||
*/
|
||||
function emitExpressionWithSourceMap(node: Expression) {
|
||||
emitNodeWithSourceMap(node, emitExpressionWorker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a node with emit notification if available.
|
||||
*/
|
||||
function emitNodeWithNotificationOption(node: Node, emit: (node: Node) => void) {
|
||||
function emitNodeWithNotification(node: Node, emitCallback: (node: Node) => void) {
|
||||
if (node) {
|
||||
if (isEmitNotificationEnabled(node)) {
|
||||
onEmitNode(node, emit);
|
||||
onEmitNode(node, emitCallback);
|
||||
}
|
||||
else {
|
||||
emit(node);
|
||||
emitCallback(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function emitNodeWithWorker(node: Node, emitWorker: (node: Node) => void) {
|
||||
function emitNodeWithSourceMap(node: Node, emitCallback: (node: Node) => void) {
|
||||
if (node) {
|
||||
const leadingComments = getLeadingComments(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingCommentsForNode, getCommentRange);
|
||||
const trailingComments = getTrailingComments(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingCommentsForNode, getCommentRange);
|
||||
emitLeadingComments(/*range*/ node, leadingComments, /*contextNode*/ node, getCommentRange);
|
||||
emitStart(/*range*/ node, /*contextNode*/ node, shouldSkipLeadingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
|
||||
emitWorker(node);
|
||||
emitCallback(node);
|
||||
emitEnd(/*range*/ node, /*contextNode*/ node, shouldSkipTrailingSourceMapForNode, shouldSkipSourceMapForChildren, getSourceMapRange);
|
||||
emitTrailingComments(node, trailingComments);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1642,13 +1655,26 @@ const _super = (function (geti, seti) {
|
||||
}
|
||||
|
||||
increaseIndent();
|
||||
emitLeadingDetachedComments(body.statements, body, shouldSkipLeadingCommentsForNode);
|
||||
|
||||
emitBodyWithDetachedComments(body, body.statements,
|
||||
shouldEmitBlockFunctionBodyOnSingleLine(parentNode, body)
|
||||
? emitBlockFunctionBodyOnSingleLine
|
||||
: emitBlockFunctionBodyWorker);
|
||||
|
||||
decreaseIndent();
|
||||
writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body);
|
||||
}
|
||||
|
||||
function emitBlockFunctionBodyOnSingleLine(body: Block) {
|
||||
emitBlockFunctionBodyWorker(body, /*emitBlockFunctionBodyOnSingleLine*/ true);
|
||||
}
|
||||
|
||||
function emitBlockFunctionBodyWorker(body: Block, emitBlockFunctionBodyOnSingleLine?: boolean) {
|
||||
// Emit all the prologue directives (like "use strict").
|
||||
const statementOffset = emitPrologueDirectives(body.statements, /*startWithNewLine*/ true);
|
||||
const helpersEmitted = emitHelpers(body);
|
||||
|
||||
if (statementOffset === 0 && !helpersEmitted && shouldEmitBlockFunctionBodyOnSingleLine(parentNode, body)) {
|
||||
if (statementOffset === 0 && !helpersEmitted && emitBlockFunctionBodyOnSingleLine) {
|
||||
decreaseIndent();
|
||||
emitList(body, body.statements, ListFormat.SingleLineFunctionBodyStatements);
|
||||
increaseIndent();
|
||||
@@ -1656,10 +1682,6 @@ const _super = (function (geti, seti) {
|
||||
else {
|
||||
emitList(body, body.statements, ListFormat.MultiLineFunctionBodyStatements, statementOffset);
|
||||
}
|
||||
|
||||
emitTrailingDetachedComments(body.statements, body, shouldSkipTrailingCommentsForNode);
|
||||
decreaseIndent();
|
||||
writeToken(SyntaxKind.CloseBraceToken, body.statements.end, body);
|
||||
}
|
||||
|
||||
function emitClassDeclaration(node: ClassDeclaration) {
|
||||
@@ -2004,9 +2026,12 @@ const _super = (function (geti, seti) {
|
||||
// }
|
||||
// "comment1" is not considered to be leading comment for node.initializer
|
||||
// but rather a trailing comment on the previous node.
|
||||
if (!shouldSkipLeadingCommentsForNode(node.initializer)) {
|
||||
emitLeadingComments(/*range*/ node.initializer, getTrailingComments(collapseRangeToStart(node.initializer)), /*contextNode*/ node.initializer, getCommentRange);
|
||||
const initializer = node.initializer;
|
||||
if (!shouldSkipLeadingCommentsForNode(initializer)) {
|
||||
const commentRange = initializer.commentRange || initializer;
|
||||
emitTrailingCommentsOfPosition(commentRange.pos);
|
||||
}
|
||||
|
||||
emitExpression(node.initializer);
|
||||
}
|
||||
|
||||
@@ -2034,23 +2059,17 @@ const _super = (function (geti, seti) {
|
||||
function emitSourceFile(node: SourceFile) {
|
||||
writeLine();
|
||||
emitShebang();
|
||||
emitLeadingDetachedComments(node);
|
||||
emitBodyWithDetachedComments(node, node.statements, emitSourceFileWorker);
|
||||
}
|
||||
|
||||
function emitSourceFileWorker(node: SourceFile) {
|
||||
const statements = node.statements;
|
||||
const statementOffset = emitPrologueDirectives(statements);
|
||||
if (getNodeEmitFlags(node) & NodeEmitFlags.NoLexicalEnvironment) {
|
||||
emitHelpers(node);
|
||||
emitList(node, statements, ListFormat.MultiLine, statementOffset);
|
||||
}
|
||||
else {
|
||||
const savedTempFlags = tempFlags;
|
||||
tempFlags = 0;
|
||||
emitHelpers(node);
|
||||
emitList(node, statements, ListFormat.MultiLine, statementOffset);
|
||||
tempFlags = savedTempFlags;
|
||||
}
|
||||
|
||||
emitTrailingDetachedComments(node.statements);
|
||||
const savedTempFlags = tempFlags;
|
||||
tempFlags = 0;
|
||||
emitHelpers(node);
|
||||
emitList(node, statements, ListFormat.MultiLine, statementOffset);
|
||||
tempFlags = savedTempFlags;
|
||||
}
|
||||
|
||||
// Transformation nodes
|
||||
@@ -2326,7 +2345,8 @@ const _super = (function (geti, seti) {
|
||||
}
|
||||
else {
|
||||
// Write the opening line terminator or leading whitespace.
|
||||
let shouldEmitInterveningComments = true;
|
||||
const mayEmitInterveningComments = (format & ListFormat.NoInterveningComments) === 0;
|
||||
let shouldEmitInterveningComments = mayEmitInterveningComments;
|
||||
if (shouldWriteLeadingLineTerminator(parentNode, children, format)) {
|
||||
writeLine();
|
||||
shouldEmitInterveningComments = false;
|
||||
@@ -2369,10 +2389,11 @@ const _super = (function (geti, seti) {
|
||||
}
|
||||
|
||||
if (shouldEmitInterveningComments) {
|
||||
emitLeadingComments(/*node*/ child, getTrailingCommentsOfPosition(child.pos), /*contextNode*/ child, getCommentRange);
|
||||
const commentRange = child.commentRange || child;
|
||||
emitTrailingCommentsOfPosition(commentRange.pos);
|
||||
}
|
||||
else {
|
||||
shouldEmitInterveningComments = true;
|
||||
shouldEmitInterveningComments = mayEmitInterveningComments;
|
||||
}
|
||||
|
||||
// Emit this child.
|
||||
@@ -2876,6 +2897,7 @@ const _super = (function (geti, seti) {
|
||||
// Other
|
||||
PreferNewLine = 1 << 15, // Prefer adding a LineTerminator between synthesized nodes.
|
||||
NoTrailingNewLine = 1 << 16, // Do not emit a trailing NewLine for a MultiLine list.
|
||||
NoInterveningComments = 1 << 17,// Do not emit comments between each node
|
||||
|
||||
// Precomputed Formats
|
||||
Modifiers = SingleLine | SpaceBetweenSiblings,
|
||||
@@ -2890,7 +2912,7 @@ const _super = (function (geti, seti) {
|
||||
ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets,
|
||||
CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis,
|
||||
NewExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis | OptionalIfUndefined,
|
||||
TemplateExpressionSpans = SingleLine,
|
||||
TemplateExpressionSpans = SingleLine | NoInterveningComments,
|
||||
SingleLineBlockStatements = SpaceBetweenBraces | SpaceBetweenSiblings | SingleLine,
|
||||
MultiLineBlockStatements = Indented | MultiLine,
|
||||
VariableDeclarationList = CommaDelimited | SpaceBetweenSiblings | SingleLine,
|
||||
@@ -2902,8 +2924,8 @@ const _super = (function (geti, seti) {
|
||||
EnumMembers = CommaDelimited | Indented | MultiLine,
|
||||
CaseBlockClauses = Indented | MultiLine,
|
||||
NamedImportsOrExportsElements = CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | SingleLine | SpaceBetweenBraces,
|
||||
JsxElementChildren = SingleLine,
|
||||
JsxElementAttributes = SingleLine | SpaceBetweenSiblings,
|
||||
JsxElementChildren = SingleLine | NoInterveningComments,
|
||||
JsxElementAttributes = SingleLine | SpaceBetweenSiblings | NoInterveningComments,
|
||||
CaseOrDefaultClauseStatements = Indented | MultiLine | NoTrailingNewLine | OptionalIfEmpty,
|
||||
HeritageClauseTypes = CommaDelimited | SpaceBetweenSiblings | SingleLine,
|
||||
SourceFileStatements = MultiLine | NoTrailingNewLine,
|
||||
|
||||
+81
-26
@@ -584,20 +584,34 @@ namespace ts {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract comments from text prefixing the token closest following `pos`.
|
||||
* The return value is an array containing a TextRange for each comment.
|
||||
* Single-line comment ranges include the beginning '//' characters but not the ending line break.
|
||||
* Multi - line comment ranges include the beginning '/* and ending '<asterisk>/' characters.
|
||||
* The return value is undefined if no comments were found.
|
||||
* @param trailing
|
||||
* If false, whitespace is skipped until the first line break and comments between that location
|
||||
* and the next token are returned.
|
||||
* If true, comments occurring between the given position and the next line break are returned.
|
||||
* Invokes a callback for each comment range following the provided position.
|
||||
*
|
||||
* Single-line comment ranges include the leading double-slash characters but not the ending
|
||||
* line break. Multi-line comment ranges include the leading slash-asterisk and trailing
|
||||
* asterisk-slash characters.
|
||||
*
|
||||
* @param reduce If true, accumulates the result of calling the callback in a fashion similar
|
||||
* to reduceLeft. If false, iteration stops when the callback returns a truthy value.
|
||||
* @param text The source text to scan.
|
||||
* @param pos The position at which to start scanning.
|
||||
* @param trailing If false, whitespace is skipped until the first line break and comments
|
||||
* between that location and the next token are returned. If true, comments occurring
|
||||
* between the given position and the next line break are returned.
|
||||
* @param cb The callback to execute as each comment range is encountered.
|
||||
* @param state A state value to pass to each iteration of the callback.
|
||||
* @param initial An initial value to pass when accumulating results (when "reduce" is true).
|
||||
* @returns If "reduce" is true, the accumulated value. If "reduce" is false, the first truthy
|
||||
* return value of the callback.
|
||||
*/
|
||||
function getCommentRanges(text: string, pos: number, trailing: boolean): CommentRange[] {
|
||||
let result: CommentRange[];
|
||||
function iterateCommentRanges<T, U>(reduce: boolean, text: string, pos: number, trailing: boolean, cb: (pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: T, memo: U) => U, state: T, initial?: U): U {
|
||||
let pendingPos: number;
|
||||
let pendingEnd: number;
|
||||
let pendingKind: SyntaxKind;
|
||||
let pendingHasTrailingNewLine: boolean;
|
||||
let hasPendingCommentRange = false;
|
||||
let collecting = trailing || pos === 0;
|
||||
while (pos >= 0 && pos < text.length) {
|
||||
let accumulator = initial;
|
||||
scan: while (pos >= 0 && pos < text.length) {
|
||||
const ch = text.charCodeAt(pos);
|
||||
switch (ch) {
|
||||
case CharacterCodes.carriageReturn:
|
||||
@@ -607,12 +621,14 @@ namespace ts {
|
||||
case CharacterCodes.lineFeed:
|
||||
pos++;
|
||||
if (trailing) {
|
||||
return result;
|
||||
break scan;
|
||||
}
|
||||
|
||||
collecting = true;
|
||||
if (result && result.length) {
|
||||
lastOrUndefined(result).hasTrailingNewLine = true;
|
||||
if (hasPendingCommentRange) {
|
||||
pendingHasTrailingNewLine = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
case CharacterCodes.tab:
|
||||
case CharacterCodes.verticalTab:
|
||||
@@ -647,37 +663,76 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (collecting) {
|
||||
if (!result) {
|
||||
result = [];
|
||||
if (hasPendingCommentRange) {
|
||||
accumulator = cb(pendingPos, pendingEnd, pendingKind, pendingHasTrailingNewLine, state, accumulator);
|
||||
if (!reduce && accumulator) {
|
||||
// If we are not reducing and we have a truthy result, return it.
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
hasPendingCommentRange = false;
|
||||
}
|
||||
|
||||
result.push({ pos: startPos, end: pos, hasTrailingNewLine, kind });
|
||||
pendingPos = startPos;
|
||||
pendingEnd = pos;
|
||||
pendingKind = kind;
|
||||
pendingHasTrailingNewLine = hasTrailingNewLine;
|
||||
hasPendingCommentRange = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
break scan;
|
||||
default:
|
||||
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpace(ch) || isLineBreak(ch))) {
|
||||
if (result && result.length && isLineBreak(ch)) {
|
||||
lastOrUndefined(result).hasTrailingNewLine = true;
|
||||
if (hasPendingCommentRange && isLineBreak(ch)) {
|
||||
pendingHasTrailingNewLine = true;
|
||||
}
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
break scan;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
if (hasPendingCommentRange) {
|
||||
accumulator = cb(pendingPos, pendingEnd, pendingKind, pendingHasTrailingNewLine, state, accumulator);
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}
|
||||
|
||||
export function forEachLeadingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: T) => U, state?: T) {
|
||||
return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ false, cb, state);
|
||||
}
|
||||
|
||||
export function forEachTrailingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: T) => U, state?: T) {
|
||||
return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ true, cb, state);
|
||||
}
|
||||
|
||||
export function reduceEachLeadingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: T, memo: U) => U, state: T, initial: U) {
|
||||
return iterateCommentRanges(/*reduce*/ true, text, pos, /*trailing*/ false, cb, state, initial);
|
||||
}
|
||||
|
||||
export function reduceEachTrailingCommentRange<T, U>(text: string, pos: number, cb: (pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: T, memo: U) => U, state: T, initial: U) {
|
||||
return iterateCommentRanges(/*reduce*/ true, text, pos, /*trailing*/ true, cb, state, initial);
|
||||
}
|
||||
|
||||
function appendCommentRange(pos: number, end: number, kind: SyntaxKind, hasTrailingNewLine: boolean, state: any, comments: CommentRange[]) {
|
||||
if (!comments) {
|
||||
comments = [];
|
||||
}
|
||||
|
||||
comments.push({ pos, end, hasTrailingNewLine, kind });
|
||||
return comments;
|
||||
}
|
||||
|
||||
export function getLeadingCommentRanges(text: string, pos: number): CommentRange[] {
|
||||
return getCommentRanges(text, pos, /*trailing*/ false);
|
||||
return reduceEachLeadingCommentRange(text, pos, appendCommentRange, undefined, undefined);
|
||||
}
|
||||
|
||||
export function getTrailingCommentRanges(text: string, pos: number): CommentRange[] {
|
||||
return getCommentRanges(text, pos, /*trailing*/ true);
|
||||
return reduceEachTrailingCommentRange(text, pos, appendCommentRange, undefined, undefined);
|
||||
}
|
||||
|
||||
/** Optionally, get the shebang */
|
||||
|
||||
+9
-108
@@ -165,15 +165,9 @@ namespace ts {
|
||||
const enabledSyntaxKindFeatures = new Array<SyntaxKindFeatureFlags>(SyntaxKind.Count);
|
||||
const sourceTreeNodesWithAnnotations: Node[] = [];
|
||||
|
||||
let lastNodeEmitFlagsNode: Node;
|
||||
let lastNodeEmitFlags: NodeEmitFlags;
|
||||
let lastSourceMapRangeNode: Node;
|
||||
let lastSourceMapRange: TextRange;
|
||||
let lastTokenSourceMapRangeNode: Node;
|
||||
let lastTokenSourceMapRangeToken: SyntaxKind;
|
||||
let lastTokenSourceMapRange: TextRange;
|
||||
let lastCommentMapRangeNode: Node;
|
||||
let lastCommentMapRange: TextRange;
|
||||
let lexicalEnvironmentStackOffset = 0;
|
||||
let hoistedVariableDeclarations: VariableDeclaration[];
|
||||
let hoistedFunctionDeclarations: FunctionDeclaration[];
|
||||
@@ -306,18 +300,12 @@ namespace ts {
|
||||
* @param node The node.
|
||||
*/
|
||||
function beforeSetAnnotation(node: Node) {
|
||||
if (node.transformId !== transformId) {
|
||||
if ((node.flags & NodeFlags.Synthesized) === 0 && node.transformId !== transformId) {
|
||||
// To avoid holding onto transformation artifacts, we keep track of any
|
||||
// source tree node we are annotating. This allows us to clean them up after
|
||||
// all transformations have completed.
|
||||
sourceTreeNodesWithAnnotations.push(node);
|
||||
node.transformId = transformId;
|
||||
if ((node.flags & NodeFlags.Synthesized) === 0) {
|
||||
node.emitFlags = 0;
|
||||
node.sourceMapRange = undefined;
|
||||
node.commentRange = undefined;
|
||||
|
||||
// To avoid holding onto transformation artifacts, we keep track of any
|
||||
// source tree node we are annotating. This allows us to clean them up after
|
||||
// all transformations have completed.
|
||||
sourceTreeNodesWithAnnotations.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,31 +318,7 @@ namespace ts {
|
||||
* @param node The node.
|
||||
*/
|
||||
function getNodeEmitFlags(node: Node) {
|
||||
// As a performance optimization, use the cached value of the most recent node.
|
||||
// This helps for cases where this function is called repeatedly for the same node.
|
||||
if (lastNodeEmitFlagsNode === node) {
|
||||
return lastNodeEmitFlags;
|
||||
}
|
||||
|
||||
// Get the emit flags for a node or from one of its original nodes.
|
||||
let flags: NodeEmitFlags;
|
||||
let current = node;
|
||||
while (current) {
|
||||
if (current.transformId === transformId) {
|
||||
const nodeEmitFlags = current.emitFlags;
|
||||
if (nodeEmitFlags) {
|
||||
flags = nodeEmitFlags & ~NodeEmitFlags.HasNodeEmitFlags;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current = current.original;
|
||||
}
|
||||
|
||||
// Cache the most recently requested value.
|
||||
lastNodeEmitFlagsNode = node;
|
||||
lastNodeEmitFlags = flags;
|
||||
return flags;
|
||||
return node.emitFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,17 +328,8 @@ namespace ts {
|
||||
* @param emitFlags The NodeEmitFlags for the node.
|
||||
*/
|
||||
function setNodeEmitFlags<T extends Node>(node: T, emitFlags: NodeEmitFlags) {
|
||||
// Merge existing flags.
|
||||
if (emitFlags & NodeEmitFlags.Merge) {
|
||||
emitFlags = getNodeEmitFlags(node) | (emitFlags & ~NodeEmitFlags.Merge);
|
||||
}
|
||||
|
||||
beforeSetAnnotation(node);
|
||||
|
||||
// Cache the most recently requested value.
|
||||
lastNodeEmitFlagsNode = node;
|
||||
lastNodeEmitFlags = emitFlags;
|
||||
node.emitFlags = emitFlags | NodeEmitFlags.HasNodeEmitFlags;
|
||||
node.emitFlags = emitFlags;
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -387,30 +342,7 @@ namespace ts {
|
||||
* @param node The node.
|
||||
*/
|
||||
function getSourceMapRange(node: Node) {
|
||||
// As a performance optimization, use the cached value of the most recent node.
|
||||
// This helps for cases where this function is called repeatedly for the same node.
|
||||
if (lastSourceMapRangeNode === node) {
|
||||
return lastSourceMapRange || node;
|
||||
}
|
||||
|
||||
// Get the custom source map range for a node or from one of its original nodes.
|
||||
let range: TextRange;
|
||||
let current = node;
|
||||
while (current) {
|
||||
if (current.transformId === transformId) {
|
||||
range = current.sourceMapRange;
|
||||
if (range !== undefined) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current = current.original;
|
||||
}
|
||||
|
||||
// Cache the most recently requested value.
|
||||
lastSourceMapRangeNode = node;
|
||||
lastSourceMapRange = range;
|
||||
return range || node;
|
||||
return node.sourceMapRange || node;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -421,10 +353,6 @@ namespace ts {
|
||||
*/
|
||||
function setSourceMapRange<T extends Node>(node: T, range: TextRange) {
|
||||
beforeSetAnnotation(node);
|
||||
|
||||
// Cache the most recently requested value.
|
||||
lastSourceMapRangeNode = node;
|
||||
lastSourceMapRange = range;
|
||||
node.sourceMapRange = range;
|
||||
return node;
|
||||
}
|
||||
@@ -490,30 +418,7 @@ namespace ts {
|
||||
* @param node The node.
|
||||
*/
|
||||
function getCommentRange(node: Node) {
|
||||
// As a performance optimization, use the cached value of the most recent node.
|
||||
// This helps for cases where this function is called repeatedly for the same node.
|
||||
if (lastCommentMapRangeNode === node) {
|
||||
return lastCommentMapRange || node;
|
||||
}
|
||||
|
||||
// Get the custom comment range for a node or from one of its original nodes.
|
||||
let range: TextRange;
|
||||
let current = node;
|
||||
while (current) {
|
||||
if (current.transformId === transformId) {
|
||||
range = current.commentRange;
|
||||
if (range !== undefined) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current = current.original;
|
||||
}
|
||||
|
||||
// Cache the most recently requested value.
|
||||
lastCommentMapRangeNode = node;
|
||||
lastCommentMapRange = range;
|
||||
return range || node;
|
||||
return node.commentRange || node;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,10 +426,6 @@ namespace ts {
|
||||
*/
|
||||
function setCommentRange<T extends Node>(node: T, range: TextRange) {
|
||||
beforeSetAnnotation(node);
|
||||
|
||||
// Cache the most recently requested value.
|
||||
lastCommentMapRangeNode = node;
|
||||
lastCommentMapRange = range;
|
||||
node.commentRange = range;
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -956,6 +956,7 @@ namespace ts {
|
||||
* @param initializer The initializer for the parameter.
|
||||
*/
|
||||
function addDefaultValueAssignmentForInitializer(statements: Statement[], parameter: ParameterDeclaration, name: Identifier, initializer: Expression): void {
|
||||
initializer = visitNode(initializer, visitor, isExpression);
|
||||
const statement = createIf(
|
||||
createStrictEquality(
|
||||
getSynthesizedClone(name),
|
||||
@@ -966,7 +967,7 @@ namespace ts {
|
||||
createStatement(
|
||||
createAssignment(
|
||||
setNodeEmitFlags(getMutableClone(name), NodeEmitFlags.NoSourceMap),
|
||||
setNodeEmitFlags(visitNode(initializer, visitor, isExpression), NodeEmitFlags.NoSourceMap | NodeEmitFlags.Merge),
|
||||
setNodeEmitFlags(initializer, NodeEmitFlags.NoSourceMap | getNodeEmitFlags(initializer)),
|
||||
/*location*/ parameter
|
||||
)
|
||||
)
|
||||
@@ -1525,6 +1526,7 @@ namespace ts {
|
||||
|
||||
const declarationList = createVariableDeclarationList(declarations, /*location*/ node);
|
||||
setOriginalNode(declarationList, node);
|
||||
setCommentRange(declarationList, node);
|
||||
|
||||
if (node.transformFlags & TransformFlags.ContainsBindingPattern
|
||||
&& (isBindingPattern(node.declarations[0].name)
|
||||
@@ -1533,7 +1535,7 @@ namespace ts {
|
||||
// the source map range for the declaration list.
|
||||
const firstDeclaration = firstOrUndefined(declarations);
|
||||
const lastDeclaration = lastOrUndefined(declarations);
|
||||
setSourceMapRange(node, createRange(firstDeclaration.pos, lastDeclaration.end));
|
||||
setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end));
|
||||
}
|
||||
|
||||
return declarationList;
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace ts {
|
||||
hoistVariableDeclaration,
|
||||
setNodeEmitFlags,
|
||||
getNodeEmitFlags,
|
||||
setSourceMapRange,
|
||||
} = context;
|
||||
|
||||
const compilerOptions = context.getCompilerOptions();
|
||||
@@ -926,7 +927,13 @@ namespace ts {
|
||||
}
|
||||
|
||||
function createExportStatement(name: Identifier, value: Expression, location?: TextRange) {
|
||||
return startOnNewLine(createStatement(createExportAssignment(name, value), location));
|
||||
const statement = createStatement(createExportAssignment(name, value));
|
||||
statement.startsOnNewLine = true;
|
||||
if (location) {
|
||||
setSourceMapRange(statement, location);
|
||||
}
|
||||
|
||||
return statement;
|
||||
}
|
||||
|
||||
function createExportAssignment(name: Identifier, value: Expression) {
|
||||
|
||||
@@ -2244,10 +2244,10 @@ namespace ts {
|
||||
|
||||
// While we emit the source map for the node after skipping decorators and modifiers,
|
||||
// we need to emit the comments for the original range.
|
||||
setOriginalNode(parameter, node);
|
||||
setCommentRange(parameter, node);
|
||||
setSourceMapRange(parameter, moveRangePastModifiers(node));
|
||||
setNodeEmitFlags(parameter.name, NodeEmitFlags.NoTrailingSourceMap);
|
||||
setOriginalNode(parameter, node);
|
||||
|
||||
return parameter;
|
||||
}
|
||||
@@ -2377,17 +2377,16 @@ namespace ts {
|
||||
* Adds a trailing VariableStatement for an enum or module declaration.
|
||||
*/
|
||||
function addVarForEnumExportedFromNamespace(statements: Statement[], node: EnumDeclaration | ModuleDeclaration) {
|
||||
statements.push(
|
||||
createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
[createVariableDeclaration(
|
||||
getDeclarationName(node),
|
||||
/*type*/ undefined,
|
||||
getExportName(node)
|
||||
)],
|
||||
/*location*/ node
|
||||
)
|
||||
const statement = createVariableStatement(
|
||||
/*modifiers*/ undefined,
|
||||
[createVariableDeclaration(
|
||||
getDeclarationName(node),
|
||||
/*type*/ undefined,
|
||||
getExportName(node)
|
||||
)]
|
||||
);
|
||||
setSourceMapRange(statement, node);
|
||||
statements.push(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2403,48 +2402,62 @@ namespace ts {
|
||||
}
|
||||
|
||||
const statements: Statement[] = [];
|
||||
|
||||
// We request to be advised when the printer is about to print this node. This allows
|
||||
// us to set up the correct state for later substitutions.
|
||||
let emitFlags = NodeEmitFlags.AdviseOnEmitNode;
|
||||
|
||||
// If needed, we should emit a variable declaration for the enum. If we emit
|
||||
// a leading variable declaration, we should not emit leading comments for the
|
||||
// enum body.
|
||||
if (shouldEmitVarForEnumDeclaration(node)) {
|
||||
addVarForEnumOrModuleDeclaration(statements, node);
|
||||
|
||||
// We should still emit the comments if we are emitting a system module.
|
||||
if (moduleKind !== ModuleKind.System || currentScope !== currentSourceFile) {
|
||||
emitFlags |= NodeEmitFlags.NoLeadingComments;
|
||||
}
|
||||
}
|
||||
|
||||
const innerName = getNamespaceContainerName(node);
|
||||
const paramName = getNamespaceParameterName(node);
|
||||
// `parameterName` is the declaration name used inside of the enum.
|
||||
const parameterName = getNamespaceParameterName(node);
|
||||
|
||||
// `containerName` is the expression used inside of the enum for assignments.
|
||||
const containerName = getNamespaceContainerName(node);
|
||||
|
||||
// `exportName` is the expression used within this node's container for any exported references.
|
||||
const exportName = getExportName(node);
|
||||
|
||||
// (function (x) {
|
||||
// x[x["y"] = 0] = "y";
|
||||
// ...
|
||||
// })(x || (x = {}));
|
||||
statements.push(
|
||||
setNodeEmitFlags(
|
||||
setOriginalNode(
|
||||
createStatement(
|
||||
createCall(
|
||||
createFunctionExpression(
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
[createParameter(paramName)],
|
||||
/*type*/ undefined,
|
||||
transformEnumBody(node, innerName)
|
||||
),
|
||||
/*typeArguments*/ undefined,
|
||||
[createLogicalOr(
|
||||
exportName,
|
||||
createAssignment(
|
||||
exportName,
|
||||
createObjectLiteral()
|
||||
)
|
||||
)]
|
||||
),
|
||||
/*location*/ node
|
||||
),
|
||||
/*original*/ node
|
||||
const enumStatement = createStatement(
|
||||
createCall(
|
||||
createFunctionExpression(
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
[createParameter(parameterName)],
|
||||
/*type*/ undefined,
|
||||
transformEnumBody(node, containerName)
|
||||
),
|
||||
NodeEmitFlags.AdviseOnEmitNode
|
||||
)
|
||||
/*typeArguments*/ undefined,
|
||||
[createLogicalOr(
|
||||
exportName,
|
||||
createAssignment(
|
||||
exportName,
|
||||
createObjectLiteral()
|
||||
)
|
||||
)]
|
||||
),
|
||||
/*location*/ node
|
||||
);
|
||||
|
||||
setOriginalNode(enumStatement, node);
|
||||
setNodeEmitFlags(enumStatement, emitFlags);
|
||||
statements.push(enumStatement);
|
||||
|
||||
if (isNamespaceExport(node)) {
|
||||
addVarForEnumExportedFromNamespace(statements, node);
|
||||
}
|
||||
@@ -2565,6 +2578,8 @@ namespace ts {
|
||||
]
|
||||
);
|
||||
|
||||
setOriginalNode(statement, /*original*/ node);
|
||||
|
||||
// Adjust the source map emit to match the old emitter.
|
||||
if (node.kind === SyntaxKind.EnumDeclaration) {
|
||||
setSourceMapRange(statement.declarationList, node);
|
||||
@@ -2593,7 +2608,6 @@ namespace ts {
|
||||
//
|
||||
setCommentRange(statement, node);
|
||||
setNodeEmitFlags(statement, NodeEmitFlags.NoTrailingComments);
|
||||
setOriginalNode(statement, /*original*/ node);
|
||||
statements.push(statement);
|
||||
}
|
||||
|
||||
@@ -2614,8 +2628,19 @@ namespace ts {
|
||||
|
||||
const statements: Statement[] = [];
|
||||
|
||||
// We request to be advised when the printer is about to print this node. This allows
|
||||
// us to set up the correct state for later substitutions.
|
||||
let emitFlags = NodeEmitFlags.AdviseOnEmitNode;
|
||||
|
||||
// If needed, we should emit a variable declaration for the module. If we emit
|
||||
// a leading variable declaration, we should not emit leading comments for the
|
||||
// module body.
|
||||
if (shouldEmitVarForModuleDeclaration(node)) {
|
||||
addVarForEnumOrModuleDeclaration(statements, node);
|
||||
// We should still emit the comments if we are emitting a system module.
|
||||
if (moduleKind !== ModuleKind.System || currentScope !== currentSourceFile) {
|
||||
emitFlags |= NodeEmitFlags.NoLeadingComments;
|
||||
}
|
||||
}
|
||||
|
||||
// `parameterName` is the declaration name used inside of the namespace.
|
||||
@@ -2649,30 +2674,25 @@ namespace ts {
|
||||
// (function (x_1) {
|
||||
// x_1.y = ...;
|
||||
// })(x || (x = {}));
|
||||
statements.push(
|
||||
setNodeEmitFlags(
|
||||
setOriginalNode(
|
||||
createStatement(
|
||||
createCall(
|
||||
createFunctionExpression(
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
[createParameter(parameterName)],
|
||||
/*type*/ undefined,
|
||||
transformModuleBody(node, containerName)
|
||||
),
|
||||
/*typeArguments*/ undefined,
|
||||
[moduleArg]
|
||||
),
|
||||
/*location*/ node
|
||||
),
|
||||
/*original*/ node
|
||||
const moduleStatement = createStatement(
|
||||
createCall(
|
||||
createFunctionExpression(
|
||||
/*asteriskToken*/ undefined,
|
||||
/*name*/ undefined,
|
||||
/*typeParameters*/ undefined,
|
||||
[createParameter(parameterName)],
|
||||
/*type*/ undefined,
|
||||
transformModuleBody(node, containerName)
|
||||
),
|
||||
NodeEmitFlags.AdviseOnEmitNode
|
||||
)
|
||||
/*typeArguments*/ undefined,
|
||||
[moduleArg]
|
||||
),
|
||||
/*location*/ node
|
||||
);
|
||||
|
||||
setOriginalNode(moduleStatement, node);
|
||||
setNodeEmitFlags(moduleStatement, emitFlags);
|
||||
statements.push(moduleStatement);
|
||||
return statements;
|
||||
}
|
||||
|
||||
@@ -2748,11 +2768,6 @@ namespace ts {
|
||||
&& resolver.isTopLevelValueImportEqualsWithEntityName(node));
|
||||
}
|
||||
|
||||
function disableCommentsRecursive(node: Node) {
|
||||
setNodeEmitFlags(node, NodeEmitFlags.NoComments | NodeEmitFlags.Merge);
|
||||
forEachChild(node, disableCommentsRecursive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits an import equals declaration.
|
||||
*
|
||||
@@ -2768,7 +2783,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
const moduleReference = createExpressionFromEntityName(<EntityName>node.moduleReference);
|
||||
disableCommentsRecursive(moduleReference);
|
||||
setNodeEmitFlags(moduleReference, NodeEmitFlags.NoComments | NodeEmitFlags.NoNestedComments);
|
||||
|
||||
if (isNamedExternalModuleExport(node) || !isNamespaceExport(node)) {
|
||||
// export var ${name} = ${moduleReference};
|
||||
// var ${name} = ${moduleReference};
|
||||
@@ -2846,16 +2862,15 @@ namespace ts {
|
||||
}
|
||||
|
||||
function addExportMemberAssignment(statements: Statement[], node: DeclarationStatement) {
|
||||
statements.push(
|
||||
createStatement(
|
||||
createAssignment(
|
||||
getExportName(node),
|
||||
getLocalName(node, /*noSourceMaps*/ true),
|
||||
/*location*/ createRange(node.name.pos, node.end)
|
||||
),
|
||||
/*location*/ createRange(-1, node.end)
|
||||
)
|
||||
const expression = createAssignment(
|
||||
getExportName(node),
|
||||
getLocalName(node, /*noSourceMaps*/ true)
|
||||
);
|
||||
setSourceMapRange(expression, createRange(node.name.pos, node.end));
|
||||
|
||||
const statement = createStatement(expression);
|
||||
setSourceMapRange(statement, createRange(-1, node.end));
|
||||
statements.push(statement);
|
||||
}
|
||||
|
||||
function createNamespaceExport(exportName: Identifier, exportValue: Expression, location?: TextRange) {
|
||||
|
||||
+14
-17
@@ -3019,34 +3019,31 @@ namespace ts {
|
||||
EmitSuperHelper = 1 << 2, // Emit the basic _super helper for async methods.
|
||||
EmitAdvancedSuperHelper = 1 << 3, // Emit the advanced _super helper for async methods.
|
||||
UMDDefine = 1 << 4, // This node should be replaced with the UMD define helper.
|
||||
NoLexicalEnvironment = 1 << 5, // A new LexicalEnvironment should *not* be introduced when emitting this node, this is primarily used when printing a SystemJS module.
|
||||
SingleLine = 1 << 6, // The contents of this node should be emitted on a single line.
|
||||
AdviseOnEmitNode = 1 << 7, // The printer should invoke the onEmitNode callback when printing this node.
|
||||
NoSubstitution = 1 << 8, // Disables further substitution of an expression.
|
||||
CapturesThis = 1 << 9, // The function captures a lexical `this`
|
||||
NoLeadingSourceMap = 1 << 10, // Do not emit a leading source map location for this node.
|
||||
NoTrailingSourceMap = 1 << 11, // Do not emit a trailing source map location for this node.
|
||||
SingleLine = 1 << 5, // The contents of this node should be emitted on a single line.
|
||||
AdviseOnEmitNode = 1 << 6, // The printer should invoke the onEmitNode callback when printing this node.
|
||||
NoSubstitution = 1 << 7, // Disables further substitution of an expression.
|
||||
CapturesThis = 1 << 8, // The function captures a lexical `this`
|
||||
NoLeadingSourceMap = 1 << 9, // Do not emit a leading source map location for this node.
|
||||
NoTrailingSourceMap = 1 << 10, // Do not emit a trailing source map location for this node.
|
||||
NoSourceMap = NoLeadingSourceMap | NoTrailingSourceMap, // Do not emit a source map location for this node.
|
||||
NoNestedSourceMaps = 1 << 12, // Do not emit source map locations for children of this node.
|
||||
NoTokenLeadingSourceMaps = 1 << 13, // Do not emit leading source map location for token nodes.
|
||||
NoTokenTrailingSourceMaps = 1 << 14, // Do not emit trailing source map location for token nodes.
|
||||
NoNestedSourceMaps = 1 << 11, // Do not emit source map locations for children of this node.
|
||||
NoTokenLeadingSourceMaps = 1 << 12, // Do not emit leading source map location for token nodes.
|
||||
NoTokenTrailingSourceMaps = 1 << 13, // Do not emit trailing source map location for token nodes.
|
||||
NoTokenSourceMaps = NoTokenLeadingSourceMaps | NoTokenTrailingSourceMaps, // Do not emit source map locations for tokens of this node.
|
||||
NoLeadingComments = 1 << 15, // Do not emit leading comments for this node.
|
||||
NoTrailingComments = 1 << 16, // Do not emit trailing comments for this node.
|
||||
NoLeadingComments = 1 << 14, // Do not emit leading comments for this node.
|
||||
NoTrailingComments = 1 << 15, // Do not emit trailing comments for this node.
|
||||
NoComments = NoLeadingComments | NoTrailingComments, // Do not emit comments for this node.
|
||||
NoNestedComments = 1 << 16,
|
||||
ExportName = 1 << 17, // Ensure an export prefix is added for an identifier that points to an exported declaration with a local name (see SymbolFlags.ExportHasLocal).
|
||||
LocalName = 1 << 18, // Ensure an export prefix is not added for an identifier that points to an exported declaration.
|
||||
Indented = 1 << 19, // Adds an explicit extra indentation level for class and function bodies when printing (used to match old emitter).
|
||||
Merge = 1 << 20, // When getting emit options, merge with existing emit options.
|
||||
|
||||
// SourceMap Specialization.
|
||||
// TODO(rbuckton): These should be removed once source maps are aligned with the old
|
||||
// emitter and new baselines are taken. This exists solely to
|
||||
// align with the old emitter.
|
||||
SourceMapEmitOpenBraceAsToken = 1 << 21, // Emits the open brace of a block function body as a source mapped token.
|
||||
SourceMapAdjustRestParameterLoop = 1 << 22, // Emits adjusted source map positions for a ForStatement generated when transforming a rest parameter for ES5/3.
|
||||
|
||||
HasNodeEmitFlags = 1 << 31, // Indicates the node has emit flags set.
|
||||
SourceMapEmitOpenBraceAsToken = 1 << 20, // Emits the open brace of a block function body as a source mapped token.
|
||||
SourceMapAdjustRestParameterLoop = 1 << 21, // Emits adjusted source map positions for a ForStatement generated when transforming a rest parameter for ES5/3.
|
||||
}
|
||||
|
||||
/** Additional context provided to `visitEachChild` */
|
||||
|
||||
+30
-16
@@ -2311,6 +2311,7 @@ namespace ts {
|
||||
getLine(): number;
|
||||
getColumn(): number;
|
||||
getIndent(): number;
|
||||
isAtStartOfLine(): boolean;
|
||||
reset(): void;
|
||||
}
|
||||
|
||||
@@ -2399,6 +2400,7 @@ namespace ts {
|
||||
getLine: () => lineCount + 1,
|
||||
getColumn: () => lineStart ? indent * getIndentSize() + 1 : output.length - linePos + 1,
|
||||
getText: () => output,
|
||||
isAtStartOfLine: () => lineStart,
|
||||
reset
|
||||
};
|
||||
}
|
||||
@@ -2616,15 +2618,27 @@ namespace ts {
|
||||
}
|
||||
|
||||
export function emitNewLineBeforeLeadingComments(lineMap: number[], writer: EmitTextWriter, node: TextRange, leadingComments: CommentRange[]) {
|
||||
emitNewLineBeforeLeadingCommentsOfPosition(lineMap, writer, node.pos, leadingComments);
|
||||
}
|
||||
|
||||
export function emitNewLineBeforeLeadingCommentsOfPosition(lineMap: number[], writer: EmitTextWriter, pos: number, leadingComments: CommentRange[]) {
|
||||
// If the leading comments start on different line than the start of node, write new line
|
||||
if (leadingComments && leadingComments.length && node.pos !== leadingComments[0].pos &&
|
||||
getLineOfLocalPositionFromLineMap(lineMap, node.pos) !== getLineOfLocalPositionFromLineMap(lineMap, leadingComments[0].pos)) {
|
||||
if (leadingComments && leadingComments.length && pos !== leadingComments[0].pos &&
|
||||
getLineOfLocalPositionFromLineMap(lineMap, pos) !== getLineOfLocalPositionFromLineMap(lineMap, leadingComments[0].pos)) {
|
||||
writer.writeLine();
|
||||
}
|
||||
}
|
||||
|
||||
export function emitNewLineBeforeLeadingCommentOfPosition(lineMap: number[], writer: EmitTextWriter, pos: number, commentPos: number) {
|
||||
// If the leading comments start on different line than the start of node, write new line
|
||||
if (pos !== commentPos &&
|
||||
getLineOfLocalPositionFromLineMap(lineMap, pos) !== getLineOfLocalPositionFromLineMap(lineMap, commentPos)) {
|
||||
writer.writeLine();
|
||||
}
|
||||
}
|
||||
|
||||
export function emitComments(text: string, lineMap: number[], writer: EmitTextWriter, comments: CommentRange[], leadingSeparator: boolean, trailingSeparator: boolean, newLine: string,
|
||||
writeComment: (text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) => void) {
|
||||
writeComment: (text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) => void) {
|
||||
if (comments && comments.length > 0) {
|
||||
if (leadingSeparator) {
|
||||
writer.write(" ");
|
||||
@@ -2637,7 +2651,7 @@ namespace ts {
|
||||
emitInterveningSeparator = false;
|
||||
}
|
||||
|
||||
writeComment(text, lineMap, writer, comment, newLine);
|
||||
writeComment(text, lineMap, writer, comment.pos, comment.end, newLine);
|
||||
if (comment.hasTrailingNewLine) {
|
||||
writer.writeLine();
|
||||
}
|
||||
@@ -2657,7 +2671,7 @@ namespace ts {
|
||||
* the next statement by space.
|
||||
*/
|
||||
export function emitDetachedComments(text: string, lineMap: number[], writer: EmitTextWriter,
|
||||
writeComment: (text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) => void,
|
||||
writeComment: (text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) => void,
|
||||
node: TextRange, newLine: string, removeComments: boolean) {
|
||||
let leadingComments: CommentRange[];
|
||||
let currentDetachedCommentInfo: {nodePos: number, detachedCommentEndPos: number};
|
||||
@@ -2721,20 +2735,20 @@ namespace ts {
|
||||
|
||||
}
|
||||
|
||||
export function writeCommentRange(text: string, lineMap: number[], writer: EmitTextWriter, comment: CommentRange, newLine: string) {
|
||||
if (text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk) {
|
||||
const firstCommentLineAndCharacter = computeLineAndCharacterOfPosition(lineMap, comment.pos);
|
||||
export function writeCommentRange(text: string, lineMap: number[], writer: EmitTextWriter, commentPos: number, commentEnd: number, newLine: string) {
|
||||
if (text.charCodeAt(commentPos + 1) === CharacterCodes.asterisk) {
|
||||
const firstCommentLineAndCharacter = computeLineAndCharacterOfPosition(lineMap, commentPos);
|
||||
const lineCount = lineMap.length;
|
||||
let firstCommentLineIndent: number;
|
||||
for (let pos = comment.pos, currentLine = firstCommentLineAndCharacter.line; pos < comment.end; currentLine++) {
|
||||
for (let pos = commentPos, currentLine = firstCommentLineAndCharacter.line; pos < commentEnd; currentLine++) {
|
||||
const nextLineStart = (currentLine + 1) === lineCount
|
||||
? text.length + 1
|
||||
: lineMap[currentLine + 1];
|
||||
|
||||
if (pos !== comment.pos) {
|
||||
if (pos !== commentPos) {
|
||||
// If we are not emitting first line, we need to write the spaces to adjust the alignment
|
||||
if (firstCommentLineIndent === undefined) {
|
||||
firstCommentLineIndent = calculateIndent(text, lineMap[firstCommentLineAndCharacter.line], comment.pos);
|
||||
firstCommentLineIndent = calculateIndent(text, lineMap[firstCommentLineAndCharacter.line], commentPos);
|
||||
}
|
||||
|
||||
// These are number of spaces writer is going to write at current indent
|
||||
@@ -2775,24 +2789,24 @@ namespace ts {
|
||||
}
|
||||
|
||||
// Write the comment line text
|
||||
writeTrimmedCurrentLine(text, comment, writer, newLine, pos, nextLineStart);
|
||||
writeTrimmedCurrentLine(text, commentEnd, writer, newLine, pos, nextLineStart);
|
||||
|
||||
pos = nextLineStart;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Single line comment of style //....
|
||||
writer.write(text.substring(comment.pos, comment.end));
|
||||
writer.write(text.substring(commentPos, commentEnd));
|
||||
}
|
||||
}
|
||||
|
||||
function writeTrimmedCurrentLine(text: string, comment: CommentRange, writer: EmitTextWriter, newLine: string, pos: number, nextLineStart: number) {
|
||||
const end = Math.min(comment.end, nextLineStart - 1);
|
||||
function writeTrimmedCurrentLine(text: string, commentEnd: number, writer: EmitTextWriter, newLine: string, pos: number, nextLineStart: number) {
|
||||
const end = Math.min(commentEnd, nextLineStart - 1);
|
||||
const currentLineText = text.substring(pos, end).replace(/^\s+|\s+$/g, "");
|
||||
if (currentLineText) {
|
||||
// trimmed forward and ending spaces text
|
||||
writer.write(currentLineText);
|
||||
if (end !== comment.end) {
|
||||
if (end !== commentEnd) {
|
||||
writer.writeLine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1044,7 +1044,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (updated !== node) {
|
||||
updated.original = node;
|
||||
updateNode(updated, node);
|
||||
}
|
||||
|
||||
// performance.measure(measureName, markName);
|
||||
|
||||
Reference in New Issue
Block a user