diff --git a/rascal-lsp/pom.xml b/rascal-lsp/pom.xml index 08450614..2c6a83f1 100644 --- a/rascal-lsp/pom.xml +++ b/rascal-lsp/pom.xml @@ -65,7 +65,7 @@ org.rascalmpl rascal - 0.40.17 + 0.41.0-RC10 org.rascalmpl diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ParserOnlyContribution.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ParserOnlyContribution.java index 708b8d4f..73fe5fa4 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ParserOnlyContribution.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/parametric/ParserOnlyContribution.java @@ -27,8 +27,9 @@ package org.rascalmpl.vscode.lsp.parametric; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.Writer; import java.util.concurrent.CompletableFuture; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -88,7 +89,7 @@ public CompletableFuture parsing(ISourceLocation loc, String input) { private static Either loadParser(ParserSpecification spec) { // the next two object are scaffolding. we only need them temporarily, and they will not be used by the returned IFunction if the (internal) _call_ methods are not used from ICallableValue. GlobalEnvironment unusedHeap = new GlobalEnvironment(); - Evaluator unusedEvaluator = new Evaluator(VF, InputStream.nullInputStream(), OutputStream.nullOutputStream(), OutputStream.nullOutputStream(), new NullRascalMonitor(), new ModuleEnvironment("***unused***", unusedHeap), unusedHeap); + Evaluator unusedEvaluator = new Evaluator(VF, Reader.nullReader(), new PrintWriter(Writer.nullWriter()), new PrintWriter(Writer.nullWriter()), new NullRascalMonitor(), new ModuleEnvironment("***unused***", unusedHeap), unusedHeap); // this is what we are after: a factory that can load back parsers. IRascalValueFactory vf = new RascalFunctionValueFactory(unusedEvaluator /*can not be null unfortunately*/); IConstructor reifiedType = makeReifiedType(spec, vf); diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/terminal/LSPTerminalREPL.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/terminal/LSPTerminalREPL.java index 6b05142e..862d838f 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/terminal/LSPTerminalREPL.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/terminal/LSPTerminalREPL.java @@ -28,48 +28,47 @@ import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Reader; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.SortedSet; import java.util.concurrent.ConcurrentHashMap; import java.util.jar.Manifest; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jline.terminal.Terminal; +import org.jline.terminal.TerminalBuilder; +import org.jline.utils.OSUtils; import org.rascalmpl.debug.IRascalMonitor; import org.rascalmpl.ideservices.IDEServices; import org.rascalmpl.interpreter.Evaluator; -import org.rascalmpl.interpreter.env.GlobalEnvironment; -import org.rascalmpl.interpreter.env.ModuleEnvironment; -import org.rascalmpl.interpreter.load.StandardLibraryContributor; -import org.rascalmpl.interpreter.result.IRascalResult; -import org.rascalmpl.interpreter.result.ResultFactory; import org.rascalmpl.interpreter.utils.RascalManifest; -import org.rascalmpl.jline.Terminal; -import org.rascalmpl.jline.TerminalFactory; import org.rascalmpl.library.util.PathConfig; import org.rascalmpl.library.util.PathConfig.RascalConfigMode; +import org.rascalmpl.parser.gtd.exception.ParseError; import org.rascalmpl.repl.BaseREPL; -import org.rascalmpl.repl.ILanguageProtocol; -import org.rascalmpl.repl.RascalInterpreterREPL; -import org.rascalmpl.shell.RascalShell; +import org.rascalmpl.repl.StopREPLException; +import org.rascalmpl.repl.output.ICommandOutput; +import org.rascalmpl.repl.output.impl.AsciiStringOutputPrinter; +import org.rascalmpl.repl.rascal.RascalInterpreterREPL; +import org.rascalmpl.repl.rascal.RascalReplServices; import org.rascalmpl.shell.ShellEvaluatorFactory; import org.rascalmpl.uri.ISourceLocationWatcher.ISourceLocationChanged; import org.rascalmpl.uri.URIResolverRegistry; import org.rascalmpl.uri.URIUtil; import org.rascalmpl.uri.classloaders.SourceLocationClassLoader; -import org.rascalmpl.values.ValueFactoryFactory; import org.rascalmpl.vscode.lsp.dap.DebugSocketServer; import org.rascalmpl.vscode.lsp.uri.ProjectURIResolver; import org.rascalmpl.vscode.lsp.uri.TargetURIResolver; import org.rascalmpl.vscode.lsp.uri.jsonrpc.impl.VSCodeVFSClient; import io.usethesource.vallang.ISourceLocation; import io.usethesource.vallang.IValue; -import io.usethesource.vallang.IValueFactory; import io.usethesource.vallang.io.StandardTextWriter; /** @@ -77,197 +76,157 @@ * connects to a running LSP server instance to * provide IDE feature to the user of a terminal instance. */ -public class LSPTerminalREPL extends BaseREPL { - private static final InputStream stdin = System.in; - private static final OutputStream stderr = System.err; - private static final boolean prettyPrompt = true; - private static final boolean allowColors = true; - - public LSPTerminalREPL(Terminal terminal, IDEServices services, OutputStream stdout) throws IOException, URISyntaxException { - super(makeInterpreter(terminal, services), null, stdin, stderr, stdout, true, terminal.isAnsiSupported(), getHistoryFile(), terminal, services); +public class LSPTerminalREPL extends RascalInterpreterREPL { + private final int ideServicePort; + private final Set dirtyModules = ConcurrentHashMap.newKeySet(); + private DebugSocketServer debugServer; + + private LSPTerminalREPL(int ideServicesPort) { + this.ideServicePort = ideServicesPort; } - private static String getRascalLspVersion() { + @Override + public Map availableCommandLineOptions() { + var result = super.availableCommandLineOptions(); + result.put("debugging", "enable debugging (true/false)"); + return result; + } + + @Override + protected IDEServices buildIDEService(PrintWriter err, IRascalMonitor monitor, Terminal term) { try { - return new Manifest(URIResolverRegistry.getInstance() - .getInputStream(URIUtil.correctLocation("lib", "rascal-lsp", "META-INF/MANIFEST.MF"))) - .getMainAttributes().getValue("Specification-Version"); + return new TerminalIDEClient(ideServicePort, err, monitor, term); } catch (IOException e) { - return "Unknown"; + throw new RuntimeException("Could not build IDE service for REPL", e); } } + @Override + protected Evaluator buildEvaluator(Reader input, PrintWriter stdout, PrintWriter stderr, IDEServices services) { + var evaluator = super.buildEvaluator(input, stdout, stderr, services); + evaluator.addRascalSearchPath(URIUtil.correctLocation("lib", "rascal-lsp", "")); - private static ILanguageProtocol makeInterpreter(Terminal terminal, final IDEServices services) throws IOException, URISyntaxException { - RascalInterpreterREPL repl = - new RascalInterpreterREPL(prettyPrompt, allowColors, getHistoryFile()) { - private final Set dirtyModules = ConcurrentHashMap.newKeySet(); - private DebugSocketServer debugServer; - private final Pattern debuggingCommandPattern = Pattern.compile("^\\s*:set\\s+debugging\\s+(true|false)"); + URIResolverRegistry reg = URIResolverRegistry.getInstance(); - @Override - protected SortedSet getCommandLineOptions() { - SortedSet options = super.getCommandLineOptions(); - options.add("debugging"); - return options; - } + ISourceLocation projectDir = ShellEvaluatorFactory.inferProjectRoot(new File(System.getProperty("user.dir"))); + String projectName = "unknown-project"; + if (projectDir != null) { + projectName = new RascalManifest().getProjectName(projectDir); + } - @Override - public IRascalResult evalStatement(String statement, String lastLine) throws InterruptedException { - Matcher matcher = debuggingCommandPattern.matcher(statement); - if (matcher.find()) { - if(matcher.group(1).equals("true")){ - if(!debugServer.isClientConnected()){ - ((TerminalIDEClient) services).startDebuggingSession(debugServer.getPort()); - getOutputWriter().println("Debugging session started."); - return ResultFactory.nothing(); - } - getOutputWriter().println("Debugging session was already running."); - return ResultFactory.nothing(); - } - if(debugServer.isClientConnected()){ - debugServer.terminateDebugSession(); - getOutputWriter().println("Debugging session stopped."); - return ResultFactory.nothing(); - } - getOutputWriter().println("Debugging session was not running."); - return ResultFactory.nothing(); - } - - return super.evalStatement(statement, lastLine); - } + reg.registerLogical(new ProjectURIResolver(services::resolveProjectLocation)); + reg.registerLogical(new TargetURIResolver(services::resolveProjectLocation)); - @Override - protected Evaluator constructEvaluator(InputStream input, OutputStream stdout, OutputStream stderr, IDEServices services) { - GlobalEnvironment heap = new GlobalEnvironment(); - ModuleEnvironment root = heap.addModule(new ModuleEnvironment(ModuleEnvironment.SHELL_MODULE, heap)); - IValueFactory vf = ValueFactoryFactory.getValueFactory(); - Evaluator evaluator = new Evaluator(vf, input, stderr, stdout, services, root, heap); - evaluator.addRascalSearchPathContributor(StandardLibraryContributor.getInstance()); - evaluator.addRascalSearchPath(URIUtil.correctLocation("lib", "rascal-lsp", "")); - - URIResolverRegistry reg = URIResolverRegistry.getInstance(); - - ISourceLocation projectDir = ShellEvaluatorFactory.inferProjectRoot(new File(System.getProperty("user.dir"))); - String projectName = "unknown-project"; - if (projectDir != null) { - projectName = new RascalManifest().getProjectName(projectDir); - } - - reg.registerLogical(new ProjectURIResolver(services::resolveProjectLocation)); - reg.registerLogical(new TargetURIResolver(services::resolveProjectLocation)); - - debugServer = new DebugSocketServer(evaluator, (TerminalIDEClient) services); - - try { - PathConfig pcfg; - if (projectDir != null) { - pcfg = PathConfig.fromSourceProjectRascalManifest(projectDir, RascalConfigMode.INTERPETER); - } - else { - pcfg = new PathConfig(); - pcfg.addSourceLoc(URIUtil.rootLocation("std")); - } - - evaluator.getErrorPrinter().println("Rascal Version: " + RascalManifest.getRascalVersionNumber()); - evaluator.getErrorPrinter().println("Rascal-lsp Version: " + getRascalLspVersion()); - new StandardTextWriter(true).write(pcfg.asConstructor(), evaluator.getErrorPrinter()); - - for (IValue srcPath : pcfg.getSrcs()) { - ISourceLocation path = (ISourceLocation)srcPath; - evaluator.addRascalSearchPath(path); - reg.watch(path, true, d -> sourceLocationChanged(path, d)); - } - - ClassLoader cl = new SourceLocationClassLoader( - pcfg.getClassloaders() - .append(URIUtil.correctLocation("lib", "rascal","")) - .append(URIUtil.correctLocation("lib", "rascal-lsp","")) - .append(URIUtil.correctLocation("target", projectName, "")), - ClassLoader.getSystemClassLoader() - ); - - evaluator.addClassLoader(cl); - } - catch (IOException e) { - e.printStackTrace(new PrintStream(stderr)); - } - - // this is very important since it hooks up the languageRegistration feature - evaluator.setMonitor(services); - - return evaluator; - } + debugServer = new DebugSocketServer(evaluator, (TerminalIDEClient) services); - private void sourceLocationChanged(ISourceLocation srcPath, ISourceLocationChanged d) { - if (URIUtil.isParentOf(srcPath, d.getLocation()) && d.getLocation().getPath().endsWith(".rsc")) { - ISourceLocation relative = URIUtil.relativize(srcPath, d.getLocation()); - relative = URIUtil.removeExtension(relative); - - String modName = relative.getPath(); - if (modName.startsWith("/")) { - modName = modName.substring(1); - } - modName = modName.replace("/", "::"); - modName = modName.replace("\\", "::"); - dirtyModules.add(modName); - } - } + try { + PathConfig pcfg; + if (projectDir != null) { + pcfg = PathConfig.fromSourceProjectRascalManifest(projectDir, RascalConfigMode.INTERPETER); + } + else { + pcfg = new PathConfig(); + pcfg.addSourceLoc(URIUtil.rootLocation("std")); + } - @Override - public void handleInput(String line, Map output, Map metadata) - throws InterruptedException { - try { - Set changes = new HashSet<>(); - changes.addAll(dirtyModules); - dirtyModules.removeAll(changes); - eval.reloadModules(eval.getMonitor(), changes, URIUtil.rootLocation("reloader")); - } - catch (Throwable e) { - getErrorWriter().println("Error during reload: " + e.getMessage()); - // in which case the dirty modules are not cleared and the system will try - // again at the next command - return; - } - - super.handleInput(line, output, metadata); - - for (String mimetype : output.keySet()) { - if (!mimetype.contains("html") && !mimetype.startsWith("image/")) { - continue; - } - - services.browse( - URIUtil.assumeCorrect(metadata.get("url")), - metadata.containsKey("title") ? metadata.get("title") : metadata.get("url"), - metadata.containsKey("viewColumn") ? Integer.parseInt(metadata.get("viewColumn")) : 1 - ); - } - } - }; + stdout.println("Rascal Version: " + RascalManifest.getRascalVersionNumber()); + stdout.println("Rascal-lsp Version: " + getRascalLspVersion()); + new StandardTextWriter(true).write(pcfg.asConstructor(), stdout); - repl.setMeasureCommandTime(false); + for (IValue srcPath : pcfg.getSrcs()) { + ISourceLocation path = (ISourceLocation)srcPath; + evaluator.addRascalSearchPath(path); + reg.watch(path, true, d -> sourceLocationChanged(path, d)); + } + + ClassLoader cl = new SourceLocationClassLoader( + pcfg.getClassloaders() + .append(URIUtil.correctLocation("lib", "rascal","")) + .append(URIUtil.correctLocation("lib", "rascal-lsp","")) + .append(URIUtil.correctLocation("target", projectName, "")), + ClassLoader.getSystemClassLoader() + ); + + evaluator.addClassLoader(cl); + } + catch (IOException e) { + e.printStackTrace(stderr); + } - return repl; + return evaluator; } + private final Pattern debuggingCommandPattern = Pattern.compile("^\\s*:set\\s+debugging\\s+(true|false)"); + private ICommandOutput handleDebuggerCommand(String command) { + Matcher matcher = debuggingCommandPattern.matcher(command); + if (matcher.find()) { + if(matcher.group(1).equals("true")){ + if(!debugServer.isClientConnected()){ + ((TerminalIDEClient) services).startDebuggingSession(debugServer.getPort()); + return () -> new AsciiStringOutputPrinter("Debugging session started."); + } + return () -> new AsciiStringOutputPrinter("Debugging session was already running."); + } + if(debugServer.isClientConnected()){ + debugServer.terminateDebugSession(); + return () -> new AsciiStringOutputPrinter("Debugging session stopped."); + } + return () -> new AsciiStringOutputPrinter("Debugging session was not running."); + } + return null; + } - @SuppressWarnings("java:S899") // it's fine to ignore the result of createNewFile - private static File getHistoryFile() throws IOException { - File home = new File(System.getProperty("user.home")); - File rascal = new File(home, ".rascal"); + @Override + public ICommandOutput handleInput(String command) throws InterruptedException, ParseError, StopREPLException { + var result = handleDebuggerCommand(command); + if (result != null) { + return result; + } + Set changes = new HashSet<>(); + changes.addAll(dirtyModules); + dirtyModules.removeAll(changes); + eval.reloadModules(eval.getMonitor(), changes, URIUtil.rootLocation("reloader")); + return super.handleInput(command); + } - if (!rascal.exists()) { - rascal.mkdirs(); + private void sourceLocationChanged(ISourceLocation srcPath, ISourceLocationChanged d) { + if (URIUtil.isParentOf(srcPath, d.getLocation()) && d.getLocation().getPath().endsWith(".rsc")) { + ISourceLocation relative = URIUtil.relativize(srcPath, d.getLocation()); + relative = URIUtil.removeExtension(relative); + + String modName = relative.getPath(); + if (modName.startsWith("/")) { + modName = modName.substring(1); + } + modName = modName.replace("/", "::"); + modName = modName.replace("\\", "::"); + dirtyModules.add(modName); } + } - File historyFile = new File(rascal, ".repl-history-rascal-terminal"); - if (!historyFile.exists()) { - historyFile.createNewFile(); + private static String getRascalLspVersion() { + try { + return new Manifest(URIResolverRegistry.getInstance() + .getInputStream(URIUtil.correctLocation("lib", "rascal-lsp", "META-INF/MANIFEST.MF"))) + .getMainAttributes().getValue("Specification-Version"); + } catch (IOException e) { + return "Unknown"; } + } + - return historyFile; + + @SuppressWarnings("java:S899") // it's fine to ignore the result of createNewFile + private static Path getHistoryFile() throws IOException { + var home = Paths.get(System.getProperty("user.home")); + var rascal = home.resolve(".rascal"); + + if (!Files.exists(rascal)) { + Files.createDirectories(rascal); + } + + return rascal.resolve(".repl-history-rascal-terminal-jline3"); } @@ -275,8 +234,6 @@ private static File getHistoryFile() throws IOException { public static void main(String[] args) throws InterruptedException, IOException { int ideServicesPort = -1; int vfsPort = -1; - String loadModule = null; - boolean runModule = false; for (int i = 0; i < args.length; i++) { switch (args[i]) { @@ -286,12 +243,6 @@ public static void main(String[] args) throws InterruptedException, IOException case "--vfsPort": vfsPort = Integer.parseInt(args[++i]); break; - case "--loadModule": - loadModule = args[++i]; - break; - case "--runModule": - runModule = true; - break; } } @@ -299,28 +250,24 @@ public static void main(String[] args) throws InterruptedException, IOException throw new IllegalArgumentException("missing --ideServicesPort commandline parameter"); } - RascalShell.setupWindowsCodepage(); - RascalShell.enableWindowsAnsiEscapesIfPossible(); - if (vfsPort != -1) { VSCodeVFSClient.buildAndRegister(vfsPort); } + var terminalBuilder = TerminalBuilder.builder() + .dumb(true) // enable fallback + .system(true); + + if (OSUtils.IS_WINDOWS) { + terminalBuilder.encoding(StandardCharsets.UTF_8); + } + try { - IRascalMonitor monitor = IRascalMonitor.buildConsoleMonitor(System.in, System.out, false); - - LSPTerminalREPL terminal = - new LSPTerminalREPL(TerminalFactory.get(), new TerminalIDEClient(ideServicesPort, monitor), monitor instanceof OutputStream ? (OutputStream) monitor : System.out); - if (loadModule != null) { - terminal.queueCommand("import " + loadModule + ";"); - if (runModule) { - terminal.queueCommand("main()"); - } - } - terminal.run(); + var repl = new BaseREPL(new RascalReplServices(new LSPTerminalREPL(ideServicesPort), getHistoryFile()), terminalBuilder.build()); + repl.run(); System.exit(0); // kill the other threads } - catch (IOException | URISyntaxException e) { + catch (IOException e) { e.printStackTrace(); System.err.println("Rascal terminal terminated exceptionally; press any key to exit process."); System.in.read(); diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/terminal/TerminalIDEClient.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/terminal/TerminalIDEClient.java index 248efca4..9820b1a1 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/terminal/TerminalIDEClient.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/terminal/TerminalIDEClient.java @@ -37,6 +37,7 @@ import org.apache.logging.log4j.Logger; import org.eclipse.lsp4j.ShowDocumentParams; import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.jline.terminal.Terminal; import org.rascalmpl.debug.IRascalMonitor; import org.rascalmpl.ideservices.IDEServices; import org.rascalmpl.library.Prelude; @@ -67,8 +68,10 @@ public class TerminalIDEClient implements IDEServices { private static final Logger logger = LogManager.getLogger(TerminalIDEClient.class); private final ColumnMaps columns = new ColumnMaps(this::getContents); private final IRascalMonitor monitor; + private final Terminal terminal; + private final PrintWriter err; - public TerminalIDEClient(int port, IRascalMonitor monitor) throws IOException { + public TerminalIDEClient(int port, PrintWriter err, IRascalMonitor monitor, Terminal term) throws IOException { @SuppressWarnings({"resource"}) // we don't have to close the socket, we are passing it off to the lsp4j framework Socket socket = new Socket(InetAddress.getLoopbackAddress(), port); socket.setTcpNoDelay(true); @@ -80,13 +83,15 @@ public TerminalIDEClient(int port, IRascalMonitor monitor) throws IOException { .create(); launch.startListening(); server = launch.getRemoteProxy(); + this.err = err; this.monitor = monitor; + this.terminal = term; } + @Override public PrintWriter stderr() { - assert false: "this method should not be used"; - return new PrintWriter(System.err); + return err; } @Override @@ -213,4 +218,10 @@ public void registerDebugServerPort(int processID, int serverPort){ server.registerDebugServerPort(processID, serverPort); } + @Override + public Terminal activeTerminal() { + return terminal; + } + + } diff --git a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/util/EvaluatorUtil.java b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/util/EvaluatorUtil.java index 0567c6ef..a466793c 100644 --- a/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/util/EvaluatorUtil.java +++ b/rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/util/EvaluatorUtil.java @@ -30,6 +30,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.PrintWriter; +import java.io.Reader; import java.io.StringWriter; import java.net.URI; import java.net.URISyntaxException; @@ -237,9 +238,9 @@ public static CompletableFuture makeFutureEvaluator(ExecutorService e String jobName = "Loading " + label; try { services.jobStart(jobName, imports.length); - Evaluator eval = ShellEvaluatorFactory.getDefaultEvaluator(new ByteArrayInputStream(new byte[0]), - IoBuilder.forLogger(customLog).setLevel(Level.INFO).buildOutputStream(), - IoBuilder.forLogger(customLog).setLevel(Level.ERROR).buildOutputStream(), services); + Evaluator eval = ShellEvaluatorFactory.getDefaultEvaluator(Reader.nullReader(), + IoBuilder.forLogger(customLog).setLevel(Level.INFO).buildPrintWriter(), + IoBuilder.forLogger(customLog).setLevel(Level.ERROR).buildPrintWriter(), services); eval.getConfiguration().setRascalJavaClassPathProperty(System.getProperty("rascal.compilerClasspath")); eval.addClassLoader(RascalLanguageServer.class.getClassLoader()); diff --git a/rascal-vscode-extension/src/RascalExtension.ts b/rascal-vscode-extension/src/RascalExtension.ts index 4220f8b3..dbef56c5 100644 --- a/rascal-vscode-extension/src/RascalExtension.ts +++ b/rascal-vscode-extension/src/RascalExtension.ts @@ -86,7 +86,7 @@ export class RascalExtension implements vscode.Disposable { if (!text.document.uri || !moduleName) { return; } - this.startTerminal(text.document.uri, "--loadModule", moduleName, "--runModule"); + this.startTerminal(text.document.uri, `import ${moduleName};\nmain();\n`); }) ); } @@ -98,12 +98,12 @@ export class RascalExtension implements vscode.Disposable { if (!text.document.uri || !moduleName) { return; } - this.startTerminal(text.document.uri, "--loadModule", moduleName); + this.startTerminal(text.document.uri, `import ${moduleName};\n`); }) ); } - private async startTerminal(uri: vscode.Uri | undefined, ...extraArgs: string[]) { + private async startTerminal(uri: vscode.Uri | undefined, command?: string | undefined) { try { await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, @@ -112,7 +112,7 @@ export class RascalExtension implements vscode.Disposable { }, async (progress) => { progress.report({message: "Starting rascal-lsp"}); const rascal = await this.rascal.rascalClient; - console.log(`Starting Rascal REPL: on ${uri} and with args: ${extraArgs}`); + console.log(`Starting Rascal REPL: on ${uri} and with command: ${command}`); if (uri && !uri.path.endsWith(".rsc")) { // do not try to figure out a rascal project path when the focus is not a rascal file uri = undefined; @@ -134,12 +134,15 @@ export class RascalExtension implements vscode.Disposable { const terminal = vscode.window.createTerminal({ iconPath: this.icon, shellPath: await getJavaExecutable(), - shellArgs: this.buildShellArgs(compilationPath, serverConfig, ...extraArgs), + shellArgs: this.buildShellArgs(compilationPath, serverConfig), isTransient: false, // right now we don't support transient terminals yet - name: `Rascal terminal (${this.getTerminalOrigin(uri, extraArgs)})`, + name: `Rascal terminal (${this.getTerminalOrigin(uri, command??"")})`, }); terminal.show(false); + if (command) { + terminal.sendText(command); + } progress.report({increment: 25, message: "Finished creating terminal"}); }); } catch (err) { @@ -147,7 +150,7 @@ export class RascalExtension implements vscode.Disposable { } } - private getTerminalOrigin(uri: vscode.Uri | undefined, extraArgs: string[]): string { + private getTerminalOrigin(uri: vscode.Uri | undefined, startCommand: string): string { if (uri) { const config = vscode.workspace.getConfiguration(); const originFormat = config.get('rascal.terminal.name.originFormat'); @@ -160,18 +163,19 @@ export class RascalExtension implements vscode.Disposable { return "no project"; } case 'Module (qualified)': { - if (extraArgs[0] === '--loadModule' && - extraArgs[1] && extraArgs[1].match(this.qualifiedName)) { - return extraArgs[1]; + const name = startCommand.match(this.qualifiedName); + if (name && name[1]) { + if (name[0] !== '') { + return name[0] + "::" + name[1]; + } + return name[1]; } return "no module"; } case 'Module (unqualified)': { - if (extraArgs[0] === '--loadModule' && extraArgs[1]) { - const name = extraArgs[1].match(this.qualifiedName); - if (name && name[1]) { - return name[1]; - } + const name = startCommand.match(this.qualifiedName); + if (name && name[1]) { + return name[1]; } return "no module"; } @@ -187,7 +191,7 @@ export class RascalExtension implements vscode.Disposable { const name2 = '(?:\\\\[A-Z_a-z][\\-0-9A-Z_a-z]*)'; const name = `(?:${name1}|${name2})`; const qualifiedName = `(?:(?:${name}::)*(${name}))`; - return new RegExp(`^${qualifiedName}$`); + return new RegExp(`^import ${qualifiedName};`); })(); // Build the regex only once private async reportTerminalStartError(msg: string, detail: string = "", config : {modal?: boolean, showOutput?: boolean, canContinue?: boolean}) : Promise {