Skip to content

Commit

Permalink
Replace config generation with automatic run of the current file
Browse files Browse the repository at this point in the history
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 scalameta/metals#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.
  • Loading branch information
tgodzik committed Dec 16, 2021
1 parent fde1ccc commit 8f669bf
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 157 deletions.
6 changes: 3 additions & 3 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down
187 changes: 33 additions & 154 deletions src/scalaDebugger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -58,164 +62,39 @@ export async function start(
});
}

class ScalaConfigProvider implements vscode.DebugConfigurationProvider {
provideDebugConfigurations(): ProviderResult<DebugConfiguration[]> {
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<DebugConfiguration> {
if (debugConfiguration.type === undefined) {
return null;
}
return debugConfiguration;
}

private provideDebugMainClassConfiguration(): Thenable<DebugConfiguration> {
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<DebugSession>(ServerCommands.DebugAdapterStart, args)
.then((response) => {
if (response === undefined) {
return;
}

private provideDebugTestClassConfiguration(): Thenable<DebugConfiguration> {
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<DebugConfiguration> {
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<string | undefined> {
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<string> {
return vscode.window
.showInputBox({
prompt: "Enter host name of the debuggee JVM",
placeHolder: "localhost",
})
.then((hostName) => hostName ?? Promise.reject());
}

private askForPort(): Thenable<number> {
return vscode.window
.showInputBox({
prompt: "Enter port to attach to",
placeHolder: "5005",
})
.then((port) => port ?? Promise.reject())
.then((port) => parseInt(port));
}

private askForBuildTarget(): Thenable<string> {
return vscode.window
.showInputBox({
prompt: "Enter the name of the build target",
})
.then((buildTarget) => buildTarget ?? Promise.reject());
}

private askForClassName(): Thenable<string> {
return vscode.window
.showInputBox({
prompt: "Enter the name of the class to debug",
placeHolder: "<package>.<Class>",
})
.then((name) => name ?? Promise.reject());
}

private askForConfigurationName(className: string): Thenable<string> {
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;
}
}

Expand Down

0 comments on commit 8f669bf

Please sign in to comment.