Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom view for easy access to the Rascal debugger #564

Merged
merged 26 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b54a243
Created custom view listing Rascal REPLs for better interaction with …
rodinaarssen Jan 23, 2025
45cfa49
Merge remote-tracking branch 'origin' into debugger-ux
rodinaarssen Jan 23, 2025
9351f4f
Changed function signature
rodinaarssen Jan 28, 2025
6c65a03
Removed debug prints
rodinaarssen Jan 28, 2025
01035c7
Removed more debug prints
rodinaarssen Jan 28, 2025
07418db
Reduced number of commands and simplified logic
rodinaarssen Jan 28, 2025
4320b9e
Using debug logo in Rascal debug view
rodinaarssen Jan 28, 2025
cbbb04f
Added command for updating debug view upon registering debug port
rodinaarssen Jan 28, 2025
77cd7c1
Using debug icon for view
rodinaarssen Jan 28, 2025
1e13fc8
Merge remote-tracking branch 'origin/main' into debugger-ux
rodinaarssen Jan 28, 2025
cd479cd
Changed name of Rascal debug view
rodinaarssen Jan 28, 2025
122a95b
Added terminal or debug icon in front of every entry in the Rascal de…
rodinaarssen Jan 28, 2025
25ac3f8
Prevent debug command from showing up in the command palette
rodinaarssen Jan 30, 2025
65197d5
Improved constructor of RascalReplNode
rodinaarssen Jan 30, 2025
3f9cc88
Moved registration of the debug command
rodinaarssen Jan 30, 2025
3a41a23
Removed the debug command from package.json and updated logic corresp…
rodinaarssen Jan 30, 2025
603a52d
Added comment
rodinaarssen Jan 30, 2025
2580d9f
Revert "Removed the debug command from package.json and updated logic…
rodinaarssen Jan 30, 2025
d4b0aac
Fixed type
rodinaarssen Jan 30, 2025
6a1a8a7
Using Event instead of internal command
rodinaarssen Jan 30, 2025
57352e6
Merge remote-tracking branch 'origin/main' into debugger-ux
rodinaarssen Jan 30, 2025
484fd1f
Updated year in license
rodinaarssen Jan 30, 2025
01b45fd
Refactored subscriptions to be an argument
rodinaarssen Jan 30, 2025
a644b26
Removed spurious assignment in constructor
rodinaarssen Jan 30, 2025
f640a5b
Improved logic around checking whether a terminal is currently active
rodinaarssen Jan 30, 2025
9c0440b
Added payload to debug event
rodinaarssen Jan 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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",
rodinaarssen marked this conversation as resolved.
Show resolved Hide resolved
"title": "Start Rascal debugger for REPL",
"icon": "$(debug)"
}
],
"menus": {
"commandPalette": [
{
"command": "rascalmpl.startDebuggerForRepl",
"when": "false"
rodinaarssen marked this conversation as resolved.
Show resolved Hide resolved
}
],
"view/item/context": [
{
"command": "rascalmpl.startDebuggerForRepl",
rodinaarssen marked this conversation as resolved.
Show resolved Hide resolved
"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;
rodinaarssen marked this conversation as resolved.
Show resolved Hide resolved
}
resolveTreeItem?(item: vscode.TreeItem, _element: RascalReplNode, _token: vscode.CancellationToken): vscode.ProviderResult<vscode.TreeItem> {
return item;
}

makeLabel(label: string, isActiveTerminal : boolean) : string | vscode.TreeItemLabel {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend using a FileDecoratorProvider to highlight the active terminal instead of "abusing" the highlights concept.

Each of the RascalReplNode will need a unique Uri and the decorator will

  • compare the Uri to the vscode.window.activeTerminal
  • subscribe to the onDidChangeActiveTerminal event to refresh the decoration on the previous and newly active Uris

Decorations can give the active node a badge like "🎯" and give currently-debug-enabled nodes a badge like "🐞"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a nice trick indeed :) but in a way, "abusing" a different concept. As a terminal is not a file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, FileDecoratorProvider is an awkward name. It can decorate anything with a Uri. The decorations are most visible in a TreeView, but they also appear on editor tabs.

if (isActiveTerminal) {
return {label: label, highlights : [[0, label.length]]};
rodinaarssen marked this conversation as resolved.
Show resolved Hide resolved
}
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");
}
}
Loading