diff --git a/src/common/commands.ts b/src/common/commands.ts index a85afe2a..ea6245e6 100644 --- a/src/common/commands.ts +++ b/src/common/commands.ts @@ -1,5 +1,7 @@ import * as vscode from "vscode"; import { ExecuteCommandRequest, LanguageClient } from "vscode-languageclient/node"; +import { getConfiguration } from "./vscodeapi"; +import { ISettings } from "./settings"; const ISSUE_TRACKER = "https://github.com/astral-sh/ruff/issues"; @@ -36,3 +38,86 @@ async function executeCommand(lsClient: LanguageClient, command: string) { ); }); } + +/** + * Creates a debug information provider for the `ruff.printDebugInformation` command. + * + * This will open a new editor window with the debug information considering the active editor. + */ +export function createDebugInformationProvider( + getClient: () => LanguageClient | undefined, + serverId: string, + context: vscode.ExtensionContext, +) { + let configuration = getConfiguration(serverId) as unknown as ISettings; + if (configuration.nativeServer === false || configuration.nativeServer === "off") { + return async () => { + vscode.window.showInformationMessage( + "Debug information is only available when using the native server", + ); + }; + } + + const contentProvider = new (class implements vscode.TextDocumentContentProvider { + readonly uri = vscode.Uri.parse("ruff-server-debug://debug"); + readonly eventEmitter = new vscode.EventEmitter(); + + async provideTextDocumentContent(_uri: vscode.Uri): Promise { + const lsClient = getClient(); + if (!lsClient) { + return ""; + } + const textEditor = vscode.window.activeTextEditor; + const notebookEditor = vscode.window.activeNotebookEditor; + const params = { + command: `${serverId}.printDebugInformation`, + arguments: [ + { + textDocument: notebookEditor + ? { uri: notebookEditor.notebook.uri.toString() } + : textEditor + ? { uri: textEditor.document.uri.toString() } + : undefined, + }, + ], + }; + return await lsClient.sendRequest(ExecuteCommandRequest.type, params).then( + (result) => { + if (typeof result === "string") { + return result; + } + // For older Ruff version, we don't return a string but log the information. + return ""; + }, + async () => { + vscode.window.showErrorMessage( + `Failed to print debug information. Please consider opening an issue at ${ISSUE_TRACKER} with steps to reproduce.`, + ); + return ""; + }, + ); + } + + get onDidChange(): vscode.Event { + return this.eventEmitter.event; + } + })(); + + context.subscriptions.push( + vscode.workspace.registerTextDocumentContentProvider("ruff-server-debug", contentProvider), + ); + + return async () => { + contentProvider.eventEmitter.fire(contentProvider.uri); + const document = await vscode.workspace.openTextDocument(contentProvider.uri); + const content = document.getText(); + + // Show the document only if it has content. + if (content.length > 0) { + void (await vscode.window.showTextDocument(document, { + viewColumn: vscode.ViewColumn.Two, + preserveFocus: true, + })); + } + }; +} diff --git a/src/extension.ts b/src/extension.ts index b3e0691e..01668cfc 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,5 +1,5 @@ import * as vscode from "vscode"; -import { ExecuteCommandRequest, LanguageClient } from "vscode-languageclient/node"; +import { LanguageClient } from "vscode-languageclient/node"; import { LazyOutputChannel, logger } from "./common/logger"; import { checkVersion, @@ -24,12 +24,21 @@ import { registerCommand, } from "./common/vscodeapi"; import { getProjectRoot } from "./common/utilities"; -import { executeAutofix, executeFormat, executeOrganizeImports } from "./common/commands"; +import { + executeAutofix, + executeFormat, + executeOrganizeImports, + createDebugInformationProvider, +} from "./common/commands"; let lsClient: LanguageClient | undefined; let restartInProgress = false; let restartQueued = false; +function getClient(): LanguageClient | undefined { + return lsClient; +} + export async function activate(context: vscode.ExtensionContext): Promise { // This is required to get server name and module. This should be // the first thing that we do in this extension. @@ -188,26 +197,10 @@ export async function activate(context: vscode.ExtensionContext): Promise await executeOrganizeImports(lsClient, serverId); } }), - registerCommand(`${serverId}.debugInformation`, async () => { - let configuration = getConfiguration(serverId) as unknown as ISettings; - if (!lsClient || !configuration.nativeServer) { - return; - } - - const editor = vscode.window.activeTextEditor; - const params = { - command: `${serverId}.printDebugInformation`, - arguments: [ - { - textDocument: editor ? { uri: editor.document.uri.toString() } : null, - }, - ], - }; - - await lsClient.sendRequest(ExecuteCommandRequest.type, params).then(undefined, async () => { - vscode.window.showErrorMessage("Failed to print debug information."); - }); - }), + registerCommand( + `${serverId}.debugInformation`, + createDebugInformationProvider(getClient, serverId, context), + ), registerLanguageStatusItem(serverId, serverName, `${serverId}.showLogs`), );