diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/BaseWorkspaceService.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/BaseWorkspaceService.java index f9b9e3fa9..72a697d59 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/BaseWorkspaceService.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/BaseWorkspaceService.java @@ -31,6 +31,10 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import com.google.gson.JsonPrimitive; + +import io.usethesource.vallang.IMap; +import io.usethesource.vallang.IValue; + import org.checkerframework.checker.nullness.qual.Nullable; import org.eclipse.lsp4j.ClientCapabilities; import org.eclipse.lsp4j.DidChangeConfigurationParams; @@ -44,6 +48,7 @@ import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageClientAware; import org.eclipse.lsp4j.services.WorkspaceService; +import org.rascalmpl.values.IRascalValueFactory; public class BaseWorkspaceService implements WorkspaceService, LanguageClientAware { public static final String RASCAL_LANGUAGE = "Rascal"; @@ -111,15 +116,36 @@ public void didChangeWorkspaceFolders(DidChangeWorkspaceFoldersParams params) { @Override public CompletableFuture executeCommand(ExecuteCommandParams params) { if (params.getCommand().startsWith(RASCAL_META_COMMAND) || params.getCommand().startsWith(RASCAL_COMMAND)) { - String languageName = ((JsonPrimitive) params.getArguments().get(0)).getAsString(); - String command = ((JsonPrimitive) params.getArguments().get(1)).getAsString(); - return documentService.executeCommand(languageName, command).thenApply(v -> v); - } + var languageName = ((JsonPrimitive) params.getArguments().get(0)).getAsString(); + var command = ((JsonPrimitive) params.getArguments().get(1)).getAsString(); - return CompletableFuture.supplyAsync(() -> params.getCommand() + " was ignored."); + return documentService.executeCommand(languageName, command).thenApply(v -> commandResultMap(v)); + } + else { + return CompletableFuture.supplyAsync(() -> commandResultMap(IRascalValueFactory.getInstance().bool(false))); + } } + /** + * Command results are wrapped in an IMap such that on the client side of the LSP they end up + * as a JSON object `{'result' : }`, and `` can remain to be any + * value including primitive values like integers and strings. + */ + private static IMap commandResultMap(IValue result) { + var vf = IRascalValueFactory.getInstance(); + + /* Check if the result is already wrapped in a singleton map `("result":x)` + * We do this for backward compatibility with the previous semantics where + * the wrapping had to be done manually in Rascal code to avoid failures in + * the JSON bridge w.r.t. to values of primitive types. + */ + if (result.getType().isMap()) { + IMap m = (IMap) result; + if (m.size() == 1 && m.iterator().next().equals(vf.string("result"))) { + return m; + } + } - - + return vf.map().put(vf.string("result"), result); + } } diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java index 5e5271277..27e724e13 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/rascal/RascalLanguageServices.java @@ -278,15 +278,13 @@ public CompletableFuture parseCodeActions(String command) { public InterruptibleFuture executeCommand(String command) { logger.debug("executeCommand({}...) (full command value in TRACE level)", () -> command.substring(0, Math.min(10, command.length()))); logger.trace("Full command: {}", command); - var defaultMap = VF.mapWriter(); - defaultMap.put(VF.string("result"), VF.bool(false)); return InterruptibleFuture.flatten(parseCommand(command).thenApply(cons -> EvaluatorUtil.runEvaluator( "executeCommand", semanticEvaluator, ev -> ev.call("evaluateRascalCommand", cons), - defaultMap.done(), + VF.bool(false), exec, true, client diff --git a/rascal-lsp/src/main/rascal/demo/lang/pico/LanguageServer.rsc b/rascal-lsp/src/main/rascal/demo/lang/pico/LanguageServer.rsc index 4cfd4fb20..951e86171 100644 --- a/rascal-lsp/src/main/rascal/demo/lang/pico/LanguageServer.rsc +++ b/rascal-lsp/src/main/rascal/demo/lang/pico/LanguageServer.rsc @@ -168,13 +168,13 @@ list[DocumentEdit] getAtoBEdits(start[Program] input) @synopsis{Command handler for the renameAtoB command} value picoExecutionService(renameAtoB(start[Program] input)) { applyDocumentsEdits(getAtoBEdits(input)); - return ("result": true); + return true; } @synopsis{Command handler for the removeDecl command} value picoExecutionService(removeDecl(start[Program] program, IdType toBeRemoved)) { applyDocumentsEdits([changed(program@\loc.top, [replace(toBeRemoved@\loc, "")])]); - return ("result": true); + return true; } @synopsis{The main function registers the Pico language with the IDE} diff --git a/rascal-lsp/src/main/rascal/demo/lang/pico/OldStyleLanguageServer.rsc b/rascal-lsp/src/main/rascal/demo/lang/pico/OldStyleLanguageServer.rsc index e1a58a979..59e902965 100644 --- a/rascal-lsp/src/main/rascal/demo/lang/pico/OldStyleLanguageServer.rsc +++ b/rascal-lsp/src/main/rascal/demo/lang/pico/OldStyleLanguageServer.rsc @@ -151,13 +151,13 @@ list[DocumentEdit] getAtoBEdits(start[Program] input) @synopsis{Command handler for the renameAtoB command} value picoCommands(renameAtoB(start[Program] input)) { applyDocumentsEdits(getAtoBEdits(input)); - return ("result": true); + return true; } @synopsis{Command handler for the removeDecl command} value picoCommands(removeDecl(start[Program] program, IdType toBeRemoved)) { applyDocumentsEdits([changed(program@\loc.top, [replace(toBeRemoved@\loc, "")])]); - return ("result": true); + return true; } @synopsis{The main function registers the Pico language with the IDE} diff --git a/rascal-lsp/src/main/rascal/lang/rascal/lsp/Actions.rsc b/rascal-lsp/src/main/rascal/lang/rascal/lsp/Actions.rsc index 0c07d6d8f..057ccdf71 100644 --- a/rascal-lsp/src/main/rascal/lang/rascal/lsp/Actions.rsc +++ b/rascal-lsp/src/main/rascal/lang/rascal/lsp/Actions.rsc @@ -131,7 +131,7 @@ default value evaluateRascalCommand(Command _) = ("result" : false); value evaluateRascalCommand(visualImportGraphCommand(PathConfig pcfg)) { importGraph(pcfg); - return ("result" : true); + return true; } value evaluateRascalCommand(sortImportsAndExtends(Header h)) { @@ -152,5 +152,5 @@ value evaluateRascalCommand(sortImportsAndExtends(Header h)) { '<}>"[..-2]; applyDocumentsEdits([changed(h@\loc.top, [replace(h.imports@\loc, newHeader)])]); - return ("result":true); + return true; }