mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Move incremental state to WeakSets (#58087)
This commit is contained in:
+45
-42
@@ -318,7 +318,6 @@ import {
|
||||
QuestionToken,
|
||||
ReadonlyKeyword,
|
||||
ReadonlyPragmaMap,
|
||||
ReadonlyTextRange,
|
||||
ResolutionMode,
|
||||
RestTypeNode,
|
||||
ReturnStatement,
|
||||
@@ -1950,7 +1949,7 @@ namespace Parser {
|
||||
function currentNode(position: number) {
|
||||
const node = baseSyntaxCursor.currentNode(position);
|
||||
if (topLevel && node && containsPossibleTopLevelAwait(node)) {
|
||||
node.intersectsChange = true;
|
||||
markAsIntersectingIncrementalChange(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -3120,7 +3119,7 @@ namespace Parser {
|
||||
// Can't reuse a node that intersected the change range.
|
||||
// Can't reuse a node that contains a parse error. This is necessary so that we
|
||||
// produce the same set of errors again.
|
||||
if (nodeIsMissing(node) || node.intersectsChange || containsParseError(node)) {
|
||||
if (nodeIsMissing(node) || intersectsIncrementalChange(node) || containsParseError(node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -9844,6 +9843,25 @@ namespace Parser {
|
||||
}
|
||||
}
|
||||
|
||||
const incrementallyParsedFiles = new WeakSet<SourceFile>();
|
||||
|
||||
function markAsIncrementallyParsed(sourceFile: SourceFile) {
|
||||
if (incrementallyParsedFiles.has(sourceFile)) {
|
||||
Debug.fail("Source file has already been incrementally parsed");
|
||||
}
|
||||
incrementallyParsedFiles.add(sourceFile);
|
||||
}
|
||||
|
||||
const intersectingChangeSet = new WeakSet<Node | NodeArray<Node>>();
|
||||
|
||||
function intersectsIncrementalChange(node: Node | NodeArray<Node>): boolean {
|
||||
return intersectingChangeSet.has(node);
|
||||
}
|
||||
|
||||
function markAsIntersectingIncrementalChange(node: Node | NodeArray<Node>) {
|
||||
intersectingChangeSet.add(node);
|
||||
}
|
||||
|
||||
namespace IncrementalParser {
|
||||
export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile {
|
||||
aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive);
|
||||
@@ -9866,10 +9884,8 @@ namespace IncrementalParser {
|
||||
// This is because we do incremental parsing in-place. i.e. we take nodes from the old
|
||||
// tree and give them new positions and parents. From that point on, trusting the old
|
||||
// tree at all is not possible as far too much of it may violate invariants.
|
||||
const incrementalSourceFile = sourceFile as Node as IncrementalNode;
|
||||
Debug.assert(!incrementalSourceFile.hasBeenIncrementallyParsed);
|
||||
incrementalSourceFile.hasBeenIncrementallyParsed = true;
|
||||
Parser.fixupParentReferences(incrementalSourceFile);
|
||||
markAsIncrementallyParsed(sourceFile);
|
||||
Parser.fixupParentReferences(sourceFile);
|
||||
const oldText = sourceFile.text;
|
||||
const syntaxCursor = createSyntaxCursor(sourceFile);
|
||||
|
||||
@@ -9908,7 +9924,7 @@ namespace IncrementalParser {
|
||||
//
|
||||
// Also, mark any syntax elements that intersect the changed span. We know, up front,
|
||||
// that we cannot reuse these elements.
|
||||
updateTokenPositionsAndMarkElements(incrementalSourceFile, changeRange.span.start, textSpanEnd(changeRange.span), textSpanEnd(textChangeRangeNewSpan(changeRange)), delta, oldText, newText, aggressiveChecks);
|
||||
updateTokenPositionsAndMarkElements(sourceFile, changeRange.span.start, textSpanEnd(changeRange.span), textSpanEnd(textChangeRangeNewSpan(changeRange)), delta, oldText, newText, aggressiveChecks);
|
||||
|
||||
// Now that we've set up our internal incremental state just proceed and parse the
|
||||
// source file in the normal fashion. When possible the parser will retrieve and
|
||||
@@ -9984,18 +10000,18 @@ namespace IncrementalParser {
|
||||
}
|
||||
}
|
||||
|
||||
function moveElementEntirelyPastChangeRange(element: IncrementalNode, isArray: false, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
|
||||
function moveElementEntirelyPastChangeRange(element: IncrementalNodeArray, isArray: true, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
|
||||
function moveElementEntirelyPastChangeRange(element: IncrementalNode | IncrementalNodeArray, isArray: boolean, delta: number, oldText: string, newText: string, aggressiveChecks: boolean) {
|
||||
function moveElementEntirelyPastChangeRange(element: Node, isArray: false, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
|
||||
function moveElementEntirelyPastChangeRange(element: NodeArray<Node>, isArray: true, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
|
||||
function moveElementEntirelyPastChangeRange(element: Node | NodeArray<Node>, isArray: boolean, delta: number, oldText: string, newText: string, aggressiveChecks: boolean) {
|
||||
if (isArray) {
|
||||
visitArray(element as IncrementalNodeArray);
|
||||
visitArray(element as NodeArray<Node>);
|
||||
}
|
||||
else {
|
||||
visitNode(element as IncrementalNode);
|
||||
visitNode(element as Node);
|
||||
}
|
||||
return;
|
||||
|
||||
function visitNode(node: IncrementalNode) {
|
||||
function visitNode(node: Node) {
|
||||
let text = "";
|
||||
if (aggressiveChecks && shouldCheckNode(node)) {
|
||||
text = oldText.substring(node.pos, node.end);
|
||||
@@ -10014,13 +10030,13 @@ namespace IncrementalParser {
|
||||
forEachChild(node, visitNode as (node: Node) => void, visitArray as (nodes: NodeArray<Node>) => void);
|
||||
if (hasJSDocNodes(node)) {
|
||||
for (const jsDocComment of node.jsDoc!) {
|
||||
visitNode(jsDocComment as Node as IncrementalNode);
|
||||
visitNode(jsDocComment);
|
||||
}
|
||||
}
|
||||
checkNodePositions(node, aggressiveChecks);
|
||||
}
|
||||
|
||||
function visitArray(array: IncrementalNodeArray) {
|
||||
function visitArray(array: NodeArray<Node>) {
|
||||
setTextRangePosEnd(array, array.pos + delta, array.end + delta);
|
||||
|
||||
for (const node of array) {
|
||||
@@ -10040,7 +10056,7 @@ namespace IncrementalParser {
|
||||
return false;
|
||||
}
|
||||
|
||||
function adjustIntersectingElement(element: IncrementalElement, changeStart: number, changeRangeOldEnd: number, changeRangeNewEnd: number, delta: number) {
|
||||
function adjustIntersectingElement(element: Node | NodeArray<Node>, changeStart: number, changeRangeOldEnd: number, changeRangeNewEnd: number, delta: number) {
|
||||
Debug.assert(element.end >= changeStart, "Adjusting an element that was entirely before the change range");
|
||||
Debug.assert(element.pos <= changeRangeOldEnd, "Adjusting an element that was entirely after the change range");
|
||||
Debug.assert(element.pos <= element.end);
|
||||
@@ -10106,9 +10122,10 @@ namespace IncrementalParser {
|
||||
Math.min(element.end, changeRangeNewEnd);
|
||||
|
||||
Debug.assert(pos <= end);
|
||||
if (element.parent) {
|
||||
Debug.assertGreaterThanOrEqual(pos, element.parent.pos);
|
||||
Debug.assertLessThanOrEqual(end, element.parent.end);
|
||||
if ((element as any).parent) {
|
||||
const parent = (element as any).parent as Node;
|
||||
Debug.assertGreaterThanOrEqual(pos, parent.pos);
|
||||
Debug.assertLessThanOrEqual(end, parent.end);
|
||||
}
|
||||
|
||||
setTextRangePosEnd(element, pos, end);
|
||||
@@ -10132,7 +10149,7 @@ namespace IncrementalParser {
|
||||
}
|
||||
|
||||
function updateTokenPositionsAndMarkElements(
|
||||
sourceFile: IncrementalNode,
|
||||
sourceFile: SourceFile,
|
||||
changeStart: number,
|
||||
changeRangeOldEnd: number,
|
||||
changeRangeNewEnd: number,
|
||||
@@ -10144,7 +10161,7 @@ namespace IncrementalParser {
|
||||
visitNode(sourceFile);
|
||||
return;
|
||||
|
||||
function visitNode(child: IncrementalNode) {
|
||||
function visitNode(child: Node) {
|
||||
Debug.assert(child.pos <= child.end);
|
||||
if (child.pos > changeRangeOldEnd) {
|
||||
// Node is entirely past the change range. We need to move both its pos and
|
||||
@@ -10158,7 +10175,7 @@ namespace IncrementalParser {
|
||||
// be able to use.
|
||||
const fullEnd = child.end;
|
||||
if (fullEnd >= changeStart) {
|
||||
child.intersectsChange = true;
|
||||
markAsIntersectingIncrementalChange(child);
|
||||
unsetNodeChildren(child);
|
||||
|
||||
// Adjust the pos or end (or both) of the intersecting element accordingly.
|
||||
@@ -10166,7 +10183,7 @@ namespace IncrementalParser {
|
||||
forEachChild(child, visitNode as (node: Node) => void, visitArray as (nodes: NodeArray<Node>) => void);
|
||||
if (hasJSDocNodes(child)) {
|
||||
for (const jsDocComment of child.jsDoc!) {
|
||||
visitNode(jsDocComment as Node as IncrementalNode);
|
||||
visitNode(jsDocComment);
|
||||
}
|
||||
}
|
||||
checkNodePositions(child, aggressiveChecks);
|
||||
@@ -10177,7 +10194,7 @@ namespace IncrementalParser {
|
||||
Debug.assert(fullEnd < changeStart);
|
||||
}
|
||||
|
||||
function visitArray(array: IncrementalNodeArray) {
|
||||
function visitArray(array: NodeArray<Node>) {
|
||||
Debug.assert(array.pos <= array.end);
|
||||
if (array.pos > changeRangeOldEnd) {
|
||||
// Array is entirely after the change range. We need to move it, and move any of
|
||||
@@ -10191,7 +10208,7 @@ namespace IncrementalParser {
|
||||
// be able to use.
|
||||
const fullEnd = array.end;
|
||||
if (fullEnd >= changeStart) {
|
||||
array.intersectsChange = true;
|
||||
markAsIntersectingIncrementalChange(array);
|
||||
|
||||
// Adjust the pos or end (or both) of the intersecting array accordingly.
|
||||
adjustIntersectingElement(array, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta);
|
||||
@@ -10340,25 +10357,11 @@ namespace IncrementalParser {
|
||||
}
|
||||
}
|
||||
|
||||
interface IncrementalElement extends ReadonlyTextRange {
|
||||
readonly parent: Node;
|
||||
intersectsChange: boolean;
|
||||
length?: number;
|
||||
}
|
||||
|
||||
export interface IncrementalNode extends Node, IncrementalElement {
|
||||
hasBeenIncrementallyParsed: boolean;
|
||||
}
|
||||
|
||||
interface IncrementalNodeArray extends NodeArray<IncrementalNode>, IncrementalElement {
|
||||
length: number;
|
||||
}
|
||||
|
||||
// Allows finding nodes in the source file at a certain position in an efficient manner.
|
||||
// The implementation takes advantage of the calling pattern it knows the parser will
|
||||
// make in order to optimize finding nodes as quickly as possible.
|
||||
export interface SyntaxCursor {
|
||||
currentNode(position: number): IncrementalNode;
|
||||
currentNode(position: number): Node;
|
||||
}
|
||||
|
||||
export function createSyntaxCursor(sourceFile: SourceFile): SyntaxCursor {
|
||||
@@ -10400,7 +10403,7 @@ namespace IncrementalParser {
|
||||
|
||||
// Either we don'd have a node, or we have a node at the position being asked for.
|
||||
Debug.assert(!current || current.pos === position);
|
||||
return current as IncrementalNode;
|
||||
return current;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user