From aeb428e9e52d1c0bed63035876ee1403a0762cf1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 5 Sep 2014 17:59:52 -0700 Subject: [PATCH] Fix incremental parsing bug due to non invalidating cached data in nodes. --- src/harness/fourslash.ts | 2 ++ src/services/syntax/parser.ts | 27 ++++++++++++++------ tests/cases/fourslash/incrementalParsing1.ts | 26 +++++++++++++++++++ 3 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 tests/cases/fourslash/incrementalParsing1.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index c4cb3662ee..c571a7d9f9 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1084,6 +1084,8 @@ module FourSlash { // Make the edit var ch = text.charAt(i); this.languageServiceShimHost.editScript(this.activeFile.fileName, offset, offset, ch); + this.languageService.getBraceMatchingAtPosition(this.activeFile.fileName, offset); + this.updateMarkersForEdit(this.activeFile.fileName, offset, offset, ch); this.editCheckpoint(this.activeFile.fileName); offset++; diff --git a/src/services/syntax/parser.ts b/src/services/syntax/parser.ts index 0c886f129f..31a87b857b 100644 --- a/src/services/syntax/parser.ts +++ b/src/services/syntax/parser.ts @@ -544,18 +544,29 @@ module TypeScript.Parser { } } - function replaceTokenInParent(oldToken: ISyntaxToken, newToken: ISyntaxToken): void { + function replaceTokenInParent(node: ISyntaxNode, oldToken: ISyntaxToken, newToken: ISyntaxToken): void { // oldToken may be parented by a node or a list. replaceTokenInParentWorker(oldToken, newToken); var parent = oldToken.parent; newToken.parent = parent; - // Parent must be a list or a node. All of those have a 'data' element. - Debug.assert(isNode(parent) || isList(parent) || isSeparatedList(parent)); - var dataElement = <{ data: number }>parent; - if (dataElement.data) { - dataElement.data &= SyntaxConstants.NodeParsedInStrictModeMask + // Walk upwards to our outermost node, clearing hte cached 'data' in it. This will + // make sure that the fullWidths and incrementally unusable bits are computed correctly + // when next requested. + while (true) { + // Parent must be a list or a node. All of those have a 'data' element. + Debug.assert(isNode(parent) || isList(parent) || isSeparatedList(parent)); + var dataElement = <{ data: number }>parent; + if (dataElement.data) { + dataElement.data &= SyntaxConstants.NodeParsedInStrictModeMask + } + + if (parent === node) { + break; + } + + parent = parent.parent; } } @@ -602,7 +613,7 @@ module TypeScript.Parser { var oldToken = lastToken(node); var newToken = addSkippedTokenAfterToken(oldToken, skippedToken); - replaceTokenInParent(oldToken, newToken); + replaceTokenInParent(node, oldToken, newToken); return node; } @@ -611,7 +622,7 @@ module TypeScript.Parser { var oldToken = firstToken(node); var newToken = addSkippedTokensBeforeToken(oldToken, skippedTokens); - replaceTokenInParent(oldToken, newToken); + replaceTokenInParent(node, oldToken, newToken); } return node; diff --git a/tests/cases/fourslash/incrementalParsing1.ts b/tests/cases/fourslash/incrementalParsing1.ts new file mode 100644 index 0000000000..4d9078bf10 --- /dev/null +++ b/tests/cases/fourslash/incrementalParsing1.ts @@ -0,0 +1,26 @@ +/// + +//// function foo() { +//// function getOccurrencesAtPosition() { +//// switch (node) { +//// enum /*1*/ +//// } +//// +//// return undefined; +//// +//// function keywordToReferenceEntry() { +//// } +//// } +//// +//// return { +//// getEmitOutput: (filename): Bar => null, +//// }; +//// } + +// Force a syntax tree ot be created. +verify.noMatchingBracePositionInCurrentFile(0); + +// make sure we check the tree after every edit. +diagnostics.setTypingFidelity(TypingFidelity.High); +goTo.marker('1'); +edit.insert('Fo');