eclipse / epsilon

Epsilon is a family of Java-based scripting languages for automating common model-based software engineering tasks, such as code generation, model-to-model transformation and model validation, that work out of the box with EMF (including Xtext and Sirius), UML (including Cameo/MagicDraw), Simulink, XML and other types of models.
https://eclipse.org/epsilon
Eclipse Public License 2.0
53 stars 11 forks source link

Add support for the Debug Adapter Protocol #92

Closed agarciadom closed 1 month ago

agarciadom commented 1 month ago

This PR adds support for debugging Epsilon scripts over the network, through the Debug Adapter Protocol. It has been tested with LSP4E and the debug-adapter branch of my fork of the Epsilon VS Code extension:

image

image

The PR includes a new example project with documentation, as well as automated tests for the server and the debug adapter itself. It has required some refactoring of the debugging infrastructure in Epsilon, separating the Eclipse-specific parts out to the EolDebugTarget, and moving all *Debugger classes (which are now Eclipse-agnostic) to their .engine plugins.

The PR allows for using the DAP server from the Ant tasks, and from plain Java. There is an implementation of approximate matching to allow for debugging scripts running from the classpath without requiring configuration, while looking out for ambiguous matches.

agarciadom commented 1 month ago

On second thought, the approximate matching was not a good idea. It would help with the initial setting of breakpoints, but when we actually hit one, we had no reliable way to tell the DAP client the file that should be shown to the user.

I have replaced the approximate matching with an explicit URI-to-path mapping. Before starting the debugging session, we tell the adapter which URI prefixes match to which filesystem paths (e.g. http://foo/bar maps to folder project/foo/bar). These mappings can be then used both ways:

agarciadom commented 1 month ago

The PR branch passes the tests on Jenkins:

https://ci.eclipse.org/epsilon/job/interim-kubernetes/job/debug-adapter/21/

agarciadom commented 1 month ago

In an initial walkthrough over the approach, @kolovos commented that it made more sense to always try to use the URI-to-Path mappings first, and only fall back to the module file if that didn't produce a path. I agree that it makes the user experience more consistent (instead of depending on whether we run a program which uses an Epsilon script from the classpath via Eclipse or via a JAR file), so I have made that change.

agarciadom commented 1 month ago

I have also looked a bit more into the VS Code configuration, and it turns out you can use preLaunchTask in the launch.json file to have it start the EOL script in debug mode, wait for the port to open, and then have VS Code connect to it. There is now a 1-click launch.json configuration which will both launch the script and attach to it for debugging.

A future version of the Epsilon VS Code extension could perhaps simplify this a bit, by just retrying the connection a few times before handing over control to VS Code.

agarciadom commented 1 month ago

VS Code already has the necessary functionality to wait until the session is ready to be debugged. You only need to provide a problemMatcher with the appropriate background patterns. My fork of the VS Code extension now contributes this problem matcher, and the VS Code example uses it.

I guess it would be nice if the user didn't have to set up tasks.json for it: there may be a way to have the VS Code extension automate that part.

Arkaedan commented 1 month ago

I guess it would be nice if the user didn't have to set up tasks.json for it: there may be a way to have the VS Code extension automate that part.

https://code.visualstudio.com/api/extension-guides/task-provider Definitely looks doable

agarciadom commented 1 month ago

I guess it would be nice if the user didn't have to set up tasks.json for it: there may be a way to have the VS Code extension automate that part.

https://code.visualstudio.com/api/extension-guides/task-provider Definitely looks doable

I had a look yesterday, and found that Gradle contributes its own kind of task and its own task provider, which does most of what we need and more (except for the background patterns for problem matching). This is what the tasks.json file looks like now:

{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "gradle",
            "script": "debugHello",
            "group": "other",
            "buildFile": "${workspaceFolder}/build.gradle",
            "workspaceFolder": "${workspaceFolder}",
            "projectFolder": "${workspaceFolder}",
            "args": "--info",
            "problemMatcher": "$epsilon-debug",
            "label": "epsilonDebug: hello",
            "isBackground": true
        }
    ]
}

Writing the task provider would not be trivial, as we'd have to find out which Gradle tasks are available. It looks like the vscode-gradle extension communicates with the Gradle daemon to do this, and I think it'd be best to avoid complicating the VS Code Epsilon extension too much. It may be best to just explain how to use the Gradle VS Code extension to automate the initial creation of the task, and what to tweak to turn that initial fragment into something suitable for debugging an Epsilon script.

We could also provide the above snippet, and tell users how to tweak the label and script keys to suit their Gradle buildfile.

agarciadom commented 1 month ago

I've added the ability to inspect collections (per-element for small ones, and per-slice for large ones) and the properties of model elements. I have also tweaked EclipseHost so it will start the DAP server if both debug and debugPort are set: it won't automatically connect to it yet because that would introduce a dependency from Epsilon to LSP4E. Users would have to separately install LSP4E and run an attach LSP4E launch configuration.

I think this is ready for general use. In terms of future improvements, we could do things like showing the type of the variables in addition to their name and value, or perhaps supporting launch requests, but those bits feel outside the scope of this first PR.