From 379d03b5a732824e561bc4c67e5232431c260c7c Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Thu, 22 Jan 2015 18:20:40 -0800 Subject: [PATCH 1/4] use nameTable - services layer storage of identifiers in the file --- src/services/services.ts | 64 +++++++++++++++---- .../fourslash/getOccurrencesAfterEdit.ts | 15 +++++ 2 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 tests/cases/fourslash/getOccurrencesAfterEdit.ts diff --git a/src/services/services.ts b/src/services/services.ts index 84e5e7fefa5..e662f6f2de1 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -59,7 +59,7 @@ module ts { isOpen: boolean; version: string; scriptSnapshot: IScriptSnapshot; - + nameTable: Map; getNamedDeclarations(): Declaration[]; } @@ -750,6 +750,7 @@ module ts { public isOpen: boolean; public languageVersion: ScriptTarget; public identifiers: Map; + public nameTable: Map; private namedDeclarations: Declaration[]; @@ -1546,6 +1547,8 @@ module ts { export function createLanguageServiceSourceFile(filename: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, isOpen: boolean, setNodeParents: boolean): SourceFile { var sourceFile = createSourceFile(filename, scriptSnapshot.getText(0, scriptSnapshot.getLength()), scriptTarget, setNodeParents); setSourceFileFields(sourceFile, scriptSnapshot, version, isOpen); + // after full parsing we can use table with interned strings as name table + sourceFile.nameTable = sourceFile.identifiers; return sourceFile; } @@ -1577,6 +1580,9 @@ module ts { if (!disableIncrementalParsing) { var newSourceFile = sourceFile.update(scriptSnapshot.getText(0, scriptSnapshot.getLength()), textChangeRange); setSourceFileFields(newSourceFile, scriptSnapshot, version, isOpen); + // after incremental parsing nameTable might not be up-to-date + // drop it so it can be lazily recreated later + newSourceFile.nameTable = undefined; return newSourceFile; } } @@ -3235,7 +3241,7 @@ module ts { if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword || isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || isNameOfExternalModuleImportOrDeclaration(node)) { - return getReferencesForNode(node, [sourceFile], /*findInStrings:*/ false, /*findInComments:*/ false); + return getReferencesForNode(node, [sourceFile], /*searchOnlyInCurrentFile*/ true, /*findInStrings:*/ false, /*findInComments:*/ false); } switch (node.kind) { @@ -3788,10 +3794,31 @@ module ts { } Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.NumericLiteral || node.kind === SyntaxKind.StringLiteral); - return getReferencesForNode(node, program.getSourceFiles(), findInStrings, findInComments); + return getReferencesForNode(node, program.getSourceFiles(), /*searchOnlyInCurrentFile*/ false, findInStrings, findInComments); } - function getReferencesForNode(node: Node, sourceFiles: SourceFile[], findInStrings: boolean, findInComments: boolean): ReferenceEntry[] { + function initializeNameTable(sourceFile: SourceFile): void { + var nameTable: Map = {}; + + walk(sourceFile); + sourceFile.nameTable = nameTable; + + function walk(node: Node) { + switch (node.kind) { + case SyntaxKind.Identifier: + nameTable[(node).text] = (node).text; + break; + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + nameTable[(node).text] = (node).text; + break; + default: + forEachChild(node, walk); + } + } + } + + function getReferencesForNode(node: Node, sourceFiles: SourceFile[], searchOnlyInCurrentFile: boolean, findInStrings: boolean, findInComments: boolean): ReferenceEntry[] { // Labels if (isLabelName(node)) { if (isJumpStatementTarget(node)) { @@ -3847,15 +3874,28 @@ module ts { getReferencesInNode(scope, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); } else { - var internedName = getInternedName(symbol, declarations) - forEach(sourceFiles, sourceFile => { - cancellationToken.throwIfCancellationRequested(); + if (searchOnlyInCurrentFile) { + Debug.assert(sourceFiles.length === 1); + result = []; + getReferencesInNode(sourceFiles[0], symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); + } + else { + var internedName = getInternedName(symbol, declarations) + forEach(sourceFiles, sourceFile => { + cancellationToken.throwIfCancellationRequested(); - if (lookUp(sourceFile.identifiers, internedName)) { - result = result || []; - getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); - } - }); + if (!sourceFile.nameTable) { + initializeNameTable(sourceFile) + } + + Debug.assert(sourceFile.nameTable !== undefined); + + if (lookUp(sourceFile.nameTable, internedName)) { + result = result || []; + getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result); + } + }); + } } return result; diff --git a/tests/cases/fourslash/getOccurrencesAfterEdit.ts b/tests/cases/fourslash/getOccurrencesAfterEdit.ts new file mode 100644 index 00000000000..f878da9356d --- /dev/null +++ b/tests/cases/fourslash/getOccurrencesAfterEdit.ts @@ -0,0 +1,15 @@ +/// + +/////*0*/ +////function f(s: string) { +//// s.con/*1*/structor +////} + +goTo.marker("1"); +verify.occurrencesAtPositionCount(1); + +goTo.marker("0"); +edit.insert("\r\n"); + +goTo.marker("1"); +verify.occurrencesAtPositionCount(1); \ No newline at end of file From 05ec43a0f3e6669b35d9d7b4b41f4ae485e25619 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 23 Jan 2015 12:04:55 -0800 Subject: [PATCH 2/4] make sure that enum values are computed before the emit --- src/compiler/checker.ts | 4 ++-- src/harness/fourslash.ts | 12 ++++++++++++ .../constEnumsEmitOutputInMultipleFiles.ts | 18 ++++++++++++++++++ tests/cases/fourslash/fourslash.ts | 4 ++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fourslash/constEnumsEmitOutputInMultipleFiles.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7233c2e85d5..a606a3f00fd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10001,8 +10001,8 @@ module ts { if (symbol && (symbol.flags & SymbolFlags.EnumMember)) { var declaration = symbol.valueDeclaration; var constantValue: number; - if (declaration.kind === SyntaxKind.EnumMember && (constantValue = getNodeLinks(declaration).enumMemberValue) !== undefined) { - return constantValue; + if (declaration.kind === SyntaxKind.EnumMember) { + return getEnumMemberValue(declaration); } } diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index cfc83af76e5..512248197a4 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -548,6 +548,18 @@ module FourSlash { } } + public verifyGetEmitOutputForCurrentFile(expected: string): void { + var emit = this.languageService.getEmitOutput(this.activeFile.fileName); + if (emit.outputFiles.length !== 1) { + throw new Error("Expected exactly one output from emit of " + this.activeFile.fileName); + } + this.taoInvalidReason = 'verifyGetEmitOutputForCurrentFile impossible'; + var actual = emit.outputFiles[0].text; + if (actual !== expected) { + this.raiseError("Expected emit output to be '" + expected + "', but got '" + actual + "'"); + } + } + public verifyMemberListContains(symbol: string, text?: string, documentation?: string, kind?: string) { this.scenarioActions.push(''); this.scenarioActions.push(''); diff --git a/tests/cases/fourslash/constEnumsEmitOutputInMultipleFiles.ts b/tests/cases/fourslash/constEnumsEmitOutputInMultipleFiles.ts new file mode 100644 index 00000000000..0f6be184d8c --- /dev/null +++ b/tests/cases/fourslash/constEnumsEmitOutputInMultipleFiles.ts @@ -0,0 +1,18 @@ +/// + +// @Filename: a.ts +////const enum TestEnum { +//// Foo, Bar +////} +////var testFirstFile = TestEnum.Bar; + +// @Filename: b.ts +/////// +/////*1*/ +////var testInOtherFile = TestEnum.Bar; + +goTo.marker("1"); +verify.verifyGetEmitOutputForCurrentFile( +"/// \r\n\ +var testInOtherFile = 1 /* Bar */;\r\n" + ) \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 0f458041521..5b88b15f9fa 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -264,6 +264,10 @@ module FourSlashInterface { FourSlash.currentTestState.verifyCurrentFileContent(text); } + public verifyGetEmitOutputForCurrentFile(expected: string): void { + FourSlash.currentTestState.verifyGetEmitOutputForCurrentFile(expected); + } + public currentParameterHelpArgumentNameIs(name: string) { FourSlash.currentTestState.verifyCurrentParameterHelpName(name); } From 6040e55fa42fdb0877c0f062840667a7f3ea9c54 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 23 Jan 2015 15:36:36 -0800 Subject: [PATCH 3/4] add 'find all references' test --- .../fourslash/findReferencesAfterEdit.ts | 22 +++++++++++++++++++ .../cases/fourslash/getOccurencesAfterEdit.ts | 18 +++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/cases/fourslash/findReferencesAfterEdit.ts create mode 100644 tests/cases/fourslash/getOccurencesAfterEdit.ts diff --git a/tests/cases/fourslash/findReferencesAfterEdit.ts b/tests/cases/fourslash/findReferencesAfterEdit.ts new file mode 100644 index 00000000000..5eca1a3b8f2 --- /dev/null +++ b/tests/cases/fourslash/findReferencesAfterEdit.ts @@ -0,0 +1,22 @@ +/// + +// @Filename: a.ts +////interface A { +//// foo: string; +////} + +// @Filename: b.ts +/////// +/////*0*/ +////function foo(x: A) { +//// x.f/*1*/oo +////} + +goTo.marker("1"); +verify.referencesCountIs(2); + +goTo.marker("0"); +edit.insert("\r\n"); + +goTo.marker("1"); +verify.referencesCountIs(2); \ No newline at end of file diff --git a/tests/cases/fourslash/getOccurencesAfterEdit.ts b/tests/cases/fourslash/getOccurencesAfterEdit.ts new file mode 100644 index 00000000000..0654cc3962c --- /dev/null +++ b/tests/cases/fourslash/getOccurencesAfterEdit.ts @@ -0,0 +1,18 @@ +/// + +/////*0*/ +////interface A { +//// foo: string; +////} +////function foo(x: A) { +//// x.f/*1*/oo +////} + +goTo.marker("1"); +verify.occurrencesAtPositionCount(2); + +goTo.marker("0"); +edit.insert("\r\n"); + +goTo.marker("1"); +verify.occurrencesAtPositionCount(2); \ No newline at end of file From da6070bcf755e1f36cb8b61f8658f0d08a41c7ce Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 23 Jan 2015 15:39:50 -0800 Subject: [PATCH 4/4] fixed typo in test name --- .../cases/fourslash/getOccurencesAfterEdit.ts | 18 ------------------ .../cases/fourslash/getOccurrencesAfterEdit.ts | 11 +++++++---- 2 files changed, 7 insertions(+), 22 deletions(-) delete mode 100644 tests/cases/fourslash/getOccurencesAfterEdit.ts diff --git a/tests/cases/fourslash/getOccurencesAfterEdit.ts b/tests/cases/fourslash/getOccurencesAfterEdit.ts deleted file mode 100644 index 0654cc3962c..00000000000 --- a/tests/cases/fourslash/getOccurencesAfterEdit.ts +++ /dev/null @@ -1,18 +0,0 @@ -/// - -/////*0*/ -////interface A { -//// foo: string; -////} -////function foo(x: A) { -//// x.f/*1*/oo -////} - -goTo.marker("1"); -verify.occurrencesAtPositionCount(2); - -goTo.marker("0"); -edit.insert("\r\n"); - -goTo.marker("1"); -verify.occurrencesAtPositionCount(2); \ No newline at end of file diff --git a/tests/cases/fourslash/getOccurrencesAfterEdit.ts b/tests/cases/fourslash/getOccurrencesAfterEdit.ts index f878da9356d..0654cc3962c 100644 --- a/tests/cases/fourslash/getOccurrencesAfterEdit.ts +++ b/tests/cases/fourslash/getOccurrencesAfterEdit.ts @@ -1,15 +1,18 @@ /// /////*0*/ -////function f(s: string) { -//// s.con/*1*/structor +////interface A { +//// foo: string; +////} +////function foo(x: A) { +//// x.f/*1*/oo ////} goTo.marker("1"); -verify.occurrencesAtPositionCount(1); +verify.occurrencesAtPositionCount(2); goTo.marker("0"); edit.insert("\r\n"); goTo.marker("1"); -verify.occurrencesAtPositionCount(1); \ No newline at end of file +verify.occurrencesAtPositionCount(2); \ No newline at end of file