import type * as lsp from "vscode-languageserver-protocol" import { EditorView, Command, KeyBinding } from "@codemirror/view" import { LSPPlugin } from "@codemirror/lsp-client"; import { addEditor } from "../editorgrid"; import { OpenFile } from "../filestate"; function getDefinition(plugin: LSPPlugin, pos: number) { return plugin.client.request("textDocument/definition", { textDocument: { uri: plugin.uri }, position: plugin.toPosition(pos) }) } function getDeclaration(plugin: LSPPlugin, pos: number) { return plugin.client.request("textDocument/declaration", { textDocument: { uri: plugin.uri }, position: plugin.toPosition(pos) }) } function getTypeDefinition(plugin: LSPPlugin, pos: number) { return plugin.client.request("textDocument/typeDefinition", { textDocument: { uri: plugin.uri }, position: plugin.toPosition(pos) }) } function getImplementation(plugin: LSPPlugin, pos: number) { return plugin.client.request("textDocument/implementation", { textDocument: { uri: plugin.uri }, position: plugin.toPosition(pos) }) } function jumpToOrigin(view: EditorView, type: { get: typeof getDefinition, capability: keyof lsp.ServerCapabilities }): boolean { const plugin = LSPPlugin.get(view); const hasCapability = plugin.client.serverCapabilities ? !!plugin.client.serverCapabilities[type.capability] : null; if (!plugin || !hasCapability) return false plugin.client.sync() plugin.client.withMapping(mapping => type.get(plugin, view.state.selection.main.head).then(async response => { if (!response) return let loc = Array.isArray(response) ? response[0] : response; const path = new URL(loc.uri).pathname; const target = addEditor(await OpenFile.openFile(path)); const pos = mapping.getMapping(loc.uri) ? mapping.mapPosition(loc.uri, loc.range.start) : plugin.fromPosition(loc.range.start, target.view.state.doc); target.view.dispatch({selection: {anchor: pos}, scrollIntoView: true, userEvent: "select.definition"}); }, error => plugin.reportError("Find definition failed", error))) return true } /// Jump to the definition of the symbol at the cursor. To support /// cross-file jumps, you'll need to implement /// [`Workspace.displayFile`](#lsp-client.Workspace.displayFile). export const jumpToDefinition: Command = view => jumpToOrigin(view, { get: getDefinition, capability: "definitionProvider" }) /// Jump to the declaration of the symbol at the cursor. export const jumpToDeclaration: Command = view => jumpToOrigin(view, { get: getDeclaration, capability: "declarationProvider" }) /// Jump to the type definition of the symbol at the cursor. export const jumpToTypeDefinition: Command = view => jumpToOrigin(view, { get: getTypeDefinition, capability: "typeDefinitionProvider" }) /// Jump to the implementation of the symbol at the cursor. export const jumpToImplementation: Command = view => jumpToOrigin(view, { get: getImplementation, capability: "implementationProvider" }) /// Binds F12 to [`jumpToDefinition`](#lsp-client.jumpToDefinition). export const jumpToDefinitionKeymap: readonly KeyBinding[] = [ { key: "F12", run: jumpToDefinition, preventDefault: true }, ]