@typescript/analyze-trace
Version:
Analyze the output of tsc --generatetrace
168 lines • 7.88 kB
JavaScript
;
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
const TriviaStateMachine = require("./trivia-state-machine");
let freezeCache = [-1, -1];
function freeze(lineChar) {
if (lineChar[0] !== freezeCache[0] || lineChar[1] !== freezeCache[1]) {
freezeCache = [lineChar[0], lineChar[1]];
}
return freezeCache;
}
function compareOffsets(a, b) {
return a - b;
}
function compareLineChars(a, b) {
return a[0] - b[0] || a[1] - b[1];
}
/**
* @param stream A stream of string chunks with respect to which `positions` should be normalized.
* @param positions Positions to be normalized. NB: negative numbers will be treated as positions
* that should be converted to line-char without but not moved past trivia.
* @returns A list of normalized line-char positions.
*/
function normalizePositions(stream, positions) {
return positions.length === 0 ? Promise.resolve([]) : new Promise((resolve, reject) => {
let prevCh = -1;
stream.on("error", err => reject(err));
// The actual event handling is in onChar and onEof below.
// These handlers provided a simplified current- and next-char view.
stream.on("data", chunk => {
const text = chunk;
const length = text.length;
for (let i = 0; i < length; i++) {
const ch = text.charCodeAt(i);
if (prevCh >= 0) {
onChar(prevCh, ch);
}
prevCh = ch;
}
});
stream.on("close", () => {
if (prevCh >= 0) {
onChar(prevCh, -1);
}
onEof();
});
// We partition the positions because the varieties
// cannot be sorted with respect to each other.
const fixedOffsetWrappers = []; // Don't skip trivia
const offsetWrappers = []; // Do skip trivia
const fixedLineCharWrappers = []; // Don't skip trivia
const lineCharWrappers = []; // Do skip trivia
for (let i = 0; i < positions.length; i++) {
const position = positions[i];
if (typeof position === "number") {
if (position < 0) {
fixedOffsetWrappers.push({
returnIndex: i,
offset: -position,
});
}
else {
offsetWrappers.push({
returnIndex: i,
offset: position,
});
}
}
else {
if (position[0] < 0 || position[1] < 0) {
fixedLineCharWrappers.push({
returnIndex: i,
lineChar: [Math.abs(position[0]), Math.abs(position[1])],
});
}
else {
lineCharWrappers.push({
returnIndex: i,
lineChar: position,
});
}
}
}
fixedOffsetWrappers.sort((a, b) => compareOffsets(a.offset, b.offset));
offsetWrappers.sort((a, b) => compareOffsets(a.offset, b.offset));
fixedLineCharWrappers.sort((a, b) => compareLineChars(a.lineChar, b.lineChar));
lineCharWrappers.sort((a, b) => compareLineChars(a.lineChar, b.lineChar));
let fixedOffsetWrappersPos = 0;
let offsetWrappersPos = 0;
let fixedLineCharWrappersPos = 0;
let lineCharWrappersPos = 0;
let currOffset = 0;
let currLineChar = [1, 1];
let stateMachine = TriviaStateMachine.create();
function onChar(ch, nextCh) {
const { charKind, wrapLine } = stateMachine.step(ch, nextCh);
const isTrivia = charKind === "comment" || charKind === "whitespace";
// This is handy when debugging
// console.error(`${currOffset}\t${/^[a-zA-Z0-9!@#$%^&*()[\]{}\\/;':"<,>.?`~+=_\-]$/.test(String.fromCharCode(ch)) ? String.fromCharCode(ch) : "0x" + ch.toString(16)}\t(${currLineChar[0]},${currLineChar[1]})\t${charKind}`);
for (; fixedOffsetWrappersPos < fixedOffsetWrappers.length && compareOffsets(fixedOffsetWrappers[fixedOffsetWrappersPos].offset, currOffset) <= 0; fixedOffsetWrappersPos++) {
fixedOffsetWrappers[fixedOffsetWrappersPos].offset = currOffset;
fixedOffsetWrappers[fixedOffsetWrappersPos].lineChar = freeze(currLineChar);
}
for (; fixedLineCharWrappersPos < fixedLineCharWrappers.length && compareLineChars(fixedLineCharWrappers[fixedLineCharWrappersPos].lineChar, currLineChar) <= 0; fixedLineCharWrappersPos++) {
fixedLineCharWrappers[fixedLineCharWrappersPos].offset = currOffset;
fixedLineCharWrappers[fixedLineCharWrappersPos].lineChar = freeze(currLineChar);
}
if (!isTrivia) {
for (; offsetWrappersPos < offsetWrappers.length && compareOffsets(offsetWrappers[offsetWrappersPos].offset, currOffset) <= 0; offsetWrappersPos++) {
offsetWrappers[offsetWrappersPos].offset = currOffset;
offsetWrappers[offsetWrappersPos].lineChar = freeze(currLineChar);
}
for (; lineCharWrappersPos < lineCharWrappers.length && compareLineChars(lineCharWrappers[lineCharWrappersPos].lineChar, currLineChar) <= 0; lineCharWrappersPos++) {
lineCharWrappers[lineCharWrappersPos].offset = currOffset;
lineCharWrappers[lineCharWrappersPos].lineChar = freeze(currLineChar);
}
}
currOffset++;
if (wrapLine) {
currLineChar[0]++;
currLineChar[1] = 1;
}
else {
currLineChar[1]++;
}
// TODO (https://github.com/microsoft/typescript-analyze-trace/issues/4)
}
function onEof() {
const result = [];
const eofLineChar = freeze(currLineChar);
for (let i = 0; i < fixedOffsetWrappersPos; i++) {
const wrapper = fixedOffsetWrappers[i];
result[wrapper.returnIndex] = wrapper.lineChar;
}
for (let i = fixedOffsetWrappersPos; i < fixedOffsetWrappers.length; i++) {
const wrapper = fixedOffsetWrappers[i];
result[wrapper.returnIndex] = eofLineChar;
}
for (let i = 0; i < offsetWrappersPos; i++) {
const wrapper = offsetWrappers[i];
result[wrapper.returnIndex] = wrapper.lineChar;
}
for (let i = offsetWrappersPos; i < offsetWrappers.length; i++) {
const wrapper = offsetWrappers[i];
result[wrapper.returnIndex] = eofLineChar;
}
for (let i = 0; i < fixedLineCharWrappersPos; i++) {
const wrapper = fixedLineCharWrappers[i];
result[wrapper.returnIndex] = wrapper.lineChar;
}
for (let i = fixedLineCharWrappersPos; i < fixedLineCharWrappers.length; i++) {
const wrapper = fixedLineCharWrappers[i];
result[wrapper.returnIndex] = eofLineChar;
}
for (let i = 0; i < lineCharWrappersPos; i++) {
const wrapper = lineCharWrappers[i];
result[wrapper.returnIndex] = wrapper.lineChar;
}
for (let i = lineCharWrappersPos; i < lineCharWrappers.length; i++) {
const wrapper = lineCharWrappers[i];
result[wrapper.returnIndex] = eofLineChar;
}
resolve(result);
}
});
}
module.exports = normalizePositions;
//# sourceMappingURL=normalize-positions.js.map