@@ -318,7 +318,6 @@ import {
318318 QuestionToken,
319319 ReadonlyKeyword,
320320 ReadonlyPragmaMap,
321- ReadonlyTextRange,
322321 ResolutionMode,
323322 RestTypeNode,
324323 ReturnStatement,
@@ -1950,7 +1949,7 @@ namespace Parser {
19501949 function currentNode(position: number) {
19511950 const node = baseSyntaxCursor.currentNode(position);
19521951 if (topLevel && node && containsPossibleTopLevelAwait(node)) {
1953- node.intersectsChange = true ;
1952+ markAsIntersectingIncrementalChange( node) ;
19541953 }
19551954 return node;
19561955 }
@@ -3120,7 +3119,7 @@ namespace Parser {
31203119 // Can't reuse a node that intersected the change range.
31213120 // Can't reuse a node that contains a parse error. This is necessary so that we
31223121 // produce the same set of errors again.
3123- if (nodeIsMissing(node) || node.intersectsChange || containsParseError(node)) {
3122+ if (nodeIsMissing(node) || intersectsIncrementalChange( node) || containsParseError(node)) {
31243123 return undefined;
31253124 }
31263125
@@ -9844,6 +9843,25 @@ namespace Parser {
98449843 }
98459844}
98469845
9846+ const incrementallyParsedFiles = new WeakSet<SourceFile>();
9847+
9848+ function markAsIncrementallyParsed(sourceFile: SourceFile) {
9849+ if (incrementallyParsedFiles.has(sourceFile)) {
9850+ Debug.fail("Source file has already been incrementally parsed");
9851+ }
9852+ incrementallyParsedFiles.add(sourceFile);
9853+ }
9854+
9855+ const intersectingChangeSet = new WeakSet<Node | NodeArray<Node>>();
9856+
9857+ function intersectsIncrementalChange(node: Node | NodeArray<Node>): boolean {
9858+ return intersectingChangeSet.has(node);
9859+ }
9860+
9861+ function markAsIntersectingIncrementalChange(node: Node | NodeArray<Node>) {
9862+ intersectingChangeSet.add(node);
9863+ }
9864+
98479865namespace IncrementalParser {
98489866 export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile {
98499867 aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive);
@@ -9866,10 +9884,8 @@ namespace IncrementalParser {
98669884 // This is because we do incremental parsing in-place. i.e. we take nodes from the old
98679885 // tree and give them new positions and parents. From that point on, trusting the old
98689886 // tree at all is not possible as far too much of it may violate invariants.
9869- const incrementalSourceFile = sourceFile as Node as IncrementalNode;
9870- Debug.assert(!incrementalSourceFile.hasBeenIncrementallyParsed);
9871- incrementalSourceFile.hasBeenIncrementallyParsed = true;
9872- Parser.fixupParentReferences(incrementalSourceFile);
9887+ markAsIncrementallyParsed(sourceFile);
9888+ Parser.fixupParentReferences(sourceFile);
98739889 const oldText = sourceFile.text;
98749890 const syntaxCursor = createSyntaxCursor(sourceFile);
98759891
@@ -9908,7 +9924,7 @@ namespace IncrementalParser {
99089924 //
99099925 // Also, mark any syntax elements that intersect the changed span. We know, up front,
99109926 // that we cannot reuse these elements.
9911- updateTokenPositionsAndMarkElements(incrementalSourceFile , changeRange.span.start, textSpanEnd(changeRange.span), textSpanEnd(textChangeRangeNewSpan(changeRange)), delta, oldText, newText, aggressiveChecks);
9927+ updateTokenPositionsAndMarkElements(sourceFile , changeRange.span.start, textSpanEnd(changeRange.span), textSpanEnd(textChangeRangeNewSpan(changeRange)), delta, oldText, newText, aggressiveChecks);
99129928
99139929 // Now that we've set up our internal incremental state just proceed and parse the
99149930 // source file in the normal fashion. When possible the parser will retrieve and
@@ -9984,18 +10000,18 @@ namespace IncrementalParser {
998410000 }
998510001 }
998610002
9987- function moveElementEntirelyPastChangeRange(element: IncrementalNode , isArray: false, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
9988- function moveElementEntirelyPastChangeRange(element: IncrementalNodeArray , isArray: true, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
9989- function moveElementEntirelyPastChangeRange(element: IncrementalNode | IncrementalNodeArray , isArray: boolean, delta: number, oldText: string, newText: string, aggressiveChecks: boolean) {
10003+ function moveElementEntirelyPastChangeRange(element: Node , isArray: false, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
10004+ function moveElementEntirelyPastChangeRange(element: NodeArray<Node> , isArray: true, delta: number, oldText: string, newText: string, aggressiveChecks: boolean): void;
10005+ function moveElementEntirelyPastChangeRange(element: Node | NodeArray<Node> , isArray: boolean, delta: number, oldText: string, newText: string, aggressiveChecks: boolean) {
999010006 if (isArray) {
9991- visitArray(element as IncrementalNodeArray );
10007+ visitArray(element as NodeArray<Node> );
999210008 }
999310009 else {
9994- visitNode(element as IncrementalNode );
10010+ visitNode(element as Node );
999510011 }
999610012 return;
999710013
9998- function visitNode(node: IncrementalNode ) {
10014+ function visitNode(node: Node ) {
999910015 let text = "";
1000010016 if (aggressiveChecks && shouldCheckNode(node)) {
1000110017 text = oldText.substring(node.pos, node.end);
@@ -10014,13 +10030,13 @@ namespace IncrementalParser {
1001410030 forEachChild(node, visitNode as (node: Node) => void, visitArray as (nodes: NodeArray<Node>) => void);
1001510031 if (hasJSDocNodes(node)) {
1001610032 for (const jsDocComment of node.jsDoc!) {
10017- visitNode(jsDocComment as Node as IncrementalNode );
10033+ visitNode(jsDocComment);
1001810034 }
1001910035 }
1002010036 checkNodePositions(node, aggressiveChecks);
1002110037 }
1002210038
10023- function visitArray(array: IncrementalNodeArray ) {
10039+ function visitArray(array: NodeArray<Node> ) {
1002410040 setTextRangePosEnd(array, array.pos + delta, array.end + delta);
1002510041
1002610042 for (const node of array) {
@@ -10040,7 +10056,7 @@ namespace IncrementalParser {
1004010056 return false;
1004110057 }
1004210058
10043- function adjustIntersectingElement(element: IncrementalElement , changeStart: number, changeRangeOldEnd: number, changeRangeNewEnd: number, delta: number) {
10059+ function adjustIntersectingElement(element: Node | NodeArray<Node> , changeStart: number, changeRangeOldEnd: number, changeRangeNewEnd: number, delta: number) {
1004410060 Debug.assert(element.end >= changeStart, "Adjusting an element that was entirely before the change range");
1004510061 Debug.assert(element.pos <= changeRangeOldEnd, "Adjusting an element that was entirely after the change range");
1004610062 Debug.assert(element.pos <= element.end);
@@ -10106,9 +10122,10 @@ namespace IncrementalParser {
1010610122 Math.min(element.end, changeRangeNewEnd);
1010710123
1010810124 Debug.assert(pos <= end);
10109- if (element.parent) {
10110- Debug.assertGreaterThanOrEqual(pos, element.parent.pos);
10111- Debug.assertLessThanOrEqual(end, element.parent.end);
10125+ if ((element as any).parent) {
10126+ const parent = (element as any).parent as Node;
10127+ Debug.assertGreaterThanOrEqual(pos, parent.pos);
10128+ Debug.assertLessThanOrEqual(end, parent.end);
1011210129 }
1011310130
1011410131 setTextRangePosEnd(element, pos, end);
@@ -10132,7 +10149,7 @@ namespace IncrementalParser {
1013210149 }
1013310150
1013410151 function updateTokenPositionsAndMarkElements(
10135- sourceFile: IncrementalNode ,
10152+ sourceFile: SourceFile ,
1013610153 changeStart: number,
1013710154 changeRangeOldEnd: number,
1013810155 changeRangeNewEnd: number,
@@ -10144,7 +10161,7 @@ namespace IncrementalParser {
1014410161 visitNode(sourceFile);
1014510162 return;
1014610163
10147- function visitNode(child: IncrementalNode ) {
10164+ function visitNode(child: Node ) {
1014810165 Debug.assert(child.pos <= child.end);
1014910166 if (child.pos > changeRangeOldEnd) {
1015010167 // Node is entirely past the change range. We need to move both its pos and
@@ -10158,15 +10175,15 @@ namespace IncrementalParser {
1015810175 // be able to use.
1015910176 const fullEnd = child.end;
1016010177 if (fullEnd >= changeStart) {
10161- child.intersectsChange = true ;
10178+ markAsIntersectingIncrementalChange( child) ;
1016210179 unsetNodeChildren(child);
1016310180
1016410181 // Adjust the pos or end (or both) of the intersecting element accordingly.
1016510182 adjustIntersectingElement(child, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta);
1016610183 forEachChild(child, visitNode as (node: Node) => void, visitArray as (nodes: NodeArray<Node>) => void);
1016710184 if (hasJSDocNodes(child)) {
1016810185 for (const jsDocComment of child.jsDoc!) {
10169- visitNode(jsDocComment as Node as IncrementalNode );
10186+ visitNode(jsDocComment);
1017010187 }
1017110188 }
1017210189 checkNodePositions(child, aggressiveChecks);
@@ -10177,7 +10194,7 @@ namespace IncrementalParser {
1017710194 Debug.assert(fullEnd < changeStart);
1017810195 }
1017910196
10180- function visitArray(array: IncrementalNodeArray ) {
10197+ function visitArray(array: NodeArray<Node> ) {
1018110198 Debug.assert(array.pos <= array.end);
1018210199 if (array.pos > changeRangeOldEnd) {
1018310200 // Array is entirely after the change range. We need to move it, and move any of
@@ -10191,7 +10208,7 @@ namespace IncrementalParser {
1019110208 // be able to use.
1019210209 const fullEnd = array.end;
1019310210 if (fullEnd >= changeStart) {
10194- array.intersectsChange = true ;
10211+ markAsIntersectingIncrementalChange( array) ;
1019510212
1019610213 // Adjust the pos or end (or both) of the intersecting array accordingly.
1019710214 adjustIntersectingElement(array, changeStart, changeRangeOldEnd, changeRangeNewEnd, delta);
@@ -10340,25 +10357,11 @@ namespace IncrementalParser {
1034010357 }
1034110358 }
1034210359
10343- interface IncrementalElement extends ReadonlyTextRange {
10344- readonly parent: Node;
10345- intersectsChange: boolean;
10346- length?: number;
10347- }
10348-
10349- export interface IncrementalNode extends Node, IncrementalElement {
10350- hasBeenIncrementallyParsed: boolean;
10351- }
10352-
10353- interface IncrementalNodeArray extends NodeArray<IncrementalNode>, IncrementalElement {
10354- length: number;
10355- }
10356-
1035710360 // Allows finding nodes in the source file at a certain position in an efficient manner.
1035810361 // The implementation takes advantage of the calling pattern it knows the parser will
1035910362 // make in order to optimize finding nodes as quickly as possible.
1036010363 export interface SyntaxCursor {
10361- currentNode(position: number): IncrementalNode ;
10364+ currentNode(position: number): Node ;
1036210365 }
1036310366
1036410367 export function createSyntaxCursor(sourceFile: SourceFile): SyntaxCursor {
@@ -10400,7 +10403,7 @@ namespace IncrementalParser {
1040010403
1040110404 // Either we don'd have a node, or we have a node at the position being asked for.
1040210405 Debug.assert(!current || current.pos === position);
10403- return current as IncrementalNode ;
10406+ return current;
1040410407 },
1040510408 };
1040610409
0 commit comments