Skip to content

Commit

Permalink
Merge pull request #564 from usethesource/debugger-ux
Browse files Browse the repository at this point in the history
Add custom view for easy access to the Rascal debugger
  • Loading branch information
rodinaarssen authored Jan 30, 2025
2 parents 1a3d93e + 9c0440b commit 32b0a8c
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 7 deletions.
32 changes: 32 additions & 0 deletions rascal-vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,28 @@
{
"command": "rascalmpl.importModule",
"title": "Start Rascal Terminal and Import this module"
},
{
"command": "rascalmpl.startDebuggerForRepl",
"title": "Start Rascal debugger for REPL",
"icon": "$(debug)"
}
],
"menus": {
"commandPalette": [
{
"command": "rascalmpl.startDebuggerForRepl",
"when": "false"
}
],
"view/item/context": [
{
"command": "rascalmpl.startDebuggerForRepl",
"when": "view == rascalmpl-debugger-view && viewItem == 'canStartDebugging'",
"group": "inline"
}
]
},
"languages": [
{
"id": "rascalmpl",
Expand Down Expand Up @@ -89,12 +109,24 @@
"icon": "./assets/images/rascal-logo-v2.1.svg",
"visibility": "collapsed"
}
],
"debug": [
{
"id": "rascalmpl-debugger-view",
"name": "Debug Rascal Terminal",
"icon": "$(debug)",
"visibility": "visible"
}
]
},
"viewsWelcome": [
{
"view": "rascalmpl-configuration-view",
"contents": "No Rascal Projects found in the workspace"
},
{
"view": "rascalmpl-debugger-view",
"contents": "No active Rascal REPLs found"
}
],
"breakpoints": [
Expand Down
2 changes: 2 additions & 0 deletions rascal-vscode-extension/src/RascalExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { RascalTerminalLinkProvider } from './RascalTerminalLinkProvider';
import { VSCodeUriResolverServer } from './fs/VSCodeURIResolver';
import { RascalLibraryProvider } from './ux/LibraryNavigator';
import { FileType } from 'vscode';
import { RascalDebugViewProvider } from './dap/RascalDebugView';

export class RascalExtension implements vscode.Disposable {
private readonly vfsServer: VSCodeUriResolverServer;
Expand All @@ -55,6 +56,7 @@ export class RascalExtension implements vscode.Disposable {
checkForJVMUpdate();

vscode.window.registerTreeDataProvider('rascalmpl-configuration-view', new RascalLibraryProvider(this.rascal.rascalClient));
vscode.window.registerTreeDataProvider('rascalmpl-debugger-view', new RascalDebugViewProvider(this.rascal.rascalDebugClient, context));
vscode.window.registerTerminalLinkProvider(new RascalTerminalLinkProvider(this.rascal.rascalClient));
}

Expand Down
13 changes: 6 additions & 7 deletions rascal-vscode-extension/src/dap/RascalDebugClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
import { debug, DebugConfiguration, DebugSession, Terminal, window } from "vscode";
import { debug, DebugConfiguration, DebugSession, Terminal, window, EventEmitter } from "vscode";
import { RascalDebugAdapterDescriptorFactory } from "./RascalDebugAdapterDescriptorFactory";
import { RascalDebugConfigurationProvider } from "./RascalDebugConfigurationProvider";

Expand All @@ -37,7 +37,8 @@ export class RascalDebugClient {
debugSocketServersPorts: Map<number, number>; // Terminal processID -> socket server port for debug
runningDebugSessionsPorts: Set<number>; // Stores all running debug session server ports


private portEventEmitter = new EventEmitter<{processId: number, serverPort: number}>();
readonly portRegistrationEvent = this.portEventEmitter.event;

constructor(){
this.rascalDescriptorFactory = new RascalDebugAdapterDescriptorFactory();
Expand Down Expand Up @@ -77,13 +78,11 @@ export class RascalDebugClient {

registerDebugServerPort(processID: number, serverPort: number){
this.debugSocketServersPorts.set(processID, serverPort);
this.portEventEmitter.fire({"processId": processID, "serverPort": serverPort});
}

getServerPort(processID: number | undefined){
if(processID !== undefined && this.debugSocketServersPorts.has(processID)){
return this.debugSocketServersPorts.get(processID);
}
return undefined;
getServerPort(processId: number){
return this.debugSocketServersPorts.get(processId);
}

isConnectedToDebugServer(serverPort: number){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ export class RascalDebugConfigurationProvider implements DebugConfigurationProvi

if (!debugConfiguration['serverPort']){
const terminalProcessID = await window.activeTerminal?.processId;
if (terminalProcessID === undefined) {
throw Error("Active terminal has no associated process ID!");
}
const port = this.debugClient.getServerPort(terminalProcessID);
if(port === undefined) {
throw Error("Active terminal has not a debug server port registered !");
Expand Down
109 changes: 109 additions & 0 deletions rascal-vscode-extension/src/dap/RascalDebugView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright (c) 2018-2025, NWO-I CWI and Swat.engineering
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
import * as vscode from 'vscode';
import { RascalDebugClient } from './RascalDebugClient';

export class RascalDebugViewProvider implements vscode.TreeDataProvider<RascalReplNode> {
private changeEmitter = new vscode.EventEmitter<RascalReplNode | undefined>();
readonly onDidChangeTreeData = this.changeEmitter.event;

constructor(private readonly rascalDebugClient: RascalDebugClient, readonly context: vscode.ExtensionContext) {
const fireEmitter = (_: vscode.Terminal | vscode.DebugSession | {processId: number, serverPort: number} | undefined) : void => {
this.changeEmitter.fire(undefined);
};

vscode.window.onDidOpenTerminal(fireEmitter, this, context.subscriptions);
vscode.window.onDidCloseTerminal(fireEmitter, this, context.subscriptions);
vscode.window.onDidChangeActiveTerminal(fireEmitter, this, context.subscriptions);
vscode.debug.onDidStartDebugSession(fireEmitter, this, context.subscriptions);
vscode.debug.onDidTerminateDebugSession(fireEmitter, this, context.subscriptions);

this.rascalDebugClient.portRegistrationEvent(fireEmitter, this, context.subscriptions);

this.context.subscriptions.push(
vscode.commands.registerCommand("rascalmpl.startDebuggerForRepl", (replNode: RascalReplNode) => {
if (replNode.serverPort !== undefined) {
this.rascalDebugClient.startDebuggingSession(replNode.serverPort);
}
}, this)
);
}

getTreeItem(element: RascalReplNode): vscode.TreeItem | Thenable<vscode.TreeItem> {
return element;
}
getChildren(element?: RascalReplNode | undefined): vscode.ProviderResult<RascalReplNode[]> {
if (element === undefined) {
return this.updateRascalDebugView();
}
return [];
}
getParent?(_element: RascalReplNode): vscode.ProviderResult<RascalReplNode> {
//The Rascal debug view gives a flat list of the opened Rascal terminals. As such, only root items exit in this view.
return undefined;
}
resolveTreeItem?(item: vscode.TreeItem, _element: RascalReplNode, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.TreeItem> {
return item;
}

makeLabel(label: string, isActiveTerminal : boolean) : string | vscode.TreeItemLabel {
if (isActiveTerminal) {
return {label: label, highlights : [[0, label.length]]};
}
return label;
}

async updateRascalDebugView() : Promise<RascalReplNode[]> {
const result : RascalReplNode[] = [];
const activeTerminalProcessId = await vscode.window.activeTerminal?.processId;
for (const terminal of vscode.window.terminals) {
const processId = await terminal.processId;
if (processId === undefined) {
continue;
}
if (terminal.name.includes("Rascal terminal")) {
const label = this.makeLabel(terminal.name, processId === activeTerminalProcessId);
const serverPort = this.rascalDebugClient.getServerPort(processId);
const isDebugging = serverPort !== undefined && this.rascalDebugClient.isConnectedToDebugServer(serverPort);
const replNode = new RascalReplNode(label, serverPort, isDebugging);
if (serverPort !== undefined && !isDebugging) {
replNode.contextValue = "canStartDebugging";
}
result.push(replNode);
}
}
return result;
}

}

export class RascalReplNode extends vscode.TreeItem {
constructor(label : string | vscode.TreeItemLabel, readonly serverPort : number | undefined, isDebugging : boolean) {
super(label, vscode.TreeItemCollapsibleState.None);
this.iconPath = new vscode.ThemeIcon(isDebugging ? "debug" : "terminal");
}
}

0 comments on commit 32b0a8c

Please sign in to comment.