From 32a27f5e392c58ce0218c12772659e91e4fcfd67 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 5 Sep 2025 16:12:27 +0200 Subject: [PATCH 1/9] Move private methods to convert from `IConstructor` to `MessageParams` (LSP) to a reusable public static one --- .../rascalmpl/vscode/lsp/LSPIDEServices.java | 30 ++++++++++++++++++ .../ParametricTextDocumentService.java | 28 ++--------------- .../lsp/rascal/RascalTextDocumentService.java | 31 ++----------------- 3 files changed, 34 insertions(+), 55 deletions(-) diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/LSPIDEServices.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/LSPIDEServices.java index c8f76e48c..58e5d4384 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/LSPIDEServices.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/LSPIDEServices.java @@ -37,6 +37,8 @@ import org.apache.logging.log4j.Logger; import org.eclipse.lsp4j.ApplyWorkspaceEditParams; import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.MessageParams; +import org.eclipse.lsp4j.MessageType; import org.eclipse.lsp4j.PublishDiagnosticsParams; import org.eclipse.lsp4j.ShowDocumentParams; import org.eclipse.lsp4j.WorkspaceFolder; @@ -77,6 +79,34 @@ public LSPIDEServices(IBaseLanguageClient client, IBaseTextDocumentService docSe this.monitor = monitor; } + public static MessageParams toMessageParams(IConstructor message) { + var params = new MessageParams(); + switch (message.getName()) { + case "error": { + params.setType(MessageType.Error); + break; + } + case "warning": { + params.setType(MessageType.Warning); + break; + } + case "info": { + params.setType(MessageType.Info); + break; + } + default: params.setType(MessageType.Log); + } + + var msgText = ((IString) message.get("msg")).getValue(); + if (message.has("at")) { + var at = ((ISourceLocation) message.get("at")).getURI(); + params.setMessage(String.format("%s (at %s)", msgText, at)); + } else { + params.setMessage(msgText); + } + return params; + } + @Override public PrintWriter stderr() { assert false: "this should not be used here"; diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ParametricTextDocumentService.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ParametricTextDocumentService.java index 616d157e1..d3836dbd9 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ParametricTextDocumentService.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ParametricTextDocumentService.java @@ -117,6 +117,7 @@ import org.rascalmpl.vscode.lsp.BaseWorkspaceService; import org.rascalmpl.vscode.lsp.IBaseLanguageClient; import org.rascalmpl.vscode.lsp.IBaseTextDocumentService; +import org.rascalmpl.vscode.lsp.LSPIDEServices; import org.rascalmpl.vscode.lsp.TextDocumentState; import org.rascalmpl.vscode.lsp.parametric.model.ParametricFileFacts; import org.rascalmpl.vscode.lsp.parametric.model.ParametricSummary; @@ -436,35 +437,10 @@ private void showMessages(ISet messages) { } for (var msg : messages) { - client.showMessage(setMessageParams((IConstructor) msg)); + client.showMessage(LSPIDEServices.toMessageParams((IConstructor) msg)); } } - private MessageParams setMessageParams(IConstructor message) { - var params = new MessageParams(); - switch (message.getName()) { - case "warning": { - params.setType(MessageType.Warning); - break; - } - case "info": { - params.setType(MessageType.Info); - break; - } - default: params.setType(MessageType.Log); - } - - var msgText = ((IString) message.get("msg")).getValue(); - if (message.has("at")) { - var at = ((ISourceLocation) message.get("at")).getURI(); - params.setMessage(String.format("%s (at %s)", msgText, at)); - } else { - params.setMessage(msgText); - } - - return params; - } - @Override public void didRenameFiles(RenameFilesParams params, List workspaceFolders) { Map> byContrib = bundleRenamesByContribution(params.getFiles()); diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalTextDocumentService.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalTextDocumentService.java index cd6afe898..c1a929085 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalTextDocumentService.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalTextDocumentService.java @@ -110,6 +110,7 @@ import org.rascalmpl.vscode.lsp.BaseWorkspaceService; import org.rascalmpl.vscode.lsp.IBaseLanguageClient; import org.rascalmpl.vscode.lsp.IBaseTextDocumentService; +import org.rascalmpl.vscode.lsp.LSPIDEServices; import org.rascalmpl.vscode.lsp.TextDocumentState; import org.rascalmpl.vscode.lsp.rascal.RascalLanguageServices.CodeLensSuggestion; import org.rascalmpl.vscode.lsp.rascal.model.FileFacts; @@ -391,38 +392,10 @@ public CompletableFuture rename(RenameParams params) { private void showMessages(ISet messages) { for (var msg : messages) { - client.showMessage(setMessageParams((IConstructor) msg)); + client.showMessage(LSPIDEServices.toMessageParams((IConstructor) msg)); } } - private MessageParams setMessageParams(IConstructor message) { - var params = new MessageParams(); - switch (message.getName()) { - case "error": { - params.setType(MessageType.Error); - break; - } - case "warning": { - params.setType(MessageType.Warning); - break; - } - case "info": { - params.setType(MessageType.Info); - break; - } - default: params.setType(MessageType.Log); - } - - var msgText = ((IString) message.get("msg")).getValue(); - if (message.has("at")) { - var at = ((ISourceLocation) message.get("at")).getURI(); - params.setMessage(String.format("%s (at %s)", msgText, at)); - } else { - params.setMessage(msgText); - } - return params; - } - @Override public CompletableFuture hover(HoverParams params) { logger.debug("textDocument/hover: {} at {}", params.getTextDocument(), params.getPosition()); From 45d32fa355ad14189a6b65b03b6ed3e2dbdf03c1 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 5 Sep 2025 16:13:22 +0200 Subject: [PATCH 2/9] Add `showMessage` override to `LSPIDEServices` so messages can be shown in the IDE via LSP --- .../main/java/org/rascalmpl/vscode/lsp/LSPIDEServices.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/LSPIDEServices.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/LSPIDEServices.java index 58e5d4384..1a35bbba6 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/LSPIDEServices.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/LSPIDEServices.java @@ -79,6 +79,11 @@ public LSPIDEServices(IBaseLanguageClient client, IBaseTextDocumentService docSe this.monitor = monitor; } + @Override + public void showMessage(IConstructor message) { + languageClient.showMessage(toMessageParams(message)); + } + public static MessageParams toMessageParams(IConstructor message) { var params = new MessageParams(); switch (message.getName()) { From e8174b64e49c8533d0a2a62d608947f035c22469 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 5 Sep 2025 16:14:26 +0200 Subject: [PATCH 3/9] Add example command to show messages to the Pico language server --- .../rascal/library/demo/lang/pico/LanguageServer.rsc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/rascal-lsp/src/main/rascal/library/demo/lang/pico/LanguageServer.rsc b/rascal-lsp/src/main/rascal/library/demo/lang/pico/LanguageServer.rsc index a5a11ee31..efc855859 100644 --- a/rascal-lsp/src/main/rascal/library/demo/lang/pico/LanguageServer.rsc +++ b/rascal-lsp/src/main/rascal/library/demo/lang/pico/LanguageServer.rsc @@ -159,11 +159,15 @@ default list[CodeAction] picoCodeActionService(Focus _focus) = []; data Command = renameAtoB(start[Program] program) | removeDecl(start[Program] program, IdType toBeRemoved) + | showInfoMessage(start[Program] program) ; @synopsis{Adds an example lense to the entire program.} lrel[loc,Command] picoCodeLenseService(start[Program] input) - = []; + = [ + , + + ]; @synopsis{Generates inlay hints that explain the type of each variable usage.} list[InlayHint] picoInlayHintService(start[Program] input) { @@ -192,6 +196,11 @@ value picoExecutionService(removeDecl(start[Program] program, IdType toBeRemoved return ("result": true); } +@synopsis{Command handler to show an info message} +void picoExecutionService(showInfoMessage(start[Program] program)) { + showMessage(info("Info message", program.src)); +} + @synopsis{Prepares the rename service by checking if the id can be renamed} loc picoRenamePreparingService(Focus _:[Id id, *_]) { if ("" == "fixed") { From 3737092020a63cbb51c34c128a45dd9741d527ae Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 5 Sep 2025 16:15:57 +0200 Subject: [PATCH 4/9] Add UI test for showing messages --- .../src/test/vscode-suite/dsl.test.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts b/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts index 5c3a80671..b929a974d 100644 --- a/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts +++ b/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts @@ -195,6 +195,24 @@ parameterizedDescribe(function (errorRecovery: boolean) { await ide.assertLineBecomes(editor, 9, "b := 2;", "a variable should be changed to b"); }); + it.only("show message works", async function() { + if (errorRecovery) { this.skip(); } + const editor = await ide.openModule(TestWorkspace.picoFile); + const lens = await driver.wait(() => editor.getCodeLens("Show info message."), Delays.verySlow, "'Show info message' lens should be available"); + await lens!.click(); + await driver.wait(async () => { + const notifications = await new Workbench().getNotifications(); + for (const notification of notifications) { + const message = await notification.getMessage(); + console.log(message); + if (message.startsWith("Info message")) { + return true; + } + } + return false; + }, Delays.normal, "The info message should be shown after clicking the lens"); + }); + it("quick fix works", async function() { if (errorRecovery) { this.skip(); } const editor = await ide.openModule(TestWorkspace.picoFile); From b8a204dea6845deb05c0fe19cc86b4dd8e6e8522 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 5 Sep 2025 16:26:06 +0200 Subject: [PATCH 5/9] Remove println --- rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts b/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts index b929a974d..9e432b88a 100644 --- a/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts +++ b/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts @@ -204,7 +204,6 @@ parameterizedDescribe(function (errorRecovery: boolean) { const notifications = await new Workbench().getNotifications(); for (const notification of notifications) { const message = await notification.getMessage(); - console.log(message); if (message.startsWith("Info message")) { return true; } From e281a4e087b4f6a65eb5e0fdef380135c14c194b Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 5 Sep 2025 16:31:10 +0200 Subject: [PATCH 6/9] Remove unused import --- .../rascalmpl/vscode/lsp/rascal/RascalTextDocumentService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalTextDocumentService.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalTextDocumentService.java index c1a929085..c0421184b 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalTextDocumentService.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalTextDocumentService.java @@ -134,7 +134,6 @@ import io.usethesource.vallang.IList; import io.usethesource.vallang.ISet; import io.usethesource.vallang.ISourceLocation; -import io.usethesource.vallang.IString; import io.usethesource.vallang.IValue; public class RascalTextDocumentService implements IBaseTextDocumentService, LanguageClientAware { From 3d4f51351ecbc70947ef85e149a16a7204b2c453 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 5 Sep 2025 16:32:59 +0200 Subject: [PATCH 7/9] Increase delay --- rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts b/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts index 9e432b88a..1cb1198d7 100644 --- a/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts +++ b/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts @@ -209,7 +209,7 @@ parameterizedDescribe(function (errorRecovery: boolean) { } } return false; - }, Delays.normal, "The info message should be shown after clicking the lens"); + }, Delays.verySlow, "The info message should be shown after clicking the lens"); }); it("quick fix works", async function() { From 849538b6203d12222318531e6f98567fd9a72aa2 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Fri, 5 Sep 2025 16:47:11 +0200 Subject: [PATCH 8/9] Remove `.only` from UI tests --- rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts b/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts index 1cb1198d7..3c171e5fe 100644 --- a/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts +++ b/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts @@ -195,7 +195,7 @@ parameterizedDescribe(function (errorRecovery: boolean) { await ide.assertLineBecomes(editor, 9, "b := 2;", "a variable should be changed to b"); }); - it.only("show message works", async function() { + it("show message works", async function() { if (errorRecovery) { this.skip(); } const editor = await ide.openModule(TestWorkspace.picoFile); const lens = await driver.wait(() => editor.getCodeLens("Show info message."), Delays.verySlow, "'Show info message' lens should be available"); From 12ba46de52239e9488af1d863b38e124428683b8 Mon Sep 17 00:00:00 2001 From: Sung-Shik Jongmans Date: Sun, 7 Sep 2025 08:36:30 +0200 Subject: [PATCH 9/9] Fix UI test --- .../main/rascal/library/demo/lang/pico/LanguageServer.rsc | 3 ++- rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/rascal-lsp/src/main/rascal/library/demo/lang/pico/LanguageServer.rsc b/rascal-lsp/src/main/rascal/library/demo/lang/pico/LanguageServer.rsc index efc855859..505c98ba3 100644 --- a/rascal-lsp/src/main/rascal/library/demo/lang/pico/LanguageServer.rsc +++ b/rascal-lsp/src/main/rascal/library/demo/lang/pico/LanguageServer.rsc @@ -197,8 +197,9 @@ value picoExecutionService(removeDecl(start[Program] program, IdType toBeRemoved } @synopsis{Command handler to show an info message} -void picoExecutionService(showInfoMessage(start[Program] program)) { +value picoExecutionService(showInfoMessage(start[Program] program)) { showMessage(info("Info message", program.src)); + return ("result": true); } @synopsis{Prepares the rename service by checking if the id can be renamed} diff --git a/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts b/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts index 3c171e5fe..65a7da8c0 100644 --- a/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts +++ b/rascal-vscode-extension/src/test/vscode-suite/dsl.test.ts @@ -25,7 +25,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -import { VSBrowser, WebDriver, Workbench } from 'vscode-extension-tester'; +import { NotificationType, VSBrowser, WebDriver, Workbench } from 'vscode-extension-tester'; import { Delays, IDEOperations, RascalREPL, TestWorkspace, ignoreFails, printRascalOutputOnFailure, sleep } from './utils'; import * as fs from 'fs/promises'; @@ -201,10 +201,12 @@ parameterizedDescribe(function (errorRecovery: boolean) { const lens = await driver.wait(() => editor.getCodeLens("Show info message."), Delays.verySlow, "'Show info message' lens should be available"); await lens!.click(); await driver.wait(async () => { - const notifications = await new Workbench().getNotifications(); + const notificationCenter = await new Workbench().openNotificationsCenter(); + const notifications = await notificationCenter.getNotifications(NotificationType.Info); for (const notification of notifications) { const message = await notification.getMessage(); if (message.startsWith("Info message")) { + await notificationCenter.clearAllNotifications(); return true; } }