From 20576b40f6164fe8a5777497882b1d96e2c92e5c Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 2 Dec 2020 15:51:18 -0800 Subject: [PATCH 01/18] Naive implementation enough to build and write a test --- src/harness/client.ts | 12 +++ src/harness/fourslashImpl.ts | 30 ++++++-- src/harness/fourslashInterfaceImpl.ts | 4 + src/harness/harnessLanguageService.ts | 3 + src/server/protocol.ts | 22 ++++++ src/server/session.ts | 79 ++++++++++++++++---- src/services/findAllReferences.ts | 6 ++ src/services/services.ts | 8 ++ src/services/shims.ts | 13 ++++ src/services/types.ts | 1 + src/testRunner/unittests/tsserver/session.ts | 2 + tests/cases/fourslash/fourslash.ts | 1 + 12 files changed, 161 insertions(+), 20 deletions(-) diff --git a/src/harness/client.ts b/src/harness/client.ts index 230517b599db9..b6385203969c3 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -362,6 +362,18 @@ namespace ts.server { })); } + getFileReferences(fileName: string): ReferenceEntry[] { + const request = this.processRequest(CommandNames.FileReferences, { file: fileName }); + const response = this.processResponse(request); + + return response.body!.refs.map(entry => ({ // TODO: GH#18217 + fileName: entry.file, + textSpan: this.decodeSpan(entry), + isWriteAccess: entry.isWriteAccess, + isDefinition: entry.isDefinition, + })); + } + getEmitOutput(file: string): EmitOutput { const request = this.processRequest(protocol.CommandTypes.EmitOutput, { file }); const response = this.processResponse(request); diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 6af8cea9d039b..80aa23a936c91 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -1116,21 +1116,41 @@ namespace FourSlash { : ts.emptyArray; // Write input files + let baselineContent = this.getBaselineContentForGroupedReferences(refsByFile, markerName); + + // Write response JSON + baselineContent += JSON.stringify(references, undefined, 2); + Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baselineContent); + } + + public verifyBaselineGetFileReferences(fileName: string) { + const references = this.languageService.getFileReferences(fileName); + const refsByFile = references + ? ts.group(ts.sort(references, (a, b) => a.textSpan.start - b.textSpan.start), ref => ref.fileName) + : ts.emptyArray; + + // Write input files + let baselineContent = this.getBaselineContentForGroupedReferences(refsByFile); + + // Write response JSON + baselineContent += JSON.stringify(references, undefined, 2); + Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baselineContent); + } + + private getBaselineContentForGroupedReferences(refsByFile: readonly (readonly ts.ReferenceEntry[])[], markerName?: string) { + const marker = markerName !== undefined && this.getMarkerByName(markerName); let baselineContent = ""; for (const group of refsByFile) { baselineContent += getBaselineContentForFile(group[0].fileName, this.getFileContent(group[0].fileName)); baselineContent += "\n\n"; } - - // Write response JSON - baselineContent += JSON.stringify(references, undefined, 2); - Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baselineContent); + return baselineContent; function getBaselineContentForFile(fileName: string, content: string) { let newContent = `=== ${fileName} ===\n`; let pos = 0; for (const { textSpan } of refsByFile.find(refs => refs[0].fileName === fileName) ?? ts.emptyArray) { - if (fileName === marker.fileName && ts.textSpanContainsPosition(textSpan, marker.position)) { + if (marker && fileName === marker.fileName && ts.textSpanContainsPosition(textSpan, marker.position)) { newContent += "/*FIND ALL REFS*/"; } const end = textSpan.start + textSpan.length; diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index e96641c2dbb88..fd38bd572c1c6 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -336,6 +336,10 @@ namespace FourSlashInterface { this.state.verifyBaselineFindAllReferences(markerName); } + public baselineGetFileReferences(fileName: string) { + this.state.verifyBaselineGetFileReferences(fileName); + } + public referenceGroups(starts: ArrayOrSingle | ArrayOrSingle, parts: ReferenceGroup[]) { this.state.verifyReferenceGroups(starts, parts); } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index cc2f285fc5c8f..85164d58bb122 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -519,6 +519,9 @@ namespace Harness.LanguageService { findReferences(fileName: string, position: number): ts.ReferencedSymbol[] { return unwrapJSONCallResult(this.shim.findReferences(fileName, position)); } + getFileReferences(fileName: string): ts.ReferenceEntry[] { + return unwrapJSONCallResult(this.shim.getFileReferences(fileName)); + } getOccurrencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] { return unwrapJSONCallResult(this.shim.getOccurrencesAtPosition(fileName, position)); } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 1f77bbfd7eb45..ad8926470a5b0 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -37,6 +37,9 @@ namespace ts.server.protocol { /* @internal */ EmitOutput = "emit-output", Exit = "exit", + FileReferences = "fileReferences", + /* @internal */ + FileReferencesFull = "fileReferences-full", Format = "format", Formatonkey = "formatonkey", /* @internal */ @@ -1152,6 +1155,25 @@ namespace ts.server.protocol { body?: ReferencesResponseBody; } + export interface FileReferencesRequest extends FileRequest { + command: CommandTypes.FileReferences; + } + + export interface FileReferencesResponseBody { + /** + * The file locations referencing the symbol. + */ + refs: readonly ReferencesResponseItem[]; + /** + * The name of the symbol. + */ + symbolName: string; + } + + export interface FileReferencesResponse extends Response { + body?: FileReferencesResponseBody; + } + /** * Argument for RenameRequest request. */ diff --git a/src/server/session.ts b/src/server/session.ts index a842694bb9a6b..1fcb3129a4d8e 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -409,6 +409,29 @@ namespace ts.server { return outputs.filter(o => o.references.length !== 0); } + function combineProjectOutputForFileReferences( + projects: Projects, + defaultProject: Project, + fileName: string + ): readonly ReferenceEntry[] { + const outputs: ReferenceEntry[] = []; + + combineProjectOutputWorker( + projects, + defaultProject, + /*initialLocation*/ undefined, + project => { + for (const referenceEntry of project.getLanguageService().getFileReferences(fileName) || emptyArray) { + if (!contains(outputs, referenceEntry, documentSpansEqual)) { + outputs.push(referenceEntry); + } + } + }, + ); + + return outputs; + } + interface ProjectAndLocation { readonly project: Project; readonly location: TLocation; @@ -1509,7 +1532,7 @@ namespace ts.server { return arrayFrom(map.values()); } - private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | undefined | readonly ReferencedSymbol[] { + private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | readonly ReferencedSymbol[] { const file = toNormalizedPath(args.file); const projects = this.getProjects(args); const position = this.getPositionInFile(args, file); @@ -1528,22 +1551,28 @@ namespace ts.server { const nameSpan = nameInfo && nameInfo.textSpan; const symbolStartOffset = nameSpan ? scriptInfo.positionToLineOffset(nameSpan.start).offset : 0; const symbolName = nameSpan ? scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan)) : ""; - const refs: readonly protocol.ReferencesResponseItem[] = flatMap(references, referencedSymbol => - referencedSymbol.references.map(({ fileName, textSpan, contextSpan, isWriteAccess, isDefinition }): protocol.ReferencesResponseItem => { - const scriptInfo = Debug.checkDefined(this.projectService.getScriptInfo(fileName)); - const span = toProtocolTextSpanWithContext(textSpan, contextSpan, scriptInfo); - const lineSpan = scriptInfo.lineToTextSpan(span.start.line - 1); - const lineText = scriptInfo.getSnapshot().getText(lineSpan.start, textSpanEnd(lineSpan)).replace(/\r|\n/g, ""); - return { - file: fileName, - ...span, - lineText, - isWriteAccess, - isDefinition - }; - })); + const refs: readonly protocol.ReferencesResponseItem[] = flatMap(references, referencedSymbol => { + return referencedSymbol.references.map(entry => referenceEntryToReferencesResponseItem(this.projectService, entry)); + }); return { refs, symbolName, symbolStartOffset, symbolDisplayString }; } + + private getFileReferences(args: protocol.FileRequestArgs, simplifiedResult: boolean): protocol.FileReferencesResponseBody | readonly ReferenceEntry[] { + const projects = this.getProjects(args); + const references = combineProjectOutputForFileReferences( + projects, + this.getDefaultProject(args), + args.file, + ); + + if (!simplifiedResult) return references; + const refs = references.map(entry => referenceEntryToReferencesResponseItem(this.projectService, entry)); + return { + refs, + symbolName: `"${args.file}"` + }; + } + /** * @param fileName is the name of the file to be opened * @param fileContent is a version of the file content that is known to be more up to date than the one on disk @@ -2639,6 +2668,12 @@ namespace ts.server { [CommandNames.GetSpanOfEnclosingComment]: (request: protocol.SpanOfEnclosingCommentRequest) => { return this.requiredResponse(this.getSpanOfEnclosingComment(request.arguments)); }, + [CommandNames.FileReferences]: (request: protocol.FileReferencesRequest) => { + return this.requiredResponse(this.getFileReferences(request.arguments, /*simplifiedResult*/ true)); + }, + [CommandNames.FileReferencesFull]: (request: protocol.FileReferencesRequest) => { + return this.requiredResponse(this.getFileReferences(request.arguments, /*simplifiedResult*/ false)); + }, [CommandNames.Format]: (request: protocol.FormatRequest) => { return this.requiredResponse(this.getFormattingEditsForRange(request.arguments)); }, @@ -3060,4 +3095,18 @@ namespace ts.server { return text; } + + function referenceEntryToReferencesResponseItem(projectService: ProjectService, { fileName, textSpan, contextSpan, isWriteAccess, isDefinition }: ReferenceEntry): protocol.ReferencesResponseItem { + const scriptInfo = Debug.checkDefined(projectService.getScriptInfo(fileName)); + const span = toProtocolTextSpanWithContext(textSpan, contextSpan, scriptInfo); + const lineSpan = scriptInfo.lineToTextSpan(span.start.line - 1); + const lineText = scriptInfo.getSnapshot().getText(lineSpan.start, textSpanEnd(lineSpan)).replace(/\r|\n/g, ""); + return { + file: fileName, + ...span, + lineText, + isWriteAccess, + isDefinition + }; + } } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index bc6e1f4cb8071..fc931b648e0b4 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -642,6 +642,12 @@ namespace ts.FindAllReferences { return mergeReferences(program, moduleReferences, references, moduleReferencesOfExportTarget); } + export function getReferencedSymbolsForFileName(fileName: string, program: Program, sourceFiles: readonly SourceFile[], sourceFilesSet: ReadonlySet = new Set(sourceFiles.map(f => f.fileName))): readonly SymbolAndEntries[] | undefined { + const moduleSymbol = program.getSourceFile(fileName)?.symbol; + if (!moduleSymbol) return undefined; + return getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet); + } + function getMergedAliasedSymbolOfNamespaceExportDeclaration(node: Node, symbol: Symbol, checker: TypeChecker) { if (node.parent && isNamespaceExportDeclaration(node.parent)) { const aliasedSymbol = checker.getAliasedSymbol(symbol); diff --git a/src/services/services.ts b/src/services/services.ts index a891c2297d6d7..218e2deb77f54 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1728,6 +1728,13 @@ namespace ts { return FindAllReferences.findReferencedSymbols(program, cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position); } + function getFileReferences(fileName: string): ReferenceEntry[] { + synchronizeHostData(); + return flatMapToMutable( + FindAllReferences.Core.getReferencedSymbolsForFileName(fileName, program, program.getSourceFiles()), + symbolAndEntries => symbolAndEntries.references.map(FindAllReferences.toReferenceEntry)); + } + function getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles = false): NavigateToItem[] { synchronizeHostData(); const sourceFiles = fileName ? [getValidSourceFile(fileName)] : program.getSourceFiles(); @@ -2526,6 +2533,7 @@ namespace ts { getTypeDefinitionAtPosition, getReferencesAtPosition, findReferences, + getFileReferences, getOccurrencesAtPosition, getDocumentHighlights, getNameOrDottedNameSpan, diff --git a/src/services/shims.ts b/src/services/shims.ts index 14f6a61e7478f..30144210271ea 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -208,6 +208,12 @@ namespace ts { */ findReferences(fileName: string, position: number): string; + /** + * Returns a JSON-encoded value of the type: + * { fileName: string; textSpan: { start: number; length: number}; isWriteAccess: boolean, isDefinition?: boolean }[] + */ + getFileReferences(fileName: string): string; + /** * @deprecated * Returns a JSON-encoded value of the type: @@ -916,6 +922,13 @@ namespace ts { ); } + public getFileReferences(fileName: string) { + return this.forwardJSONCall( + `getFileReferences('${fileName})`, + () => this.languageService.getFileReferences(fileName) + ); + } + public getOccurrencesAtPosition(fileName: string, position: number): string { return this.forwardJSONCall( `getOccurrencesAtPosition('${fileName}', ${position})`, diff --git a/src/services/types.ts b/src/services/types.ts index bc7d7328a70ae..35815ade6f53f 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -468,6 +468,7 @@ namespace ts { getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] | undefined; findReferences(fileName: string, position: number): ReferencedSymbol[] | undefined; getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] | undefined; + getFileReferences(fileName: string): ReferenceEntry[]; /** @deprecated */ getOccurrencesAtPosition(fileName: string, position: number): readonly ReferenceEntry[] | undefined; diff --git a/src/testRunner/unittests/tsserver/session.ts b/src/testRunner/unittests/tsserver/session.ts index 8084b79056b30..96bae928817db 100644 --- a/src/testRunner/unittests/tsserver/session.ts +++ b/src/testRunner/unittests/tsserver/session.ts @@ -207,6 +207,8 @@ namespace ts.server { CommandNames.Implementation, CommandNames.ImplementationFull, CommandNames.Exit, + CommandNames.FileReferences, + CommandNames.FileReferencesFull, CommandNames.Format, CommandNames.Formatonkey, CommandNames.FormatFull, diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 98298bbfe5f21..9f3f8eb1e993a 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -290,6 +290,7 @@ declare namespace FourSlashInterface { verifyGetEmitOutputForCurrentFile(expected: string): void; verifyGetEmitOutputContentsForCurrentFile(expected: ts.OutputFile[]): void; baselineFindAllReferences(markerName: string): void; + baselineGetFileReferences(fileName: string): void; noReferences(markerNameOrRange?: string | Range): void; symbolAtLocation(startRange: Range, ...declarationRanges: Range[]): void; typeOfSymbolAtLocation(range: Range, symbol: any, expected: string): void; From 8b740158515f98013363f6c035e20169efc38e26 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 2 Dec 2020 15:56:27 -0800 Subject: [PATCH 02/18] Add simple test --- tests/cases/fourslash/getFileReferences1.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/cases/fourslash/getFileReferences1.ts diff --git a/tests/cases/fourslash/getFileReferences1.ts b/tests/cases/fourslash/getFileReferences1.ts new file mode 100644 index 0000000000000..82ce2af09bbb3 --- /dev/null +++ b/tests/cases/fourslash/getFileReferences1.ts @@ -0,0 +1,16 @@ +/// + +// @Filename: /project/a.ts +//// export const a = 0; + +// @Filename: /project/b.ts +//// import "./a"; + +// @Filename: /project/c.ts +//// import {} from "./a"; + +// @Filename: /project/d.ts +//// import { a } from "/project/a"; +//// type T = typeof import("./a").a; + +verify.baselineGetFileReferences("/project/a.ts"); From cbd885cec18131399c8929bd844e48a8de3ab519 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 3 Dec 2020 09:45:32 -0800 Subject: [PATCH 03/18] Add project references test --- .../server/getFileReferences_server1.ts | 19 +++++++++++++ .../server/getFileReferences_server2.ts | 27 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/cases/fourslash/server/getFileReferences_server1.ts create mode 100644 tests/cases/fourslash/server/getFileReferences_server2.ts diff --git a/tests/cases/fourslash/server/getFileReferences_server1.ts b/tests/cases/fourslash/server/getFileReferences_server1.ts new file mode 100644 index 0000000000000..020b6ec586ca4 --- /dev/null +++ b/tests/cases/fourslash/server/getFileReferences_server1.ts @@ -0,0 +1,19 @@ +/// + +// @Filename: /project/tsconfig.json +//// {} + +// @Filename: /project/a.ts +//// export const a = 0; + +// @Filename: /project/b.ts +//// import "./a"; + +// @Filename: /project/c.ts +//// import {} from "./a"; + +// @Filename: /project/d.ts +//// import { a } from "/project/a"; +//// type T = typeof import("./a").a; + +verify.baselineGetFileReferences("/project/a.ts"); diff --git a/tests/cases/fourslash/server/getFileReferences_server2.ts b/tests/cases/fourslash/server/getFileReferences_server2.ts new file mode 100644 index 0000000000000..0217444eeee59 --- /dev/null +++ b/tests/cases/fourslash/server/getFileReferences_server2.ts @@ -0,0 +1,27 @@ +/// + +// @Filename: /tsconfig.json +//// { "files": [], "references": [{ "path": "packages/server" }, { "path": "packages/client" }] } + +// @Filename: /packages/shared/tsconfig.json +//// { "compilerOptions": { "rootDir": "src", "outDir": "dist", "composite": true } } + +// @Filename: /packages/shared/src/referenced.ts +//// export {}; + +// @Filename: /packages/server/tsconfig.json +//// { "compilerOptions": { "checkJs": true }, "references": [{ "path": "../shared" }] } + +// @Filename: /packages/server/index.js +//// const mod = require("../shared/src/referenced"); + +// @Filename: /packages/server/router.js +//// const blah = require("../shared/dist/referenced"); + +// @Filename: /packages/client/tsconfig.json +//// { "compilerOptions": { "paths": { "@shared/*": ["../shared/src/*"] } }, "references": [{ "path": "../shared" }] } + +// @Filename: /packages/client/index.ts +//// import "@shared/referenced"; + +verify.baselineGetFileReferences("/packages/shared/src/referenced.ts"); From 9fed8c2295a8e2a99511bb1ac416f38969c75e3d Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 3 Dec 2020 10:24:53 -0800 Subject: [PATCH 04/18] Add deduplication test, accept baselines --- .../getFileReferences1.baseline.jsonc | 64 +++++++++++++++++++ ...tFileReferences_deduplicate.baseline.jsonc | 26 ++++++++ .../getFileReferences_server1.baseline.jsonc | 48 ++++++++++++++ .../getFileReferences_server2.baseline.jsonc | 38 +++++++++++ .../server/getFileReferences_deduplicate.ts | 23 +++++++ 5 files changed, 199 insertions(+) create mode 100644 tests/baselines/reference/getFileReferences1.baseline.jsonc create mode 100644 tests/baselines/reference/getFileReferences_deduplicate.baseline.jsonc create mode 100644 tests/baselines/reference/getFileReferences_server1.baseline.jsonc create mode 100644 tests/baselines/reference/getFileReferences_server2.baseline.jsonc create mode 100644 tests/cases/fourslash/server/getFileReferences_deduplicate.ts diff --git a/tests/baselines/reference/getFileReferences1.baseline.jsonc b/tests/baselines/reference/getFileReferences1.baseline.jsonc new file mode 100644 index 0000000000000..6ec3f5ec54589 --- /dev/null +++ b/tests/baselines/reference/getFileReferences1.baseline.jsonc @@ -0,0 +1,64 @@ +// === /project/b.ts === +// import "[|./a|]"; + +// === /project/c.ts === +// import {} from "[|./a|]"; + +// === /project/d.ts === +// import { a } from "[|/project/a|]"; +// type T = typeof import("[|./a|]").a; + +[ + { + "textSpan": { + "start": 8, + "length": 3 + }, + "fileName": "/project/b.ts", + "contextSpan": { + "start": 0, + "length": 13 + }, + "isWriteAccess": false, + "isDefinition": false + }, + { + "textSpan": { + "start": 16, + "length": 3 + }, + "fileName": "/project/c.ts", + "contextSpan": { + "start": 0, + "length": 21 + }, + "isWriteAccess": false, + "isDefinition": false + }, + { + "textSpan": { + "start": 19, + "length": 10 + }, + "fileName": "/project/d.ts", + "contextSpan": { + "start": 0, + "length": 31 + }, + "isWriteAccess": false, + "isDefinition": false + }, + { + "textSpan": { + "start": 56, + "length": 3 + }, + "fileName": "/project/d.ts", + "contextSpan": { + "start": 32, + "length": 32 + }, + "isWriteAccess": false, + "isDefinition": false + } +] \ No newline at end of file diff --git a/tests/baselines/reference/getFileReferences_deduplicate.baseline.jsonc b/tests/baselines/reference/getFileReferences_deduplicate.baseline.jsonc new file mode 100644 index 0000000000000..bc83a05736875 --- /dev/null +++ b/tests/baselines/reference/getFileReferences_deduplicate.baseline.jsonc @@ -0,0 +1,26 @@ +// === /test.ts === +// import "[|./util|]"; + +// === /index.ts === +// export * from "[|./util|]"; + +[ + { + "fileName": "/index.ts", + "textSpan": { + "start": 15, + "length": 6 + }, + "isWriteAccess": false, + "isDefinition": false + }, + { + "fileName": "/test.ts", + "textSpan": { + "start": 8, + "length": 6 + }, + "isWriteAccess": false, + "isDefinition": false + } +] \ No newline at end of file diff --git a/tests/baselines/reference/getFileReferences_server1.baseline.jsonc b/tests/baselines/reference/getFileReferences_server1.baseline.jsonc new file mode 100644 index 0000000000000..f83fee577916d --- /dev/null +++ b/tests/baselines/reference/getFileReferences_server1.baseline.jsonc @@ -0,0 +1,48 @@ +// === /project/b.ts === +// import "[|./a|]"; + +// === /project/c.ts === +// import {} from "[|./a|]"; + +// === /project/d.ts === +// import { a } from "[|/project/a|]"; +// type T = typeof import("[|./a|]").a; + +[ + { + "fileName": "/project/b.ts", + "textSpan": { + "start": 8, + "length": 3 + }, + "isWriteAccess": false, + "isDefinition": false + }, + { + "fileName": "/project/c.ts", + "textSpan": { + "start": 16, + "length": 3 + }, + "isWriteAccess": false, + "isDefinition": false + }, + { + "fileName": "/project/d.ts", + "textSpan": { + "start": 19, + "length": 10 + }, + "isWriteAccess": false, + "isDefinition": false + }, + { + "fileName": "/project/d.ts", + "textSpan": { + "start": 56, + "length": 3 + }, + "isWriteAccess": false, + "isDefinition": false + } +] \ No newline at end of file diff --git a/tests/baselines/reference/getFileReferences_server2.baseline.jsonc b/tests/baselines/reference/getFileReferences_server2.baseline.jsonc new file mode 100644 index 0000000000000..f5d88f1f3d857 --- /dev/null +++ b/tests/baselines/reference/getFileReferences_server2.baseline.jsonc @@ -0,0 +1,38 @@ +// === /packages/client/index.ts === +// import "[|@shared/referenced|]"; + +// === /packages/server/index.js === +// const mod = require("[|../shared/src/referenced|]"); + +// === /packages/server/router.js === +// const blah = require("[|../shared/dist/referenced|]"); + +[ + { + "fileName": "/packages/server/index.js", + "textSpan": { + "start": 21, + "length": 24 + }, + "isWriteAccess": false, + "isDefinition": false + }, + { + "fileName": "/packages/server/router.js", + "textSpan": { + "start": 22, + "length": 25 + }, + "isWriteAccess": false, + "isDefinition": false + }, + { + "fileName": "/packages/client/index.ts", + "textSpan": { + "start": 8, + "length": 18 + }, + "isWriteAccess": false, + "isDefinition": false + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/server/getFileReferences_deduplicate.ts b/tests/cases/fourslash/server/getFileReferences_deduplicate.ts new file mode 100644 index 0000000000000..9786f170537c0 --- /dev/null +++ b/tests/cases/fourslash/server/getFileReferences_deduplicate.ts @@ -0,0 +1,23 @@ +/// + +// @Filename: /tsconfig.json +//// { "files": [], "references": [{ "path": "tsconfig.build.json" }, { "path": "tsconfig.test.json" }] } + +// @Filename: /tsconfig.build.json +//// { "compilerOptions": { "rootDir": "src", "outDir": "dist/build", "composite": true }, "files": ["index.ts"] } + +// @Filename: /index.ts +//// export * from "./util"; + +// @Filename: /tsconfig.test.json +//// { "compilerOptions": { "rootDir": "src", "outDir": "dist/test", "composite": true }, "files": ["test.ts", "index.ts"] } + +// @Filename: /test.ts +//// import "./util"; + +// @Filename: /util.ts +//// export {} + +// util.ts is referenced by index.ts, which is included in tsconfig.build.json and tsconfig.test.json. +// That reference will be returned from both projects' language services. Test ensures it gets deduplicated. +verify.baselineGetFileReferences("/util.ts"); From b9609d6c386aae48b3b9ec160549a90cc008e4b9 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 3 Dec 2020 11:04:53 -0800 Subject: [PATCH 05/18] =?UTF-8?q?Add=20test=20for=20referencing=20a=20scri?= =?UTF-8?q?pt=20(doesn=E2=80=99t=20do=20anything)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reference/getFileReferences2.baseline.jsonc | 1 + tests/cases/fourslash/getFileReferences2.ts | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 tests/baselines/reference/getFileReferences2.baseline.jsonc create mode 100644 tests/cases/fourslash/getFileReferences2.ts diff --git a/tests/baselines/reference/getFileReferences2.baseline.jsonc b/tests/baselines/reference/getFileReferences2.baseline.jsonc new file mode 100644 index 0000000000000..0637a088a01e8 --- /dev/null +++ b/tests/baselines/reference/getFileReferences2.baseline.jsonc @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/cases/fourslash/getFileReferences2.ts b/tests/cases/fourslash/getFileReferences2.ts new file mode 100644 index 0000000000000..025a4ed915622 --- /dev/null +++ b/tests/cases/fourslash/getFileReferences2.ts @@ -0,0 +1,9 @@ +/// + +// @Filename: /project/a.ts +//// console.log("I'm a script!"); + +// @Filename: /project/b.ts +//// import "./a"; + +verify.baselineGetFileReferences("/project/a.ts"); From 38a7b6567764ded604e8c116032c7141d988f72e Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 3 Dec 2020 11:40:07 -0800 Subject: [PATCH 06/18] Update API baselines --- .../reference/api/tsserverlibrary.d.ts | 19 +++++++++++++++++++ tests/baselines/reference/api/typescript.d.ts | 1 + 2 files changed, 20 insertions(+) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index c269e0f0b0be2..70d0abb337c8d 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5534,6 +5534,7 @@ declare namespace ts { getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] | undefined; findReferences(fileName: string, position: number): ReferencedSymbol[] | undefined; getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] | undefined; + getFileReferences(fileName: string): ReferenceEntry[]; /** @deprecated */ getOccurrencesAtPosition(fileName: string, position: number): readonly ReferenceEntry[] | undefined; getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles?: boolean): NavigateToItem[]; @@ -6539,6 +6540,7 @@ declare namespace ts.server.protocol { DefinitionAndBoundSpan = "definitionAndBoundSpan", Implementation = "implementation", Exit = "exit", + FileReferences = "fileReferences", Format = "format", Formatonkey = "formatonkey", Geterr = "geterr", @@ -7352,6 +7354,22 @@ declare namespace ts.server.protocol { interface ReferencesResponse extends Response { body?: ReferencesResponseBody; } + interface FileReferencesRequest extends FileRequest { + command: CommandTypes.FileReferences; + } + interface FileReferencesResponseBody { + /** + * The file locations referencing the symbol. + */ + refs: readonly ReferencesResponseItem[]; + /** + * The name of the symbol. + */ + symbolName: string; + } + interface FileReferencesResponse extends Response { + body?: FileReferencesResponseBody; + } /** * Argument for RenameRequest request. */ @@ -9983,6 +10001,7 @@ declare namespace ts.server { private mapRenameInfo; private toSpanGroups; private getReferences; + private getFileReferences; /** * @param fileName is the name of the file to be opened * @param fileContent is a version of the file content that is known to be more up to date than the one on disk diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index f985d34fdd3c8..23238b45c3efa 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5534,6 +5534,7 @@ declare namespace ts { getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] | undefined; findReferences(fileName: string, position: number): ReferencedSymbol[] | undefined; getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] | undefined; + getFileReferences(fileName: string): ReferenceEntry[]; /** @deprecated */ getOccurrencesAtPosition(fileName: string, position: number): readonly ReferenceEntry[] | undefined; getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles?: boolean): NavigateToItem[]; From e61714bb47e87271c2b86fb0459f68adf2e21058 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 3 Dec 2020 16:03:06 -0800 Subject: [PATCH 07/18] Use refFileMap for non-module references --- src/compiler/program.ts | 4 ++- src/compiler/types.ts | 2 +- src/services/findAllReferences.ts | 31 ++++++++++++++++--- src/services/services.ts | 4 +-- src/services/utilities.ts | 14 +++++++++ .../getFileReferences2.baseline.jsonc | 15 ++++++++- 6 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 8d4efc3e941b3..18de7987239d5 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2631,7 +2631,9 @@ namespace ts { referencedFileName, kind: refFile.kind, index: refFile.index, - file: refFile.file.path + file: refFile.file.path, + pos: refFile.pos, + end: refFile.end }); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7c9038ed3ba96..b4cd903abde8d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3694,7 +3694,7 @@ namespace ts { } /*@internal*/ - export interface RefFile { + export interface RefFile extends TextRange { referencedFileName: string; kind: RefFileKind; index: number; diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index fc931b648e0b4..871690ee70634 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -622,7 +622,16 @@ namespace ts.FindAllReferences { // Could not find a symbol e.g. unknown identifier if (!symbol) { // String literal might be a property (and thus have a symbol), so do this here rather than in getReferencedSymbolsSpecial. - return !options.implementations && isStringLiteralLike(node) ? getReferencesForStringLiteral(node, sourceFiles, checker, cancellationToken) : undefined; + if (!options.implementations && isStringLiteralLike(node)) { + const refFileMap = program.getRefFileMap(); + const refs = refFileMap?.get(node.getSourceFile().path); + const ref = refs && find(refs, ref => textRangesEqual(ref, node)); + const referencedFile = ref && program.getSourceFileByPath(ref.file); + return referencedFile + ? [{ definition: { type: DefinitionKind.String, node }, references: getReferencesForNonModule(referencedFile, refFileMap!, program) || emptyArray }] + : getReferencesForStringLiteral(node, sourceFiles, checker, cancellationToken); + } + return undefined; } if (symbol.escapedName === InternalSymbolName.ExportEquals) { @@ -642,10 +651,24 @@ namespace ts.FindAllReferences { return mergeReferences(program, moduleReferences, references, moduleReferencesOfExportTarget); } - export function getReferencedSymbolsForFileName(fileName: string, program: Program, sourceFiles: readonly SourceFile[], sourceFilesSet: ReadonlySet = new Set(sourceFiles.map(f => f.fileName))): readonly SymbolAndEntries[] | undefined { + export function getReferencesForFileName(fileName: string, program: Program, sourceFiles: readonly SourceFile[], sourceFilesSet: ReadonlySet = new Set(sourceFiles.map(f => f.fileName))): readonly Entry[] { const moduleSymbol = program.getSourceFile(fileName)?.symbol; - if (!moduleSymbol) return undefined; - return getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet); + if (moduleSymbol) { + return getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet)[0]?.references; + } + const refFileMap = program.getRefFileMap(); + const referencedFile = program.getSourceFile(fileName); + return referencedFile && refFileMap && getReferencesForNonModule(referencedFile, refFileMap, program) || emptyArray; + } + + function getReferencesForNonModule(referencedFile: SourceFile, refFileMap: MultiMap, program: Program): readonly SpanEntry[] | undefined { + let entries: SpanEntry[] | undefined; + const references = refFileMap.get(referencedFile.path) || emptyArray; + for (const ref of references) { + const referencingFile = program.getSourceFileByPath(ref.file)!; + entries = append(entries, { kind: EntryKind.Span, fileName: referencingFile.fileName, textSpan: createTextSpanFromRange(ref) }); + } + return entries; } function getMergedAliasedSymbolOfNamespaceExportDeclaration(node: Node, symbol: Symbol, checker: TypeChecker) { diff --git a/src/services/services.ts b/src/services/services.ts index 218e2deb77f54..fa0e3770d9049 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1730,9 +1730,7 @@ namespace ts { function getFileReferences(fileName: string): ReferenceEntry[] { synchronizeHostData(); - return flatMapToMutable( - FindAllReferences.Core.getReferencedSymbolsForFileName(fileName, program, program.getSourceFiles()), - symbolAndEntries => symbolAndEntries.references.map(FindAllReferences.toReferenceEntry)); + return FindAllReferences.Core.getReferencesForFileName(fileName, program, program.getSourceFiles()).map(FindAllReferences.toReferenceEntry); } function getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles = false): NavigateToItem[] { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index bcfe1f8aff435..b4ed7298d5377 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1961,6 +1961,9 @@ namespace ts { export function documentSpansEqual(a: DocumentSpan, b: DocumentSpan): boolean { return a.fileName === b.fileName && textSpansEqual(a.textSpan, b.textSpan); } + export function textRangesEqual(a: TextRange, b: TextRange): boolean { + return a.pos === b.pos && a.end === b.end; + } /** * Iterates through 'array' by index and performs the callback on each element of array until the callback @@ -2923,5 +2926,16 @@ namespace ts { return isInJSFile(declaration) || !findAncestor(declaration, isGlobalScopeAugmentation); } + export function getNodeAtTextRange(sourceFile: SourceFile, textRange: TextRange, includeLeadingTrivia: boolean): Node | undefined { + const token = getTokenAtPosition(sourceFile, textRange.pos); + return findAncestor(token, node => { + const pos = includeLeadingTrivia ? node.pos : node.getStart(); + const end = node.end; + if (pos < textRange.pos || end > textRange.end) return "quit"; + if (textRangesEqual({ pos, end }, textRange)) return true; + return false; + }); + } + // #endregion } diff --git a/tests/baselines/reference/getFileReferences2.baseline.jsonc b/tests/baselines/reference/getFileReferences2.baseline.jsonc index 0637a088a01e8..aa7100e480da9 100644 --- a/tests/baselines/reference/getFileReferences2.baseline.jsonc +++ b/tests/baselines/reference/getFileReferences2.baseline.jsonc @@ -1 +1,14 @@ -[] \ No newline at end of file +// === /project/b.ts === +// import [|"./a"|]; + +[ + { + "textSpan": { + "start": 7, + "length": 5 + }, + "fileName": "/project/b.ts", + "isWriteAccess": false, + "isDefinition": false + } +] \ No newline at end of file From 85a6be6a35028f1cf9fd85c31136616b2acf7285 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 4 Dec 2020 12:50:52 -0800 Subject: [PATCH 08/18] Fix find-all-refs on module specifier --- src/harness/fourslashImpl.ts | 28 +++-- src/harness/fourslashInterfaceImpl.ts | 4 +- src/services/findAllReferences.ts | 7 +- .../findAllRefsNonModule.baseline.jsonc | 119 ++++++++++++++++++ tests/cases/fourslash/findAllRefsNonModule.ts | 20 +++ tests/cases/fourslash/fourslash.ts | 2 +- 6 files changed, 160 insertions(+), 20 deletions(-) create mode 100644 tests/baselines/reference/findAllRefsNonModule.baseline.jsonc create mode 100644 tests/cases/fourslash/findAllRefsNonModule.ts diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 80aa23a936c91..6fc139776cf22 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -1108,19 +1108,21 @@ namespace FourSlash { } } - public verifyBaselineFindAllReferences(markerName: string) { - const marker = this.getMarkerByName(markerName); - const references = this.languageService.findReferences(marker.fileName, marker.position); - const refsByFile = references - ? ts.group(ts.sort(ts.flatMap(references, r => r.references), (a, b) => a.textSpan.start - b.textSpan.start), ref => ref.fileName) - : ts.emptyArray; - - // Write input files - let baselineContent = this.getBaselineContentForGroupedReferences(refsByFile, markerName); - - // Write response JSON - baselineContent += JSON.stringify(references, undefined, 2); - Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baselineContent); + public verifyBaselineFindAllReferences(...markerNames: string[]) { + const baseline = markerNames.map(markerName => { + const marker = this.getMarkerByName(markerName); + const references = this.languageService.findReferences(marker.fileName, marker.position); + const refsByFile = references + ? ts.group(ts.sort(ts.flatMap(references, r => r.references), (a, b) => a.textSpan.start - b.textSpan.start), ref => ref.fileName) + : ts.emptyArray; + + // Write input files + const baselineContent = this.getBaselineContentForGroupedReferences(refsByFile, markerName); + + // Write response JSON + return baselineContent + JSON.stringify(references, undefined, 2); + }).join("\n\n"); + Harness.Baseline.runBaseline(this.getBaselineFileNameForContainingTestFile(".baseline.jsonc"), baseline); } public verifyBaselineGetFileReferences(fileName: string) { diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index fd38bd572c1c6..6fd2d834039d2 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -332,8 +332,8 @@ namespace FourSlashInterface { this.state.verifyTypeOfSymbolAtLocation(range, symbol, expected); } - public baselineFindAllReferences(markerName: string) { - this.state.verifyBaselineFindAllReferences(markerName); + public baselineFindAllReferences(...markerNames: string[]) { + this.state.verifyBaselineFindAllReferences(...markerNames); } public baselineGetFileReferences(fileName: string) { diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 871690ee70634..9befd3e92db91 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -624,9 +624,8 @@ namespace ts.FindAllReferences { // String literal might be a property (and thus have a symbol), so do this here rather than in getReferencedSymbolsSpecial. if (!options.implementations && isStringLiteralLike(node)) { const refFileMap = program.getRefFileMap(); - const refs = refFileMap?.get(node.getSourceFile().path); - const ref = refs && find(refs, ref => textRangesEqual(ref, node)); - const referencedFile = ref && program.getSourceFileByPath(ref.file); + const referencedFileName = node.getSourceFile().resolvedModules?.get(node.text)?.resolvedFileName; + const referencedFile = referencedFileName && program.getSourceFile(referencedFileName); return referencedFile ? [{ definition: { type: DefinitionKind.String, node }, references: getReferencesForNonModule(referencedFile, refFileMap!, program) || emptyArray }] : getReferencesForStringLiteral(node, sourceFiles, checker, cancellationToken); @@ -654,7 +653,7 @@ namespace ts.FindAllReferences { export function getReferencesForFileName(fileName: string, program: Program, sourceFiles: readonly SourceFile[], sourceFilesSet: ReadonlySet = new Set(sourceFiles.map(f => f.fileName))): readonly Entry[] { const moduleSymbol = program.getSourceFile(fileName)?.symbol; if (moduleSymbol) { - return getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet)[0]?.references; + return getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet)[0]?.references || emptyArray; } const refFileMap = program.getRefFileMap(); const referencedFile = program.getSourceFile(fileName); diff --git a/tests/baselines/reference/findAllRefsNonModule.baseline.jsonc b/tests/baselines/reference/findAllRefsNonModule.baseline.jsonc new file mode 100644 index 0000000000000..d60e5784bd86d --- /dev/null +++ b/tests/baselines/reference/findAllRefsNonModule.baseline.jsonc @@ -0,0 +1,119 @@ +// === /import.ts === +// /*FIND ALL REFS*/import [|"./script"|]; + +// === /require.js === +// require([|"./script"|]); + +// === /tripleSlash.ts === +// /// + +[ + { + "definition": { + "containerKind": "", + "containerName": "", + "fileName": "/import.ts", + "kind": "var", + "name": "./script", + "textSpan": { + "start": 8, + "length": 8 + }, + "displayParts": [ + { + "text": "\"./script\"", + "kind": "stringLiteral" + } + ] + }, + "references": [ + { + "textSpan": { + "start": 7, + "length": 10 + }, + "fileName": "/import.ts", + "isWriteAccess": false, + "isDefinition": false + }, + { + "textSpan": { + "start": 8, + "length": 10 + }, + "fileName": "/require.js", + "isWriteAccess": false, + "isDefinition": false + }, + { + "textSpan": { + "start": 21, + "length": 9 + }, + "fileName": "/tripleSlash.ts", + "isWriteAccess": false, + "isDefinition": false + } + ] + } +] + +// === /import.ts === +// import [|"./script"|]; + +// === /require.js === +// /*FIND ALL REFS*/require([|"./script"|]); + +// === /tripleSlash.ts === +// /// + +[ + { + "definition": { + "containerKind": "", + "containerName": "", + "fileName": "/require.js", + "kind": "var", + "name": "./script", + "textSpan": { + "start": 9, + "length": 8 + }, + "displayParts": [ + { + "text": "\"./script\"", + "kind": "stringLiteral" + } + ] + }, + "references": [ + { + "textSpan": { + "start": 7, + "length": 10 + }, + "fileName": "/import.ts", + "isWriteAccess": false, + "isDefinition": false + }, + { + "textSpan": { + "start": 8, + "length": 10 + }, + "fileName": "/require.js", + "isWriteAccess": false, + "isDefinition": false + }, + { + "textSpan": { + "start": 21, + "length": 9 + }, + "fileName": "/tripleSlash.ts", + "isWriteAccess": false, + "isDefinition": false + } + ] + } +] \ No newline at end of file diff --git a/tests/cases/fourslash/findAllRefsNonModule.ts b/tests/cases/fourslash/findAllRefsNonModule.ts new file mode 100644 index 0000000000000..77c2e189fc438 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsNonModule.ts @@ -0,0 +1,20 @@ +/// + +// @checkJs: true + +// @Filename: /script.ts +//// console.log("I'm a script!"); + +// @Filename: /import.ts +//// import "./script/*1*/"; + +// @Filename: /require.js +//// require("./script/*2*/"); + +// @Filename: /tripleSlash.ts +//// /// + +// @Filename: /stringLiteral.ts +//// console.log("./script"); + +verify.baselineFindAllReferences("1", "2"); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 9f3f8eb1e993a..d2ea70036a4b5 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -289,7 +289,7 @@ declare namespace FourSlashInterface { goToType(startMarkerNames: ArrayOrSingle, endMarkerNames: ArrayOrSingle): void; verifyGetEmitOutputForCurrentFile(expected: string): void; verifyGetEmitOutputContentsForCurrentFile(expected: ts.OutputFile[]): void; - baselineFindAllReferences(markerName: string): void; + baselineFindAllReferences(...markerNames: string[]): void; baselineGetFileReferences(fileName: string): void; noReferences(markerNameOrRange?: string | Range): void; symbolAtLocation(startRange: Range, ...declarationRanges: Range[]): void; From 8e5200ac1437f2d2e2cbb8ca0c3edc878b30f2c4 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 4 Dec 2020 12:51:34 -0800 Subject: [PATCH 09/18] Remove unused util --- src/services/utilities.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index b4ed7298d5377..85748c0676e39 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2926,16 +2926,5 @@ namespace ts { return isInJSFile(declaration) || !findAncestor(declaration, isGlobalScopeAugmentation); } - export function getNodeAtTextRange(sourceFile: SourceFile, textRange: TextRange, includeLeadingTrivia: boolean): Node | undefined { - const token = getTokenAtPosition(sourceFile, textRange.pos); - return findAncestor(token, node => { - const pos = includeLeadingTrivia ? node.pos : node.getStart(); - const end = node.end; - if (pos < textRange.pos || end > textRange.end) return "quit"; - if (textRangesEqual({ pos, end }, textRange)) return true; - return false; - }); - } - // #endregion } From bf28ee1ebd271707822f154b7f67c18e215b001a Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 4 Dec 2020 16:27:32 -0800 Subject: [PATCH 10/18] =?UTF-8?q?Don=E2=80=99t=20store=20text=20range=20on?= =?UTF-8?q?=20ts.RefFile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/compiler/program.ts | 21 ++------------------- src/compiler/types.ts | 2 +- src/compiler/utilities.ts | 20 ++++++++++++++++++++ src/services/findAllReferences.ts | 6 +++++- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 18de7987239d5..fc6d65a89ab5e 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2631,9 +2631,7 @@ namespace ts { referencedFileName, kind: refFile.kind, index: refFile.index, - file: refFile.file.path, - pos: refFile.pos, - end: refFile.end + file: refFile.file.path }); } } @@ -3357,22 +3355,7 @@ namespace ts { function createFileDiagnosticAtReference(refPathToReportErrorOn: ts.RefFile, message: DiagnosticMessage, ...args: (string | number | undefined)[]) { const refFile = Debug.checkDefined(getSourceFileByPath(refPathToReportErrorOn.file)); - const { kind, index } = refPathToReportErrorOn; - let pos: number, end: number; - switch (kind) { - case RefFileKind.Import: - pos = skipTrivia(refFile.text, refFile.imports[index].pos); - end = refFile.imports[index].end; - break; - case RefFileKind.ReferenceFile: - ({ pos, end } = refFile.referencedFiles[index]); - break; - case RefFileKind.TypeReferenceDirective: - ({ pos, end } = refFile.typeReferenceDirectives[index]); - break; - default: - return Debug.assertNever(kind); - } + const { pos, end } = getRangeOfRef(refPathToReportErrorOn, refFile); return createFileDiagnostic(refFile, pos, end - pos, message, ...args); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b4cd903abde8d..7c9038ed3ba96 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3694,7 +3694,7 @@ namespace ts { } /*@internal*/ - export interface RefFile extends TextRange { + export interface RefFile { referencedFileName: string; kind: RefFileKind; index: number; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 43f5e4aa8eaae..a2a593ff79c0a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -7001,4 +7001,24 @@ namespace ts { return bindParentToChildIgnoringJSDoc(child, parent) || bindJSDoc(child); } } + + export function getRangeOfRef(ref: RefFile, referencingFile: SourceFile): TextRange { + const { kind, index } = ref; + let pos: number, end: number; + switch (kind) { + case RefFileKind.Import: + pos = skipTrivia(referencingFile.text, referencingFile.imports[index].pos); + end = referencingFile.imports[index].end; + break; + case RefFileKind.ReferenceFile: + ({ pos, end } = referencingFile.referencedFiles[index]); + break; + case RefFileKind.TypeReferenceDirective: + ({ pos, end } = referencingFile.typeReferenceDirectives[index]); + break; + default: + return Debug.assertNever(kind); + } + return { pos, end }; + } } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 9befd3e92db91..bc73b6282bec2 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -665,7 +665,11 @@ namespace ts.FindAllReferences { const references = refFileMap.get(referencedFile.path) || emptyArray; for (const ref of references) { const referencingFile = program.getSourceFileByPath(ref.file)!; - entries = append(entries, { kind: EntryKind.Span, fileName: referencingFile.fileName, textSpan: createTextSpanFromRange(ref) }); + entries = append(entries, { + kind: EntryKind.Span, + fileName: referencingFile.fileName, + textSpan: createTextSpanFromRange(getRangeOfRef(ref, referencingFile)) + }); } return entries; } From c3837be054f350ad9f67e0165955efa11a6cb72e Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 8 Dec 2020 12:57:32 -0800 Subject: [PATCH 11/18] Ensure string literal could itself be a file reference --- src/harness/fourslashImpl.ts | 18 +++-- src/services/findAllReferences.ts | 13 ++-- .../findAllRefsNonModule.baseline.jsonc | 71 ++++++++++++++++++- tests/cases/fourslash/findAllRefsNonModule.ts | 3 +- 4 files changed, 91 insertions(+), 14 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 6fc139776cf22..552949c8ae319 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -1140,7 +1140,7 @@ namespace FourSlash { } private getBaselineContentForGroupedReferences(refsByFile: readonly (readonly ts.ReferenceEntry[])[], markerName?: string) { - const marker = markerName !== undefined && this.getMarkerByName(markerName); + const marker = markerName !== undefined ? this.getMarkerByName(markerName) : undefined; let baselineContent = ""; for (const group of refsByFile) { baselineContent += getBaselineContentForFile(group[0].fileName, this.getFileContent(group[0].fileName)); @@ -1152,16 +1152,24 @@ namespace FourSlash { let newContent = `=== ${fileName} ===\n`; let pos = 0; for (const { textSpan } of refsByFile.find(refs => refs[0].fileName === fileName) ?? ts.emptyArray) { - if (marker && fileName === marker.fileName && ts.textSpanContainsPosition(textSpan, marker.position)) { - newContent += "/*FIND ALL REFS*/"; - } const end = textSpan.start + textSpan.length; newContent += content.slice(pos, textSpan.start); newContent += "[|"; - newContent += content.slice(textSpan.start, end); + pos = textSpan.start; + if (fileName === marker?.fileName && ts.textSpanContainsPosition(textSpan, marker.position)) { + newContent += content.slice(pos, marker.position); + newContent += "/*FIND ALL REFS*/"; + pos = marker.position; + } + newContent += content.slice(pos, end); newContent += "|]"; pos = end; } + if (marker?.fileName === fileName && marker.position >= pos) { + newContent += content.slice(pos, marker.position); + newContent += "/*FIND ALL REFS*/"; + pos = marker.position; + } newContent += content.slice(pos); return newContent.split(/\r?\n/).map(l => "// " + l).join("\n"); } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index bc73b6282bec2..c4d132e1a788f 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -623,12 +623,13 @@ namespace ts.FindAllReferences { if (!symbol) { // String literal might be a property (and thus have a symbol), so do this here rather than in getReferencedSymbolsSpecial. if (!options.implementations && isStringLiteralLike(node)) { - const refFileMap = program.getRefFileMap(); - const referencedFileName = node.getSourceFile().resolvedModules?.get(node.text)?.resolvedFileName; - const referencedFile = referencedFileName && program.getSourceFile(referencedFileName); - return referencedFile - ? [{ definition: { type: DefinitionKind.String, node }, references: getReferencesForNonModule(referencedFile, refFileMap!, program) || emptyArray }] - : getReferencesForStringLiteral(node, sourceFiles, checker, cancellationToken); + if (isRequireCall(node.parent, /*requireStringLiteralLikeArgument*/ true) || isExternalModuleReference(node.parent) || isImportDeclaration(node.parent) || isImportCall(node.parent)) { + const refFileMap = program.getRefFileMap(); + const referencedFileName = node.getSourceFile().resolvedModules?.get(node.text)?.resolvedFileName; + const referencedFile = referencedFileName ? program.getSourceFile(referencedFileName) : undefined; + return referencedFile && [{ definition: { type: DefinitionKind.String, node }, references: getReferencesForNonModule(referencedFile, refFileMap!, program) || emptyArray }]; + } + return getReferencesForStringLiteral(node, sourceFiles, checker, cancellationToken); } return undefined; } diff --git a/tests/baselines/reference/findAllRefsNonModule.baseline.jsonc b/tests/baselines/reference/findAllRefsNonModule.baseline.jsonc index d60e5784bd86d..47d7678bb21e2 100644 --- a/tests/baselines/reference/findAllRefsNonModule.baseline.jsonc +++ b/tests/baselines/reference/findAllRefsNonModule.baseline.jsonc @@ -1,8 +1,9 @@ // === /import.ts === -// /*FIND ALL REFS*/import [|"./script"|]; +// import [|"./script/*FIND ALL REFS*/"|]; // === /require.js === // require([|"./script"|]); +// console.log("./script"); // === /tripleSlash.ts === // /// @@ -62,7 +63,8 @@ // import [|"./script"|]; // === /require.js === -// /*FIND ALL REFS*/require([|"./script"|]); +// require([|"./script/*FIND ALL REFS*/"|]); +// console.log("./script"); // === /tripleSlash.ts === // /// @@ -116,4 +118,69 @@ } ] } +] + +// === /require.js === +// require("[|./script|]"); +// console.log("[|./script|]/*FIND ALL REFS*/"); + +// === /stringLiteral.ts === +// console.log("[|./script|]"); + +[ + { + "definition": { + "containerKind": "", + "containerName": "", + "fileName": "/require.js", + "kind": "var", + "name": "./script", + "textSpan": { + "start": 34, + "length": 8 + }, + "displayParts": [ + { + "text": "\"./script\"", + "kind": "stringLiteral" + } + ] + }, + "references": [ + { + "textSpan": { + "start": 9, + "length": 8 + }, + "fileName": "/require.js", + "contextSpan": { + "start": 0, + "length": 20 + }, + "isWriteAccess": false, + "isDefinition": false, + "isInString": true + }, + { + "textSpan": { + "start": 34, + "length": 8 + }, + "fileName": "/require.js", + "isWriteAccess": false, + "isDefinition": false, + "isInString": true + }, + { + "textSpan": { + "start": 13, + "length": 8 + }, + "fileName": "/stringLiteral.ts", + "isWriteAccess": false, + "isDefinition": false, + "isInString": true + } + ] + } ] \ No newline at end of file diff --git a/tests/cases/fourslash/findAllRefsNonModule.ts b/tests/cases/fourslash/findAllRefsNonModule.ts index 77c2e189fc438..371696132b628 100644 --- a/tests/cases/fourslash/findAllRefsNonModule.ts +++ b/tests/cases/fourslash/findAllRefsNonModule.ts @@ -10,6 +10,7 @@ // @Filename: /require.js //// require("./script/*2*/"); +//// console.log("./script/*3*/"); // @Filename: /tripleSlash.ts //// /// @@ -17,4 +18,4 @@ // @Filename: /stringLiteral.ts //// console.log("./script"); -verify.baselineFindAllReferences("1", "2"); +verify.baselineFindAllReferences("1", "2", "3"); From 74a7438174ca29c65fa431619fd0ed3d48e992af Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 8 Dec 2020 12:57:54 -0800 Subject: [PATCH 12/18] Remove unused utilities --- src/services/utilities.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 85748c0676e39..bcfe1f8aff435 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1961,9 +1961,6 @@ namespace ts { export function documentSpansEqual(a: DocumentSpan, b: DocumentSpan): boolean { return a.fileName === b.fileName && textSpansEqual(a.textSpan, b.textSpan); } - export function textRangesEqual(a: TextRange, b: TextRange): boolean { - return a.pos === b.pos && a.end === b.end; - } /** * Iterates through 'array' by index and performs the callback on each element of array until the callback From 6d7fd47d5f8e9bf6defd69833f93fa80e1280aab Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 8 Dec 2020 13:16:24 -0800 Subject: [PATCH 13/18] Improve baseline format --- src/harness/fourslashImpl.ts | 13 +++++++++++-- .../findAllRefsCommonJsRequire.baseline.jsonc | 4 ++-- .../findAllRefsCommonJsRequire2.baseline.jsonc | 4 ++-- .../findAllRefsCommonJsRequire3.baseline.jsonc | 4 ++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 552949c8ae319..e5d5bb6ffb75a 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -1154,13 +1154,22 @@ namespace FourSlash { for (const { textSpan } of refsByFile.find(refs => refs[0].fileName === fileName) ?? ts.emptyArray) { const end = textSpan.start + textSpan.length; newContent += content.slice(pos, textSpan.start); - newContent += "[|"; pos = textSpan.start; - if (fileName === marker?.fileName && ts.textSpanContainsPosition(textSpan, marker.position)) { + // It's easier to read if the /*FIND ALL REFS*/ comment is outside the range markers, which makes + // this code a bit more verbose than it would be if I were less picky about the baseline format. + if (fileName === marker?.fileName && marker.position === textSpan.start) { + newContent += "/*FIND ALL REFS*/"; + newContent += "[|"; + } + else if (fileName === marker?.fileName && ts.textSpanContainsPosition(textSpan, marker.position)) { + newContent += "[|"; newContent += content.slice(pos, marker.position); newContent += "/*FIND ALL REFS*/"; pos = marker.position; } + else { + newContent += "[|"; + } newContent += content.slice(pos, end); newContent += "|]"; pos = end; diff --git a/tests/baselines/reference/findAllRefsCommonJsRequire.baseline.jsonc b/tests/baselines/reference/findAllRefsCommonJsRequire.baseline.jsonc index 675238f9655e0..d6bed0659ba93 100644 --- a/tests/baselines/reference/findAllRefsCommonJsRequire.baseline.jsonc +++ b/tests/baselines/reference/findAllRefsCommonJsRequire.baseline.jsonc @@ -1,6 +1,6 @@ // === /b.js === -// const { [|f|]/*FIND ALL REFS*/ } = require('./a') -// [|f|] +// const { [|f|] } = require('./a') +// /*FIND ALL REFS*/[|f|] // === /a.js === // function [|f|]() { } diff --git a/tests/baselines/reference/findAllRefsCommonJsRequire2.baseline.jsonc b/tests/baselines/reference/findAllRefsCommonJsRequire2.baseline.jsonc index e647bf183db75..3ea2975673f4b 100644 --- a/tests/baselines/reference/findAllRefsCommonJsRequire2.baseline.jsonc +++ b/tests/baselines/reference/findAllRefsCommonJsRequire2.baseline.jsonc @@ -1,6 +1,6 @@ // === /b.js === -// const { [|f|]/*FIND ALL REFS*/ } = require('./a') -// [|f|] +// const { [|f|] } = require('./a') +// /*FIND ALL REFS*/[|f|] // === /a.js === // function [|f|]() { } diff --git a/tests/baselines/reference/findAllRefsCommonJsRequire3.baseline.jsonc b/tests/baselines/reference/findAllRefsCommonJsRequire3.baseline.jsonc index e1295c1a01228..87d82f1ae6ed2 100644 --- a/tests/baselines/reference/findAllRefsCommonJsRequire3.baseline.jsonc +++ b/tests/baselines/reference/findAllRefsCommonJsRequire3.baseline.jsonc @@ -1,6 +1,6 @@ // === /b.js === -// const { [|f|]/*FIND ALL REFS*/ } = require('./a') -// [|f|] +// const { [|f|] } = require('./a') +// /*FIND ALL REFS*/[|f|] // === /a.js === // function [|f|]() { } From 859506e4e470d409956a3fd5f240f119f2b30c55 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 8 Dec 2020 13:25:56 -0800 Subject: [PATCH 14/18] Preserve old behavior of falling back to string literal references --- src/services/findAllReferences.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index c4d132e1a788f..b0a2068c36671 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -627,7 +627,12 @@ namespace ts.FindAllReferences { const refFileMap = program.getRefFileMap(); const referencedFileName = node.getSourceFile().resolvedModules?.get(node.text)?.resolvedFileName; const referencedFile = referencedFileName ? program.getSourceFile(referencedFileName) : undefined; - return referencedFile && [{ definition: { type: DefinitionKind.String, node }, references: getReferencesForNonModule(referencedFile, refFileMap!, program) || emptyArray }]; + if (referencedFile) { + return [{ definition: { type: DefinitionKind.String, node }, references: getReferencesForNonModule(referencedFile, refFileMap!, program) || emptyArray }]; + } + // Fall through to string literal references. This is not very likely to return + // anything useful, but I guess it's better than nothing, and there's an existing + // test that expects this to happen (fourslash/cases/untypedModuleImport.ts). } return getReferencesForStringLiteral(node, sourceFiles, checker, cancellationToken); } From 09f9989ddd93a3095e32d96041ccd1e3e0554e79 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Tue, 8 Dec 2020 13:53:53 -0800 Subject: [PATCH 15/18] Update baselines from master --- .../reference/findAllRefsForImportCall.baseline.jsonc | 2 +- .../reference/findAllRefsForImportCallType.baseline.jsonc | 2 +- .../reference/findAllRefs_importType_js.1.baseline.jsonc | 2 +- .../reference/findAllRefs_importType_js.2.baseline.jsonc | 4 ++-- .../reference/findAllRefs_importType_js.3.baseline.jsonc | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/baselines/reference/findAllRefsForImportCall.baseline.jsonc b/tests/baselines/reference/findAllRefsForImportCall.baseline.jsonc index cfff022786521..a64732cd61503 100644 --- a/tests/baselines/reference/findAllRefsForImportCall.baseline.jsonc +++ b/tests/baselines/reference/findAllRefsForImportCall.baseline.jsonc @@ -1,5 +1,5 @@ // === /app.ts === -// /*FIND ALL REFS*/export function [|hello|]() {}; +// export function [|he/*FIND ALL REFS*/llo|]() {}; // === /indirect-use.ts === // import("./re-export").then(mod => mod.services.app.[|hello|]()); diff --git a/tests/baselines/reference/findAllRefsForImportCallType.baseline.jsonc b/tests/baselines/reference/findAllRefsForImportCallType.baseline.jsonc index 1f512403f0296..0ffe79597f375 100644 --- a/tests/baselines/reference/findAllRefsForImportCallType.baseline.jsonc +++ b/tests/baselines/reference/findAllRefsForImportCallType.baseline.jsonc @@ -1,5 +1,5 @@ // === /app.ts === -// /*FIND ALL REFS*/export function [|hello|]() {}; +// export function [|he/*FIND ALL REFS*/llo|]() {}; // === /indirect-use.ts === // import type { app } from "./re-export"; diff --git a/tests/baselines/reference/findAllRefs_importType_js.1.baseline.jsonc b/tests/baselines/reference/findAllRefs_importType_js.1.baseline.jsonc index fa8e4c790bf3b..e8b02bb4811b7 100644 --- a/tests/baselines/reference/findAllRefs_importType_js.1.baseline.jsonc +++ b/tests/baselines/reference/findAllRefs_importType_js.1.baseline.jsonc @@ -5,7 +5,7 @@ // const y = 0; // === /a.js === -// /*FIND ALL REFS*/module.exports = class [|C|] {}; +// module.exports = class /*FIND ALL REFS*/[|C|] {}; // module.exports.D = class D {}; [ diff --git a/tests/baselines/reference/findAllRefs_importType_js.2.baseline.jsonc b/tests/baselines/reference/findAllRefs_importType_js.2.baseline.jsonc index 5e6981dd49a2d..55e21a8106311 100644 --- a/tests/baselines/reference/findAllRefs_importType_js.2.baseline.jsonc +++ b/tests/baselines/reference/findAllRefs_importType_js.2.baseline.jsonc @@ -1,6 +1,6 @@ // === /a.js === -// /*FIND ALL REFS*/module.exports = class C {}; -// module.exports.[|D|] = class D {}; +// module.exports = class C {}; +// module.exports./*FIND ALL REFS*/[|D|] = class D {}; // === /b.js === // /** @type {import("./a")} */ diff --git a/tests/baselines/reference/findAllRefs_importType_js.3.baseline.jsonc b/tests/baselines/reference/findAllRefs_importType_js.3.baseline.jsonc index b949b88ae8b1a..b7ca959132a05 100644 --- a/tests/baselines/reference/findAllRefs_importType_js.3.baseline.jsonc +++ b/tests/baselines/reference/findAllRefs_importType_js.3.baseline.jsonc @@ -1,6 +1,6 @@ // === /a.js === -// /*FIND ALL REFS*/module.exports = class C {}; -// module.exports.D = class [|D|] {}; +// module.exports = class C {}; +// module.exports.D = class /*FIND ALL REFS*/[|D|] {}; // === /b.js === // /** @type {import("./a")} */ From 100b90930bf9940780c1b65845642a7c75b4a918 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 10 Dec 2020 13:32:46 -0800 Subject: [PATCH 16/18] Fix old RefFileMap code after merge --- src/compiler/utilities.ts | 20 -------------------- src/services/findAllReferences.ts | 13 ++++++++----- 2 files changed, 8 insertions(+), 25 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index c624a588b18fa..ee9db727dd4f7 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -7016,24 +7016,4 @@ namespace ts { return bindParentToChildIgnoringJSDoc(child, parent) || bindJSDoc(child); } } - - export function getRangeOfRef(ref: RefFile, referencingFile: SourceFile): TextRange { - const { kind, index } = ref; - let pos: number, end: number; - switch (kind) { - case RefFileKind.Import: - pos = skipTrivia(referencingFile.text, referencingFile.imports[index].pos); - end = referencingFile.imports[index].end; - break; - case RefFileKind.ReferenceFile: - ({ pos, end } = referencingFile.referencedFiles[index]); - break; - case RefFileKind.TypeReferenceDirective: - ({ pos, end } = referencingFile.typeReferenceDirectives[index]); - break; - default: - return Debug.assertNever(kind); - } - return { pos, end }; - } } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index b17b525dec06a..885595aed653b 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -672,11 +672,14 @@ namespace ts.FindAllReferences { for (const ref of references) { if (isReferencedFile(ref)) { const referencingFile = program.getSourceFileByPath(ref.file)!; - entries = append(entries, { - kind: EntryKind.Span, - fileName: referencingFile.fileName, - textSpan: createTextSpanFromRange(getRangeOfRef(ref, referencingFile)) - }); + const location = getReferencedFileLocation(program.getSourceFileByPath, ref); + if (isReferenceFileLocation(location)) { + entries = append(entries, { + kind: EntryKind.Span, + fileName: referencingFile.fileName, + textSpan: createTextSpanFromRange(location) + }); + } } } return entries; From d88296b7c8c01afc80fd3cf60c8dd9f21fcd9a42 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 11 Dec 2020 11:57:08 -0800 Subject: [PATCH 17/18] Add test for additional response info --- src/server/session.ts | 4 +- src/testRunner/tsconfig.json | 1 + .../unittests/tsserver/getFileReferences.ts | 58 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 src/testRunner/unittests/tsserver/getFileReferences.ts diff --git a/src/server/session.ts b/src/server/session.ts index 518c297c91bf5..a92ae6cbd4366 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1550,11 +1550,11 @@ namespace ts.server { const symbolDisplayString = nameInfo ? displayPartsToString(nameInfo.displayParts) : ""; const nameSpan = nameInfo && nameInfo.textSpan; const symbolStartOffset = nameSpan ? scriptInfo.positionToLineOffset(nameSpan.start).offset : 0; - const symbolName = nameSpan ? scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan)) : ""; + // const symbolName = nameSpan ? scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan)) : ""; const refs: readonly protocol.ReferencesResponseItem[] = flatMap(references, referencedSymbol => { return referencedSymbol.references.map(entry => referenceEntryToReferencesResponseItem(this.projectService, entry)); }); - return { refs, symbolName, symbolStartOffset, symbolDisplayString }; + return { refs, symbolName: "", symbolStartOffset, symbolDisplayString }; } private getFileReferences(args: protocol.FileRequestArgs, simplifiedResult: boolean): protocol.FileReferencesResponseBody | readonly ReferenceEntry[] { diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json index 523ea769fa738..1d389e21f0140 100644 --- a/src/testRunner/tsconfig.json +++ b/src/testRunner/tsconfig.json @@ -171,6 +171,7 @@ "unittests/tsserver/getApplicableRefactors.ts", "unittests/tsserver/getEditsForFileRename.ts", "unittests/tsserver/getExportReferences.ts", + "unittests/tsserver/getFileReferences.ts", "unittests/tsserver/importHelpers.ts", "unittests/tsserver/importSuggestionsCache.ts", "unittests/tsserver/inferredProjects.ts", diff --git a/src/testRunner/unittests/tsserver/getFileReferences.ts b/src/testRunner/unittests/tsserver/getFileReferences.ts new file mode 100644 index 0000000000000..f9d049f8ead3f --- /dev/null +++ b/src/testRunner/unittests/tsserver/getFileReferences.ts @@ -0,0 +1,58 @@ +namespace ts.projectSystem { + describe("unittests:: tsserver:: getFileReferences", () => { + const importA = `import "./a";`; + const importCurlyFromA = `import {} from "./a";`; + const importAFromA = `import { a } from "/project/a";`; + const typeofImportA = `type T = typeof import("./a").a;`; + + const aTs: File = { + path: "/project/a.ts", + content: "export const a = {};", + }; + const bTs: File = { + path: "/project/b.ts", + content: importA, + }; + const cTs: File = { + path: "/project/c.ts", + content: importCurlyFromA + }; + const dTs: File = { + path: "/project/d.ts", + content: [importAFromA, typeofImportA].join("\n") + }; + const tsconfig: File = { + path: "/project/tsconfig.json", + content: "{}", + }; + + function makeSampleSession() { + const host = createServerHost([aTs, bTs, cTs, dTs, tsconfig]); + const session = createSession(host); + openFilesForSession([aTs, bTs, cTs, dTs], session); + return session; + } + + it("should get file references", () => { + const session = makeSampleSession(); + + const response = executeSessionRequest( + session, + protocol.CommandTypes.FileReferences, + { file: aTs.path }, + ); + + const expectResponse: protocol.FileReferencesResponseBody = { + refs: [ + makeReferenceItem({ file: bTs, text: "./a", lineText: importA, contextText: importA, isDefinition: false, isWriteAccess: false }), + makeReferenceItem({ file: cTs, text: "./a", lineText: importCurlyFromA, contextText: importCurlyFromA, isDefinition: false, isWriteAccess: false }), + makeReferenceItem({ file: dTs, text: "/project/a", lineText: importAFromA, contextText: importAFromA, isDefinition: false, isWriteAccess: false }), + makeReferenceItem({ file: dTs, text: "./a", lineText: typeofImportA, contextText: typeofImportA, isDefinition: false, isWriteAccess: false }), + ], + symbolName: `"${aTs.path}"`, + }; + + assert.deepEqual(response, expectResponse); + }); + }); +} From b40cf6bed108f9e84cf5434cdc86c831f5fa6472 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 11 Dec 2020 12:11:00 -0800 Subject: [PATCH 18/18] Undo test change --- src/server/session.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index a92ae6cbd4366..518c297c91bf5 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1550,11 +1550,11 @@ namespace ts.server { const symbolDisplayString = nameInfo ? displayPartsToString(nameInfo.displayParts) : ""; const nameSpan = nameInfo && nameInfo.textSpan; const symbolStartOffset = nameSpan ? scriptInfo.positionToLineOffset(nameSpan.start).offset : 0; - // const symbolName = nameSpan ? scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan)) : ""; + const symbolName = nameSpan ? scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan)) : ""; const refs: readonly protocol.ReferencesResponseItem[] = flatMap(references, referencedSymbol => { return referencedSymbol.references.map(entry => referenceEntryToReferencesResponseItem(this.projectService, entry)); }); - return { refs, symbolName: "", symbolStartOffset, symbolDisplayString }; + return { refs, symbolName, symbolStartOffset, symbolDisplayString }; } private getFileReferences(args: protocol.FileRequestArgs, simplifiedResult: boolean): protocol.FileReferencesResponseBody | readonly ReferenceEntry[] {