Add 'go to source definition' command

Fixes #147532

Requires TS 4.7+
pull/147750/head
Matt Bierner 2022-04-19 19:31:58 -07:00
parent 973ebbcdd8
commit d851ea5d49
No known key found for this signature in database
GPG Key ID: 099C331567E11888
5 changed files with 157 additions and 0 deletions

View File

@ -65,6 +65,7 @@
"onCommand:_typescript.configurePlugin",
"onCommand:_typescript.learnMoreAboutRefactorings",
"onCommand:typescript.fileReferences",
"onCommand:typescript.goToSourceDefinition",
"onTaskType:typescript",
"onLanguage:jsonc"
],
@ -1229,6 +1230,11 @@
"command": "typescript.findAllFileReferences",
"title": "%typescript.findAllFileReferences%",
"category": "TypeScript"
},
{
"command": "typescript.goToSourceDefinition",
"title": "%typescript.goToSourceDefinition%",
"category": "TypeScript"
}
],
"menus": {
@ -1280,6 +1286,32 @@
{
"command": "typescript.findAllFileReferences",
"when": "tsSupportsFileReferences && typescript.isManagedFile"
},
{
"command": "typescript.goToSourceDefinition",
"when": "tsSupportsSourceDefinition && typescript.isManagedFile"
}
],
"editor/context": [
{
"command": "typescript.goToSourceDefinition",
"when": "tsSupportsSourceDefinition && resourceLangId == typescript",
"group": "navigation@9"
},
{
"command": "typescript.goToSourceDefinition",
"when": "tsSupportsSourceDefinition && resourceLangId == typescriptreact",
"group": "navigation@9"
},
{
"command": "typescript.goToSourceDefinition",
"when": "tsSupportsSourceDefinition && resourceLangId == javascript",
"group": "navigation@9"
},
{
"command": "typescript.goToSourceDefinition",
"when": "tsSupportsSourceDefinition && resourceLangId == javascriptreact",
"group": "navigation@9"
}
],
"explorer/context": [

View File

@ -190,6 +190,7 @@
"codeActions.refactor.rewrite.property.generateAccessors.description": "Generate 'get' and 'set' accessors",
"codeActions.source.organizeImports.title": "Organize imports",
"typescript.findAllFileReferences": "Find File References",
"typescript.goToSourceDefinition": "Go to Source Definition",
"configuration.suggest.classMemberSnippets.enabled": "Enable/disable snippet completions for class members. Requires using TypeScript 4.5+ in the workspace",
"configuration.suggest.objectLiteralMethodSnippets.enabled": "Enable/disable snippet completions for methods in object literals. Requires using TypeScript 4.7+ in the workspace"
}

View File

@ -0,0 +1,122 @@
/*---------------------------------------------------------------------------------------------
* 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 * as Proto from '../protocol';
import { ExecConfig, ITypeScriptServiceClient, ServerResponse } from '../typescriptService';
import API from '../utils/api';
import { isSupportedLanguageMode } from '../utils/languageIds';
import * as typeConverters from '../utils/typeConverters';
const localize = nls.loadMessageBundle();
namespace ExperimentalProto {
export const enum CommandTypes {
FindSourceDefinition = 'findSourceDefinition'
}
export interface SourceDefinitionRequestArgs extends Proto.FileLocationRequestArgs { }
export interface SourceDefinitionRequest extends Proto.Request {
command: CommandTypes.FindSourceDefinition;
arguments: SourceDefinitionRequestArgs;
}
export interface InlayHintsResponse extends Proto.DefinitionResponse { }
export interface IExtendedTypeScriptServiceClient {
execute<K extends keyof ExtendedTsServerRequests>(
command: K,
args: ExtendedTsServerRequests[K][0],
token: vscode.CancellationToken,
config?: ExecConfig
): Promise<ServerResponse.Response<ExtendedTsServerRequests[K][1]>>;
}
export interface ExtendedTsServerRequests {
'findSourceDefinition': [SourceDefinitionRequestArgs, InlayHintsResponse];
}
}
class SourceDefinitionCommand implements Command {
public static readonly context = 'tsSupportsSourceDefinition';
public static readonly minVersion = API.v470;
public readonly id = 'typescript.goToSourceDefinition';
public constructor(
private readonly client: ITypeScriptServiceClient
) { }
public async execute() {
if (this.client.apiVersion.lt(SourceDefinitionCommand.minVersion)) {
vscode.window.showErrorMessage(localize('error.unsupportedVersion', "Go to Source Definition failed. Requires TypeScript 4.2+."));
return;
}
const activeEditor = vscode.window.activeTextEditor;
if (!activeEditor) {
vscode.window.showErrorMessage(localize('error.noResource', "Go to Source Definition failed. No resource provided."));
return;
}
const resource = activeEditor.document.uri;
const document = await vscode.workspace.openTextDocument(resource);
if (!isSupportedLanguageMode(document)) {
vscode.window.showErrorMessage(localize('error.unsupportedLanguage', "Go to Source Definition failed. Unsupported file type."));
return;
}
const openedFiledPath = this.client.toOpenedFilePath(document);
if (!openedFiledPath) {
vscode.window.showErrorMessage(localize('error.unknownFile', "Go to Source Definition failed. Unknown file type."));
return;
}
await vscode.window.withProgress({
location: vscode.ProgressLocation.Window,
title: localize('progress.title', "Finding source definitions")
}, async (_progress, token) => {
const position = activeEditor.selection.anchor;
const args = typeConverters.Position.toFileLocationRequestArgs(openedFiledPath, position);
const response = await (this.client as ExperimentalProto.IExtendedTypeScriptServiceClient).execute('findSourceDefinition', args, token);
if (response.type === 'response' && response.body) {
const locations: vscode.Location[] = response.body.map(reference =>
typeConverters.Location.fromTextSpan(this.client.toResource(reference.file), reference));
if (locations.length) {
if (locations.length === 1) {
vscode.commands.executeCommand('vscode.open', locations[0].uri.with({
fragment: `L${locations[0].range.start.line + 1},${locations[0].range.start.character + 1}`
}));
} else {
vscode.commands.executeCommand('editor.action.showReferences', resource, position, locations);
}
return;
}
}
vscode.window.showErrorMessage(localize('error.noReferences', "No source definitions found."));
});
}
}
export function register(
client: ITypeScriptServiceClient,
commandManager: CommandManager
) {
function updateContext() {
vscode.commands.executeCommand('setContext', SourceDefinitionCommand.context, client.apiVersion.gte(SourceDefinitionCommand.minVersion));
}
updateContext();
commandManager.register(new SourceDefinitionCommand(client));
return client.onTsServerStarted(() => updateContext());
}

View File

@ -82,6 +82,7 @@ export default class LanguageProvider extends Disposable {
import('./languageFeatures/semanticTokens').then(provider => this._register(provider.register(selector, this.client))),
import('./languageFeatures/signatureHelp').then(provider => this._register(provider.register(selector, this.client))),
import('./languageFeatures/smartSelect').then(provider => this._register(provider.register(selector, this.client))),
import('./languageFeatures/sourceDefinition').then(provider => this._register(provider.register(this.client, this.commandManager))),
import('./languageFeatures/tagClosing').then(provider => this._register(provider.register(selector, this.description, this.client))),
import('./languageFeatures/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))),
import('./languageFeatures/inlayHints').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager))),

View File

@ -39,6 +39,7 @@ export default class API {
public static readonly v430 = API.fromSimpleString('4.3.0');
public static readonly v440 = API.fromSimpleString('4.4.0');
public static readonly v460 = API.fromSimpleString('4.6.0');
public static readonly v470 = API.fromSimpleString('4.7.0');
public static fromVersionString(versionString: string): API {
let version = semver.valid(versionString);