diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 8406f27bcc6013de64affc86200491da82c0887c..d22f57d50952223cf1e6389182467874aef0a035 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -49,6 +49,7 @@ "onCommand:workbench.action.tasks.runTask", "onCommand:_typescript.configurePlugin", "onCommand:_typescript.learnMoreAboutRefactorings", + "onCommand:typescript.fileReferences", "onLanguage:jsonc" ], "main": "./out/extension", @@ -961,6 +962,11 @@ "command": "typescript.restartTsServer", "title": "%typescript.restartTsServer%", "category": "TypeScript" + }, + { + "command": "typescript.findAllFileReferences", + "title": "%typescript.findAllFileReferences%", + "category": "TypeScript" } ], "menus": { @@ -1008,6 +1014,46 @@ { "command": "typescript.restartTsServer", "when": "typescript.isManagedFile" + }, + { + "command": "typescript.findAllFileReferences", + "when": "tsSupportsFileReferences && typescript.isManagedFile" + } + ], + "explorer/context": [ + { + "command": "typescript.findAllFileReferences", + "when": "tsSupportsFileReferences && resourceLangId == javascript" + }, + { + "command": "typescript.findAllFileReferences", + "when": "tsSupportsFileReferences && resourceLangId == javascriptreact" + }, + { + "command": "typescript.findAllFileReferences", + "when": "tsSupportsFileReferences && resourceLangId == typescript" + }, + { + "command": "typescript.findAllFileReferences", + "when": "tsSupportsFileReferences && resourceLangId == typescriptreact" + } + ], + "editor/title/context": [ + { + "command": "typescript.findAllFileReferences", + "when": "tsSupportsFileReferences && resourceLangId == javascript" + }, + { + "command": "typescript.findAllFileReferences", + "when": "tsSupportsFileReferences && resourceLangId == javascriptreact" + }, + { + "command": "typescript.findAllFileReferences", + "when": "tsSupportsFileReferences && resourceLangId == typescript" + }, + { + "command": "typescript.findAllFileReferences", + "when": "tsSupportsFileReferences && resourceLangId == typescriptreact" } ] }, diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 4283596c2ffe156c78e88ea67bbf1e1e2e7264c3..a347b903d4178c2bfdbae024a6c347c4d4bec378 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -135,5 +135,6 @@ "codeActions.refactor.rewrite.parameters.toDestructured.title": "Convert parameters to destructured object", "codeActions.refactor.rewrite.property.generateAccessors.title": "Generate accessors", "codeActions.refactor.rewrite.property.generateAccessors.description": "Generate 'get' and 'set' accessors", - "codeActions.source.organizeImports.title": "Organize imports" + "codeActions.source.organizeImports.title": "Organize imports", + "typescript.findAllFileReferences": "Find File References" } diff --git a/extensions/typescript-language-features/src/languageFeatures/fileReferences.ts b/extensions/typescript-language-features/src/languageFeatures/fileReferences.ts new file mode 100644 index 0000000000000000000000000000000000000000..d74db5c107af53e43b3690ea1d32ec241b6abfdb --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/fileReferences.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import { Command, CommandManager } from '../commands/commandManager'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { isSupportedLanguageMode } from '../utils/languageModeIds'; +import * as typeConverters from '../utils/typeConverters'; + +const localize = nls.loadMessageBundle(); + +class FileReferencesCommand implements Command { + + public static readonly context = 'tsSupportsFileReferences'; + public static readonly minVersion = API.v420; + + public readonly id = 'typescript.findAllFileReferences'; + + public constructor( + private readonly client: ITypeScriptServiceClient + ) { } + + public async execute(resource?: vscode.Uri) { + if (this.client.apiVersion.lt(FileReferencesCommand.minVersion)) { + vscode.window.showErrorMessage(localize('error.unsupportedVersion', "Find file references failed. Requires TypeScript 4.2+.")); + return; + } + + if (!resource) { + resource = vscode.window.activeTextEditor?.document.uri; + } + + if (!resource) { + vscode.window.showErrorMessage(localize('error.noResource', "Find file references failed. No resource provided.")); + return; + } + + const document = await vscode.workspace.openTextDocument(resource); + if (!isSupportedLanguageMode(document)) { + vscode.window.showErrorMessage(localize('error.unsupportedLanguage', "Find file references failed. Unsupported file type.")); + return; + } + + const openedFiledPath = this.client.toOpenedFilePath(document); + if (!openedFiledPath) { + vscode.window.showErrorMessage(localize('error.unknownFile', "Find file references failed. Unknown file type.")); + return; + } + + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: localize('progress.title', "Finding file references") + }, async (_progress, token) => { + + const response = await this.client.execute('fileReferences', { + file: openedFiledPath + }, token); + if (response.type !== 'response' || !response.body) { + return; + } + + const locations: vscode.Location[] = response.body.refs.map(reference => + typeConverters.Location.fromTextSpan(this.client.toResource(reference.file), reference)); + + const config = vscode.workspace.getConfiguration('references'); + const existingSetting = config.get('preferredLocation', undefined); + + await config.update('preferredLocation', 'view'); + try { + await vscode.commands.executeCommand('editor.action.showReferences', resource, new vscode.Position(0, 0), locations); + } finally { + await config.update('preferredLocation', existingSetting); + } + }); + } +} + + +export function register( + client: ITypeScriptServiceClient, + commandManager: CommandManager +) { + function updateContext() { + vscode.commands.executeCommand('setContext', FileReferencesCommand.context, client.apiVersion.gte(FileReferencesCommand.minVersion)); + } + updateContext(); + + commandManager.register(new FileReferencesCommand(client)); + return client.onTsServerStarted(() => updateContext()); +} diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 92ab84be308926bc82da03f3744ca487b1182c57..d0bac49ab91a6b063b8853eb893979145892c7ae 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -63,6 +63,7 @@ export default class LanguageProvider extends Disposable { import('./languageFeatures/directiveCommentCompletions').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/documentHighlight').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/documentSymbol').then(provider => this._register(provider.register(selector, this.client, cachedResponse))), + import('./languageFeatures/fileReferences').then(provider => this._register(provider.register(this.client, this.commandManager))), import('./languageFeatures/folding').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/formatting').then(provider => this._register(provider.register(selector, this.description.id, this.client, this.fileConfigurationManager))), import('./languageFeatures/hover').then(provider => this._register(provider.register(selector, this.client))), diff --git a/extensions/typescript-language-features/src/protocol.d.ts b/extensions/typescript-language-features/src/protocol.d.ts index e81fe81f2dbb43ea881b02c996f6d43aff08cfb0..af899e7c381167e2d71f54d9a3058d62dd12fe37 100644 --- a/extensions/typescript-language-features/src/protocol.d.ts +++ b/extensions/typescript-language-features/src/protocol.d.ts @@ -9,4 +9,22 @@ declare module 'typescript/lib/protocol' { interface Response { readonly _serverType?: ServerType; } + + 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; + } } + diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 588925ec6db70b235ed2e98be55f4cd61c6028c1..8bcfb24f5b24655880553cdd98b9b60f87cab408 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -68,6 +68,7 @@ interface StandardTsServerRequests { 'prepareCallHierarchy': [Proto.FileLocationRequestArgs, Proto.PrepareCallHierarchyResponse]; 'provideCallHierarchyIncomingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyIncomingCallsResponse]; 'provideCallHierarchyOutgoingCalls': [Proto.FileLocationRequestArgs, Proto.ProvideCallHierarchyOutgoingCallsResponse]; + 'fileReferences': [Proto.FileRequestArgs, Proto.FileReferencesResponse]; } interface NoResponseTsServerRequests { diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index afcf603178edf509f26ce837f803eddecf03c231..2282791a9ddbf5c96aaa5ea29e31efce50ef5e2e 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -34,6 +34,7 @@ export default class API { public static readonly v390 = API.fromSimpleString('3.9.0'); public static readonly v400 = API.fromSimpleString('4.0.0'); public static readonly v401 = API.fromSimpleString('4.0.1'); + public static readonly v420 = API.fromSimpleString('4.2.0'); public static fromVersionString(versionString: string): API { let version = semver.valid(versionString);