mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 10:42:05 +03:00
Execute and debug .enso
files with bin/enso
in VSCode (#8923)
Let's _untie_ the [VSCode Enso extension](https://marketplace.visualstudio.com/items?itemName=Enso.enso4vscode) from `sbt` commands. Let's **open any Enso file in the editor** and then use _F5_ or _Ctrl-F5_ to execute it. Let the user choose which `bin/enso` script to use for execution completely skipping the need for `sbt`.
This commit is contained in:
parent
b00dc9e9c0
commit
ca9125f8e9
4
.github/workflows/enso4igv.yml
vendored
4
.github/workflows/enso4igv.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
java: ["8"]
|
||||
java: ["11"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@ -30,7 +30,7 @@ jobs:
|
||||
# Why do we subtract a number? Read versioning policy!
|
||||
# https://github.com/enso-org/enso/pull/7861#discussion_r1333133490
|
||||
echo "POM_VERSION=`mvn -q -DforceStdout help:evaluate -Dexpression=project.version | cut -f1 -d -`" >> "$GITHUB_ENV"
|
||||
echo "MICRO_VERSION=`expr $GITHUB_RUN_NUMBER - 2100`" >> "$GITHUB_ENV"
|
||||
echo "MICRO_VERSION=`expr $GITHUB_RUN_NUMBER - 2250`" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Update project version
|
||||
working-directory: tools/enso4igv
|
||||
|
5
.vscode/launch.json
vendored
5
.vscode/launch.json
vendored
@ -1,6 +1,11 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "java+",
|
||||
"request": "launch",
|
||||
"name": "Launch Enso File"
|
||||
},
|
||||
{
|
||||
"name": "Listen to 5005",
|
||||
"type": "java+",
|
||||
|
@ -1040,6 +1040,7 @@
|
||||
- [DataflowError.withoutTrace doesn't store stacktrace][8608]
|
||||
- [Derive --in-project from --run source location][8775]
|
||||
- [Binary operator resolution based on that value][8779]
|
||||
- [Execute and debug individual Enso files in VSCode extension][8923]
|
||||
|
||||
[3227]: https://github.com/enso-org/enso/pull/3227
|
||||
[3248]: https://github.com/enso-org/enso/pull/3248
|
||||
@ -1195,6 +1196,7 @@
|
||||
[8608]: https://github.com/enso-org/enso/pull/8608
|
||||
[8775]: https://github.com/enso-org/enso/pull/8775
|
||||
[8779]: https://github.com/enso-org/enso/pull/8779
|
||||
[8923]: https://github.com/enso-org/enso/pull/8923
|
||||
|
||||
# Enso 2.0.0-alpha.18 (2021-10-12)
|
||||
|
||||
|
@ -22,9 +22,26 @@ following two extensions in the system:
|
||||
|
||||
![Installed VSCode extensions](https://user-images.githubusercontent.com/26887752/274904239-ae1ad4cc-e2ec-4c5b-bca0-c7d7189c6885.png)
|
||||
|
||||
## Using & Debugging
|
||||
## Debugging a Single Enso File
|
||||
|
||||
Once installation is over let's continue with choosing _File/Open Folder..._ and
|
||||
Open any `.enso` files. Click left editor gutter to place breakpoints. Then
|
||||
choose _Run/Start Debugging_. If asked, choose _debug with_ **Java+** (Enso is
|
||||
Java virtual machine friendly). A prompt appears asking for path to `bin/enso`
|
||||
binary:
|
||||
|
||||
![Select enso executable](https://github.com/enso-org/enso/assets/26887752/4e1d0666-634d-4fb8-bf61-6dbf765311e8)
|
||||
|
||||
Locate `bin/enso` executable in the Enso engine download. If binding from source
|
||||
code, the executable is located at root of
|
||||
[Enso repository](https://github.com/enso-org/enso/) in
|
||||
`./built-distribution/enso-engine-*/enso-*/bin/enso`. The `.enso` file gets
|
||||
executed and output is printed in the area below editor:
|
||||
|
||||
![Executed](https://github.com/enso-org/enso/assets/26887752/2165a04f-bc0a-4b62-9ad7-e74e354e6937)
|
||||
|
||||
## Workspace Debugging
|
||||
|
||||
To work with all Enso code base continue with choosing _File/Open Folder..._ and
|
||||
opening root of [Enso Git Repository](http://github.com/enso-org/enso)
|
||||
(presumably already built with
|
||||
[sbt buildEngineDistribution](https://github.com/enso-org/enso/blob/develop/docs/CONTRIBUTING.md#running-enso)).
|
||||
@ -43,6 +60,17 @@ place one on line 120:
|
||||
|
||||
![Breakpoint](https://github.com/enso-org/enso/assets/26887752/b6ae4725-49ef-439f-b900-3e08724e3748)
|
||||
|
||||
To debug the `test/Base_Tests/src/Data/Vector_Spec.enso` file with the root of
|
||||
Enso repository opened in the VSCode workspace, choose preconfigured _Launch
|
||||
Enso File_ debug configuration before _Run/Start Debugging_.:
|
||||
|
||||
![Launch Enso File in a Project](https://github.com/enso-org/enso/assets/26887752/3680aab2-bf99-41d2-ada7-491d6040f8d2)
|
||||
|
||||
The rest of the workflow remains the same as in case of individual (without any
|
||||
project )`.enso` file case.
|
||||
|
||||
## Attach Debugger to a Process
|
||||
|
||||
Let's do a bit of debugging. Select _"Listen to 5005"_ debug configuration:
|
||||
|
||||
![Listen to 5005](https://github.com/enso-org/enso/assets/26887752/1874bcb1-cf8b-4df4-92d8-e7fb57e1b17a)
|
||||
|
@ -23,6 +23,7 @@
|
||||
},
|
||||
"categories": [
|
||||
"Programming Languages",
|
||||
"Debuggers",
|
||||
"Other"
|
||||
],
|
||||
"keywords": [
|
||||
@ -31,7 +32,10 @@
|
||||
"truffle"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onLanguage:enso"
|
||||
"workspaceContains:**/*.java",
|
||||
"onLanguage:enso",
|
||||
"onDebug",
|
||||
"onDebugDynamicConfigurations"
|
||||
],
|
||||
"main": "./dist/extension",
|
||||
"contributes": {
|
||||
@ -66,7 +70,33 @@
|
||||
"language": "enso"
|
||||
}
|
||||
],
|
||||
"debuggers": []
|
||||
"debuggers": [
|
||||
{
|
||||
"type": "java+",
|
||||
"runtime": "node",
|
||||
"languages": [
|
||||
"enso"
|
||||
],
|
||||
"initialConfigurations": [
|
||||
{
|
||||
"type": "java+",
|
||||
"request": "launch",
|
||||
"name": "Launch Enso File"
|
||||
}
|
||||
],
|
||||
"configurationSnippets": [
|
||||
{
|
||||
"label": "Enso Launch",
|
||||
"description": "Debugging Enso Programs.",
|
||||
"body": {
|
||||
"type": "java+",
|
||||
"request": "launch",
|
||||
"name": "Launch Enso File"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"vsix": "vsce package",
|
||||
|
@ -5,7 +5,7 @@
|
||||
<artifactId>enso4igv</artifactId>
|
||||
<packaging>nbm</packaging>
|
||||
<name>Enso Language Support for NetBeans & Ideal Graph Visualizer</name>
|
||||
<version>1.21-SNAPSHOT</version>
|
||||
<version>1.33-SNAPSHOT</version>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@ -72,6 +72,7 @@
|
||||
<target>1.8</target>
|
||||
<compilerArgs>
|
||||
<arg>-Xlint:deprecation</arg>
|
||||
<arg>-XDignore.symbol.file</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
@ -128,6 +129,11 @@
|
||||
<artifactId>org-openide-util</artifactId>
|
||||
<version>${netbeans.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.netbeans.api</groupId>
|
||||
<artifactId>org-openide-modules</artifactId>
|
||||
<version>${netbeans.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.netbeans.api</groupId>
|
||||
<artifactId>org-openide-util-lookup</artifactId>
|
||||
@ -225,10 +231,28 @@
|
||||
<scope>test</scope>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.netbeans.api</groupId>
|
||||
<artifactId>org-netbeans-modules-extexecution-base</artifactId>
|
||||
<version>${netbeans.version}</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.netbeans.api</groupId>
|
||||
<artifactId>org-netbeans-modules-extexecution</artifactId>
|
||||
<version>${netbeans.version}</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.netbeans.api</groupId>
|
||||
<artifactId>org-openide-io</artifactId>
|
||||
<version>${netbeans.version}</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<netbeans.version>RELEASE113</netbeans.version>
|
||||
<netbeans.version>RELEASE140</netbeans.version>
|
||||
<netbeans.compile.on.save>none</netbeans.compile.on.save>
|
||||
</properties>
|
||||
<profiles>
|
||||
|
@ -0,0 +1,203 @@
|
||||
package org.enso.tools.enso4igv;
|
||||
|
||||
import com.sun.jdi.connect.Connector;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.Future;
|
||||
import org.netbeans.api.debugger.jpda.JPDADebugger;
|
||||
import org.netbeans.api.debugger.jpda.ListeningDICookie;
|
||||
import org.netbeans.api.extexecution.ExecutionDescriptor;
|
||||
import org.netbeans.api.extexecution.ExecutionService;
|
||||
import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
|
||||
import org.netbeans.spi.project.ActionProvider;
|
||||
import org.openide.DialogDisplayer;
|
||||
import org.openide.NotifyDescriptor;
|
||||
import org.openide.filesystems.FileObject;
|
||||
import org.openide.util.Lookup;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
|
||||
import org.netbeans.api.extexecution.base.ProcessBuilder;
|
||||
import org.netbeans.api.java.classpath.ClassPath;
|
||||
import org.netbeans.api.java.platform.JavaPlatform;
|
||||
import org.netbeans.spi.project.ActionProgress;
|
||||
import org.openide.filesystems.FileUtil;
|
||||
import org.openide.modules.Modules;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbPreferences;
|
||||
import org.openide.util.RequestProcessor;
|
||||
import org.openide.windows.IOProvider;
|
||||
|
||||
@NbBundle.Messages({
|
||||
"CTL_EnsoWhere=Enso Executable Location",
|
||||
"CTL_EnsoExecutable=enso/enso.bat",
|
||||
"# {0} - executable file",
|
||||
"MSG_CannotExecute=Cannot execute {0}",
|
||||
"# {0} - executable file",
|
||||
"# {1} - exit code",
|
||||
"MSG_ExecutionError=Process {0} finished with exit code {1}"
|
||||
})
|
||||
@ServiceProvider(service = ActionProvider.class)
|
||||
public final class EnsoActionProvider implements ActionProvider {
|
||||
|
||||
@Override
|
||||
public String[] getSupportedActions() {
|
||||
return new String[]{ActionProvider.COMMAND_RUN_SINGLE, ActionProvider.COMMAND_DEBUG_SINGLE};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeAction(String action, Lookup lkp) throws IllegalArgumentException {
|
||||
var process = ActionProgress.start(lkp);
|
||||
var params = ExplicitProcessParameters.buildExplicitParameters(lkp);
|
||||
var fo = lkp.lookup(FileObject.class);
|
||||
var script = FileUtil.toFile(fo);
|
||||
|
||||
var io = IOProvider.getDefault().getIO(script.getName(), false);
|
||||
var dd = DialogDisplayer.getDefault();
|
||||
|
||||
var prefs = NbPreferences.forModule(EnsoActionProvider.class);
|
||||
var exeKey = "enso.executable";
|
||||
|
||||
var exe = prefs.get(exeKey, "");
|
||||
var nd = new NotifyDescriptor.InputLine(Bundle.CTL_EnsoExecutable(), Bundle.CTL_EnsoWhere());
|
||||
nd.setInputText(exe);
|
||||
|
||||
var builderFuture = dd.notifyFuture(nd).thenApply(exec -> {
|
||||
var file = new File(exec.getInputText());
|
||||
if (file.canExecute()) {
|
||||
prefs.put(exeKey, file.getPath());
|
||||
|
||||
var platform = JavaPlatform.getDefault();
|
||||
var isGraalVM = platform.findTool("native-image") != null;
|
||||
var java = platform.findTool("java");
|
||||
|
||||
var b = ProcessBuilder.getLocal();
|
||||
b.setExecutable(file.getPath());
|
||||
b.setArguments(prepareArguments(script, isGraalVM));
|
||||
b.setWorkingDirectory(script.getParent());
|
||||
b.setRedirectErrorStream(true);
|
||||
|
||||
var env = b.getEnvironment();
|
||||
var path = env.getVariable("PATH");
|
||||
if (path != null && java != null) {
|
||||
var javaBinDir = FileUtil.toFile(java.getParent());
|
||||
if (javaBinDir != null) {
|
||||
var newPath = path + File.pathSeparator + javaBinDir;
|
||||
env.setVariable("PATH", newPath);
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
var msg = Bundle.MSG_CannotExecute(file.getPath());
|
||||
throw new IllegalArgumentException(msg);
|
||||
});
|
||||
|
||||
var waitForProcessFuture = builderFuture.thenCompose((builder) -> {
|
||||
var cf = new CompletableFuture<Integer>();
|
||||
var descriptor = new ExecutionDescriptor()
|
||||
.frontWindow(true).controllable(true)
|
||||
.inputOutput(io)
|
||||
.postExecution((exitCode) -> {
|
||||
cf.complete(exitCode);
|
||||
});
|
||||
var launch = ActionProvider.COMMAND_DEBUG_SINGLE.equals(action) ?
|
||||
new DebugAndLaunch(fo, builder, params) : builder;
|
||||
var service = ExecutionService.newService(launch, descriptor, script.getName());
|
||||
service.run();
|
||||
return cf;
|
||||
});
|
||||
|
||||
waitForProcessFuture.thenAcceptBoth(builderFuture, (exitCode, builder) -> {
|
||||
if (exitCode != 0) {
|
||||
var msg = Bundle.MSG_ExecutionError(builder.getDescription(), exitCode);
|
||||
var md = new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE);
|
||||
dd.notifyLater(md);
|
||||
}
|
||||
process.finished(exitCode == 0);
|
||||
}).exceptionally((ex) -> {
|
||||
process.finished(false);
|
||||
if (ex instanceof CompletionException && ex.getCause() instanceof CancellationException) {
|
||||
return null;
|
||||
}
|
||||
dd.notifyLater(new NotifyDescriptor.Message(ex.getMessage(), NotifyDescriptor.ERROR_MESSAGE));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private static List<String> prepareArguments(File script, boolean isGraalVM) {
|
||||
var list = new ArrayList<String>();
|
||||
list.add("--run");
|
||||
list.add(script.getPath());
|
||||
var isIGV = Modules.getDefault().findCodeNameBase("org.graalvm.visualizer.connection") != null;
|
||||
if (isGraalVM && isIGV) {
|
||||
list.add("--dump-graphs");
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActionEnabled(String string, Lookup lkp) throws IllegalArgumentException {
|
||||
if (lkp.lookup(EnsoDataObject.class) != null) {
|
||||
return true;
|
||||
} else {
|
||||
var fo = lkp.lookup(FileObject.class);
|
||||
return fo != null && fo.getLookup().lookup(EnsoDataObject.class) != null;
|
||||
}
|
||||
}
|
||||
|
||||
static final class DebugAndLaunch implements Callable<Process> {
|
||||
private static final RequestProcessor RP = new RequestProcessor(DebugAndLaunch.class);
|
||||
private final FileObject script;
|
||||
private final ProcessBuilder builder;
|
||||
private final ExplicitProcessParameters params;
|
||||
private final Future<String> computeAddress;
|
||||
|
||||
DebugAndLaunch(FileObject script, ProcessBuilder builder, ExplicitProcessParameters params) {
|
||||
this.script = script;
|
||||
this.builder = builder;
|
||||
this.computeAddress = RP.submit(this::initAddress);
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Process call() throws Exception {
|
||||
var port = computeAddress.get();
|
||||
builder.getEnvironment().setVariable("JAVA_OPTS", "-agentlib:jdwp=transport=dt_socket,address=" + port);
|
||||
return builder.call();
|
||||
}
|
||||
|
||||
private String initAddress() throws Exception {
|
||||
var lc = ListeningDICookie.create(-1);
|
||||
var connector = lc.getListeningConnector();
|
||||
|
||||
var args = lc.getArgs();
|
||||
var address = connector.startListening(args);
|
||||
|
||||
var properties = new HashMap<>();
|
||||
{
|
||||
var sourcePath = ClassPath.getClassPath(script, ClassPath.SOURCE);
|
||||
properties.put("sourcepath", sourcePath);
|
||||
properties.put("baseDir", FileUtil.toFile(script.getParent()));
|
||||
properties.put("name", script.getName());
|
||||
}
|
||||
|
||||
var services = new Object[] { properties };
|
||||
int port = Integer.parseInt(address.substring(address.indexOf(':') + 1));
|
||||
Connector.IntegerArgument portArg = (Connector.IntegerArgument) args.get("port");
|
||||
portArg.setValue(port);
|
||||
|
||||
RP.submit(() -> {
|
||||
JPDADebugger.startListening(connector, args, services);
|
||||
return null;
|
||||
});
|
||||
|
||||
return Integer.toString(port);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user