From 8f669bffabe8774fbd611e5d39d14f13480ced02 Mon Sep 17 00:00:00 2001 From: tgodzik Date: Fri, 26 Nov 2021 15:03:23 +0100 Subject: [PATCH] Replace config generation with automatic run of the current file Previously, when no launch.json was configured we would show a chain of quick picks so that the user would guided through configuring their entry. This however, would not be obvious to the users and would show up only once at the start. This was also not too obvious for the bginners and prompted https://github.com/scalameta/metals/issues/3272 Now, we remove the config generation and replace it by mechanism to detect if no launch.json is available and then run the command to run anything in the current file. We might return partly to config generation when we have a mechanism to discover the the mains and tests, which can then be used to suggest new entries to launch.json. --- src/extension.ts | 6 +- src/scalaDebugger.ts | 187 ++++++++----------------------------------- 2 files changed, 36 insertions(+), 157 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 8dfd9ee88..bd54f4b3c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1034,9 +1034,9 @@ function launchMetals( ); treeViews = startTreeView(client, outputChannel, context, viewIds); context.subscriptions.concat(treeViews.disposables); - scalaDebugger - .initialize(outputChannel) - .forEach((disposable) => context.subscriptions.push(disposable)); + scalaDebugger.initialize(outputChannel).forEach((disposable) => { + context.subscriptions.push(disposable); + }); client.onNotification(DecorationTypeDidChange.type, (options) => { decorationType = window.createTextEditorDecorationType(options); }); diff --git a/src/scalaDebugger.ts b/src/scalaDebugger.ts index 905601c2b..a3d2b0a5e 100644 --- a/src/scalaDebugger.ts +++ b/src/scalaDebugger.ts @@ -6,19 +6,23 @@ import { ProviderResult, WorkspaceFolder, DebugAdapterDescriptor, + DebugConfigurationProviderTriggerKind, } from "vscode"; -import { ServerCommands } from "metals-languageclient"; +import { + DebugDiscoveryParams, + RunType, + ServerCommands, +} from "metals-languageclient"; const configurationType = "scala"; -const launchRequestType = "launch"; -const attachRequestType = "attach"; export function initialize(outputChannel: vscode.OutputChannel): Disposable[] { outputChannel.appendLine("Initializing Scala Debugger"); return [ vscode.debug.registerDebugConfigurationProvider( configurationType, - new ScalaConfigProvider() + new ScalaMainConfigProvider(), + DebugConfigurationProviderTriggerKind.Initial ), vscode.debug.registerDebugAdapterDescriptorFactory( configurationType, @@ -58,164 +62,39 @@ export async function start( }); } -class ScalaConfigProvider implements vscode.DebugConfigurationProvider { - provideDebugConfigurations(): ProviderResult { - const mainClassPick = "Main Class"; - const testClassPick = "Test Suite"; - const attachPick = "Attach to JVM"; - - return vscode.window - .showQuickPick([mainClassPick, testClassPick, attachPick], { - placeHolder: - "Pick the kind of the class to debug (Press 'Escape' to create 'launch.json' with no initial configuration)", - }) - .then((result) => { - switch (result) { - case mainClassPick: - return this.provideDebugMainClassConfiguration().then((config) => [ - config, - ]); - case testClassPick: - return this.provideDebugTestClassConfiguration().then((config) => [ - config, - ]); - case attachPick: - return this.provideDebugAttachConfiguration().then((config) => [ - config, - ]); - default: - return []; - } - }) - .then( - (result) => result, - () => [] - ); - } - +class ScalaMainConfigProvider implements vscode.DebugConfigurationProvider { resolveDebugConfiguration( _folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration ): ProviderResult { - if (debugConfiguration.type === undefined) { - return null; - } - return debugConfiguration; - } - - private provideDebugMainClassConfiguration(): Thenable { - return this.askForOptionalBuildTarget().then((buildTarget) => - this.askForClassName().then((className) => - this.askForConfigurationName(className).then((name) => { - const result: DebugConfiguration = { - type: configurationType, - name: name, - request: launchRequestType, - mainClass: className, - buildTarget: buildTarget, - args: [], - }; - return result; - }) - ) - ); - } + let editor = vscode.window.activeTextEditor; + // debugConfiguration.type is undefined if there are no configurations + // we are running whatever is in the file + if (debugConfiguration.type === undefined && editor) { + const args: DebugDiscoveryParams = { + path: editor.document.uri.toString(true), + runType: RunType.RunOrTestFile, + }; + return vscode.commands + .executeCommand(ServerCommands.DebugAdapterStart, args) + .then((response) => { + if (response === undefined) { + return; + } - private provideDebugTestClassConfiguration(): Thenable { - return this.askForOptionalBuildTarget().then((buildTarget) => - this.askForClassName().then((className) => - this.askForConfigurationName(className).then((name) => { - const result: DebugConfiguration = { - type: configurationType, - name: name, - request: launchRequestType, - testClass: className, - buildTarget: buildTarget, - }; - return result; - }) - ) - ); - } + const port = debugServerFromUri(response.uri).port; - provideDebugAttachConfiguration(): Thenable { - return this.askForHostName().then((hostName) => - this.askForPort().then((port) => - this.askForBuildTarget().then((buildTarget) => { - const result: DebugConfiguration = { + const configuration: vscode.DebugConfiguration = { type: configurationType, - name: `Attach to ${hostName}:${port} - ${buildTarget}`, - request: attachRequestType, - hostName: hostName, - port: port, - buildTarget: buildTarget, + name: response.name, + noDebug: false, + request: "launch", + debugServer: port, // note: MUST be a number. vscode magic - automatically connects to the server }; - return result; - }) - ) - ); - } - - private askForOptionalBuildTarget(): Thenable { - return vscode.window - .showInputBox({ - prompt: "Enter the name of the build target", - placeHolder: "Optional, you can leave it empty", - }) - .then((buildTarget) => { - if (buildTarget === undefined) { - return Promise.reject(); - } else if (buildTarget === "") { - return undefined; - } else { - return buildTarget; - } - }); - } - - private askForHostName(): Thenable { - return vscode.window - .showInputBox({ - prompt: "Enter host name of the debuggee JVM", - placeHolder: "localhost", - }) - .then((hostName) => hostName ?? Promise.reject()); - } - - private askForPort(): Thenable { - return vscode.window - .showInputBox({ - prompt: "Enter port to attach to", - placeHolder: "5005", - }) - .then((port) => port ?? Promise.reject()) - .then((port) => parseInt(port)); - } - - private askForBuildTarget(): Thenable { - return vscode.window - .showInputBox({ - prompt: "Enter the name of the build target", - }) - .then((buildTarget) => buildTarget ?? Promise.reject()); - } - - private askForClassName(): Thenable { - return vscode.window - .showInputBox({ - prompt: "Enter the name of the class to debug", - placeHolder: ".", - }) - .then((name) => name ?? Promise.reject()); - } - - private askForConfigurationName(className: string): Thenable { - return vscode.window - .showInputBox({ - prompt: "Enter the name of the configuration", - value: `Debug ${className}`, - }) - .then((name) => name ?? Promise.reject()); + commands.executeCommand("workbench.panel.repl.view.focus"); + return configuration; + }); + } else return debugConfiguration; } }