Forráskód Böngészése

feat: Debug support

Matthias Ladkau 5 éve
szülő
commit
a1c29a0a08

+ 4 - 0
.gitignore

@@ -8,3 +8,7 @@ ecal
 /dist
 /build
 /stdlib/stdlib_gen.go
+/ecal-support/node_modules
+/ecal-support/out
+/ecal-support/*.vsix
+/ecal-support/package-lock.json

+ 1 - 1
ecal-support/.vscode/launch.json

@@ -1,6 +1,6 @@
 // A launch configuration that launches the extension inside a new window
 {
-	"version": "0.2.0",
+    "version": "0.2.0",
     "configurations": [
         {
             "name": "Extension",

+ 38 - 1
ecal-support/README.md

@@ -6,6 +6,43 @@
 * `syntaxes/ecal.tmLanguage.json` - Text mate grammar file
 * `language-configuration.json` - language configuration for VSCode
 
+## Build the extention
+
+To build the extention you need `npm` installed.
+
+VSIX file can be build with `npm run package`
+
 ## Install the extension
 
-To start using your extension with Visual Studio Code copy it into the `<user home>/.vscode/extensions` folder and restart Code.
+The extention can be installed using a precompiled VSIX file which can be downloaded from here:
+
+https://devt.de/krotik/ecal/releases
+
+## Launch config for ecal projects
+
+```
+{
+	"version": "0.2.0",
+	"configurations": [
+		{
+			"type": "ecaldebug",
+			"request": "launch",
+			"name": "Debug ECAL script with ECAL Debug Server",
+
+			"serverURL": "localhost:43806",
+            "dir": "${workspaceFolder}",
+			"executeOnEntry": true,
+			"trace": false,
+		}
+	]
+}
+```
+
+- serverURL: URL of the ECAL debug server.
+- dir: Root directory for ECAL debug server.
+- executeOnEntry: (optional) Execute the ECAL script on entry. If this is set to false then code needs to be manually started from the ECAL debug server console.
+- trace: (optional) Enable tracing messages for debug adapter (useful when debugging the debugger).
+
+## Developing the extension
+
+In VSCode the extention can be launched and debugged using the included launch configuration. Press F5 to start a VS Code instance with ECAL support extention form the development code.

+ 24 - 0
ecal-support/notes.txt

@@ -0,0 +1,24 @@
+debug extension - how to declare it - must have a bin file
+https://code.visualstudio.com/api/extension-guides/debugger-extension
+
+https://github.com/Microsoft/vscode-mock-debug/blob/d33d6057c2d7d7f0495e26d6fa8844c8336b2408/src/extension.ts#L79
+
+
+https://microsoft.github.io/debug-adapter-protocol/overview
+
+https://github.com/google/go-dap
+
+https://github.com/golang/vscode-go/blob/master/package.json
+
+
+Firefox vscode extension
+https://github.com/firefox-devtools/vscode-firefox-debug
+
+
+Blog:
+https://code.visualstudio.com/blogs/2018/07/12/introducing-logpoints-and-auto-attach
+
+
+BUG:
+https://github.com/Microsoft/vscode/issues/63896
+

+ 105 - 13
ecal-support/package.json

@@ -1,25 +1,117 @@
 {
     "name": "ecal-support",
     "displayName": "ECAL Support",
-    "description": "Support for the Event Condition Action Language (ECAL).",
-    "version": "0.0.1",
+    "version": "0.9.1",
+    "publisher": "krotik",
+    "description": "Extension to support the development of ECAL scripts in VS Code.",
+    "author": {
+        "name": "Matthias Ladkau",
+        "email": "github@ladkau.de"
+    },
+    "license": "MIT",
     "engines": {
         "vscode": "^1.50.0"
     },
+    "icon": "images/logo.png",
     "categories": [
         "Programming Languages"
     ],
+    "repository": {
+        "type": "git",
+        "url": "https://devt.de/krotik/ecal.git"
+    },
+    "scripts": {
+        "compile": "tsc",
+        "watch": "tsc -w",
+        "package": "vsce package"
+    },
+    "dependencies": {
+        "@jpwilliams/waitgroup": "1.0.1",
+        "vscode-debugadapter": "^1.42.1"
+    },
+    "devDependencies": {
+        "@types/vscode": "^1.50.0",
+        "@types/node": "^14.14.2",
+        "vsce": "^1.81.1",
+        "typescript": "^4.0.3"
+    },
+    "main": "./out/extension.js",
+    "activationEvents": [
+        "onDebug"
+    ],
     "contributes": {
-        "languages": [{
-            "id": "ecal",
-            "aliases": ["Event Condition Action Language", "ecal"],
-            "extensions": [".ecal"],
-            "configuration": "./language-configuration.json"
-        }],
-        "grammars": [{
-            "language": "ecal",
-            "scopeName": "source.ecal",
-            "path": "./syntaxes/ecal.tmLanguage.json"
-        }]
+        "languages": [
+            {
+                "id": "ecal",
+                "aliases": [
+                    "Event Condition Action Language",
+                    "ecal"
+                ],
+                "extensions": [
+                    ".ecal"
+                ],
+                "configuration": "./language-configuration.json"
+            }
+        ],
+        "grammars": [
+            {
+                "language": "ecal",
+                "scopeName": "source.ecal",
+                "path": "./syntaxes/ecal.tmLanguage.json"
+            }
+        ],
+        "breakpoints": [
+            {
+                "language": "ecal"
+            }
+        ],
+        "debuggers": [
+            {
+                "type": "ecaldebug",
+                "label": "ECAL Debug",
+                "program": "./out/ecalDebugAdapter.js",
+                "runtime": "node",
+                "configurationAttributes": {
+                    "launch": {
+                      "required": [
+                        "serverURL", "dir"
+                      ],
+                      "properties": {
+                        "serverURL": {
+                            "type": "string",
+                            "description": "URL of the ECAL debug server.",
+                            "default": "localhost:43806"
+                        },
+                        "dir": {
+                            "type": "string",
+                            "description": "Root directory for ECAL debug server.",
+                            "default": "${workspaceFolder}"
+                        },
+                        "executeOnEntry": {
+                            "type": "boolean",
+                            "description": "Execute the ECAL script on entry. If this is set to false then code needs to be manually started from the ECAL debug server console.",
+                            "default": true
+                        },
+                        "trace": {
+                            "type": "boolean",
+                            "description": "Enable logging of the Debug Adapter Protocol.",
+                            "default": false
+                        }
+                      }
+                    }
+                  },
+                  "initialConfigurations": [
+                    {
+                      "type": "ecaldebug",
+                      "request": "launch",
+                      "name": "Debug ECAL script with ECAL Debug Server",
+                      "serverURL": "localhost:43806",
+                      "dir": "${workspaceFolder}",
+                      "executeOnEntry": true,
+                      "trace": false
+                    }
+                  ]
+            }
+        ]
     }
 }

+ 268 - 0
ecal-support/src/ecalDebugAdapter.ts

@@ -0,0 +1,268 @@
+/**
+ * Debug Adapter for VS Code to support the ECAL debugger.
+ *
+ * See the debugger extension guide:
+ * https://code.visualstudio.com/api/extension-guides/debugger-extension
+ */
+
+import { logger, Logger, LoggingDebugSession, InitializedEvent, Thread } from 'vscode-debugadapter';
+import { DebugProtocol } from 'vscode-debugprotocol';
+import { WaitGroup } from "@jpwilliams/waitgroup";
+
+/**
+ * ECALDebugArguments are the arguments which VSCode can pass to the debug adapter.
+ * This defines the parameter which a VSCode instance using the ECAL extention can pass to the
+ * debug adapter from a lauch configuration ('.vscode/launch.json') in a project folder.
+ */
+interface ECALDebugArguments extends DebugProtocol.LaunchRequestArguments {
+
+	dir: string; // Root directory for ECAL interpreter
+    serverURL: string // URL of the ECAL debug server
+	executeOnEntry?: boolean; // Flag if the debugged script should be executed when the debug session is started
+    trace?: boolean; // Flag to enable verbose logging of the adapter protocol
+}
+
+/**
+ * Debug adapter implementation.
+ *
+ * Uses: https://github.com/microsoft/vscode-debugadapter-node
+ *
+ * See the Debug Adapter Protocol (DAP) documentation:
+ * https://microsoft.github.io/debug-adapter-protocol/overview#How_it_works
+ */
+export class ECALDebugSession extends LoggingDebugSession {
+
+    /**
+     * WaitGroup to wait the finish of the configuration sequence
+     */
+    private wgConfig = new WaitGroup();
+
+	/**
+	 * Create a new debug adapter which is used for one debug session.
+	 */
+	public constructor() {
+		super("mock-debug.txt");
+console.error("##### constructor");
+		// Lines and columns start at 1
+		this.setDebuggerLinesStartAt1(true);
+        this.setDebuggerColumnsStartAt1(true);
+
+        // Increment the config WaitGroup counter for configurationDoneRequest()
+        this.wgConfig.add(1);
+    }
+
+	/**
+     * Called as the first step in the DAP. The client (e.g. VSCode)
+     * interrogates the debug adapter on the features which it provides.
+	 */
+	protected initializeRequest(response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments): void {
+        console.log("##### initializeRequest:", args);
+
+		response.body = response.body || {};
+
+		// The adapter implements the configurationDoneRequest.
+		response.body.supportsConfigurationDoneRequest = true;
+
+        this.sendResponse(response);
+
+		this.sendEvent(new InitializedEvent());
+	}
+
+
+
+    /**
+     * Called as part of the "configuration Done" step in the DAP. The client (e.g. VSCode) has
+     * finished the initialization of the debug adapter.
+     */
+	protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse, args: DebugProtocol.ConfigurationDoneArguments): void {
+console.error("##### configurationDoneRequest");
+
+		super.configurationDoneRequest(response, args);
+		this.wgConfig.done();
+	}
+
+    /**
+     * The client (e.g. VSCode) asks the debug adapter to start the debuggee communication.
+     */
+	protected async launchRequest(response: DebugProtocol.LaunchResponse, args: ECALDebugArguments) {
+console.error("##### launchRequest:", args);
+
+        // Setup logging either verbose or just on errors
+
+		logger.setup(args.trace ? Logger.LogLevel.Verbose : Logger.LogLevel.Error, false);
+
+		await this.wgConfig.wait(); // Wait for configuration sequence to finish
+
+		this.sendResponse(response);
+	}
+
+	protected async setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): Promise<void> {
+        console.error("##### setBreakPointsRequest:", args);
+
+		response.body = {
+			breakpoints: []
+		};
+		this.sendResponse(response);
+	}
+
+	protected threadsRequest(response: DebugProtocol.ThreadsResponse): void {
+        console.error("##### threadsRequest");
+
+		// runtime supports no threads so just return a default thread.
+		response.body = {
+			threads: [
+				new Thread(1, "thread 1")
+			]
+		};
+		this.sendResponse(response);
+    }
+
+
+	protected stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): void {
+        console.error("##### stackTraceRequest:", args);
+
+		response.body = {
+			stackFrames:[]
+		};
+		this.sendResponse(response);
+	}
+
+    protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments): void {
+        console.error("##### scopesRequest:", args);
+
+		response.body = {
+			scopes: []
+		};
+		this.sendResponse(response);
+	}
+
+
+	protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments, request?: DebugProtocol.Request) {
+        console.error("##### variablesRequest", args, request);
+
+		response.body = {
+			variables: []
+		};
+		this.sendResponse(response);
+	}
+
+	protected continueRequest(response: DebugProtocol.ContinueResponse, args: DebugProtocol.ContinueArguments): void {
+        console.error("##### continueRequest", args);
+		this.sendResponse(response);
+	}
+
+	protected reverseContinueRequest(response: DebugProtocol.ReverseContinueResponse, args: DebugProtocol.ReverseContinueArguments): void {
+        console.error("##### reverseContinueRequest", args);
+		this.sendResponse(response);
+ 	}
+
+	protected nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments): void {
+        console.error("##### nextRequest", args);
+		this.sendResponse(response);
+	}
+
+	protected stepBackRequest(response: DebugProtocol.StepBackResponse, args: DebugProtocol.StepBackArguments): void {
+        console.error("##### stepBackRequest", args);
+		this.sendResponse(response);
+	}
+
+	protected stepInTargetsRequest(response: DebugProtocol.StepInTargetsResponse, args: DebugProtocol.StepInTargetsArguments) {
+        console.error("##### stepInTargetsRequest", args);
+		response.body = {
+			targets: []
+		};
+		this.sendResponse(response);
+	}
+
+	protected stepInRequest(response: DebugProtocol.StepInResponse, args: DebugProtocol.StepInArguments): void {
+        console.error("##### stepInRequest", args);
+		this.sendResponse(response);
+	}
+
+	protected stepOutRequest(response: DebugProtocol.StepOutResponse, args: DebugProtocol.StepOutArguments): void {
+        console.error("##### stepOutRequest", args);
+		this.sendResponse(response);
+	}
+
+	protected async evaluateRequest(response: DebugProtocol.EvaluateResponse, args: DebugProtocol.EvaluateArguments): Promise<void> {
+        console.error("##### evaluateRequest", args);
+
+		response.body = {
+			result: "evaluate",
+			variablesReference: 0
+		};
+		this.sendResponse(response);
+	}
+
+	protected dataBreakpointInfoRequest(response: DebugProtocol.DataBreakpointInfoResponse, args: DebugProtocol.DataBreakpointInfoArguments): void {
+        console.error("##### dataBreakpointInfoRequest", args);
+
+		response.body = {
+            dataId: null,
+            description: "cannot break on data access",
+            accessTypes: undefined,
+            canPersist: false
+        };
+
+		this.sendResponse(response);
+	}
+
+	protected setDataBreakpointsRequest(response: DebugProtocol.SetDataBreakpointsResponse, args: DebugProtocol.SetDataBreakpointsArguments): void {
+        console.error("##### setDataBreakpointsRequest", args);
+
+		response.body = {
+			breakpoints: []
+		};
+
+		this.sendResponse(response);
+	}
+
+	protected completionsRequest(response: DebugProtocol.CompletionsResponse, args: DebugProtocol.CompletionsArguments): void {
+        console.error("##### completionsRequest", args);
+
+		response.body = {
+			targets: [
+				{
+					label: "item 10",
+					sortText: "10"
+				},
+				{
+					label: "item 1",
+					sortText: "01"
+				},
+				{
+					label: "item 2",
+					sortText: "02"
+				},
+				{
+					label: "array[]",
+					selectionStart: 6,
+					sortText: "03"
+				},
+				{
+					label: "func(arg)",
+					selectionStart: 5,
+					selectionLength: 3,
+					sortText: "04"
+				}
+			]
+		};
+		this.sendResponse(response);
+	}
+
+	protected cancelRequest(response: DebugProtocol.CancelResponse, args: DebugProtocol.CancelArguments) {
+        console.error("##### cancelRequest", args);
+        this.sendResponse(response);
+	}
+
+	protected customRequest(command: string, response: DebugProtocol.Response, args: any) {
+        console.error("##### customRequest", args);
+
+		if (command === 'toggleFormatting') {
+			this.sendResponse(response);
+		} else {
+			super.customRequest(command, response, args);
+		}
+	}
+
+}

+ 23 - 0
ecal-support/src/extension.ts

@@ -0,0 +1,23 @@
+import * as vscode from 'vscode';
+import { ProviderResult } from 'vscode';
+import { ECALDebugSession } from './ecalDebugAdapter';
+
+export function activate(context: vscode.ExtensionContext) {
+    console.error("#### Activate extension");
+    context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('ecaldebug', new InlineDebugAdapterFactory()));
+}
+
+export function deactivate() {
+}
+
+
+class InlineDebugAdapterFactory implements vscode.DebugAdapterDescriptorFactory {
+
+	createDebugAdapterDescriptor(_session: vscode.DebugSession): ProviderResult<vscode.DebugAdapterDescriptor> {
+
+        // Declare the ECALDebugSession as an DebugAdapterInlineImplementation so extention and adapter can
+        // run in-process (making it possible to easily debug the adapter)
+
+		return new vscode.DebugAdapterInlineImplementation(new ECALDebugSession());
+	}
+}

+ 26 - 0
ecal-support/tsconfig.json

@@ -0,0 +1,26 @@
+{
+	"compilerOptions": {
+		"module": "commonjs",
+		"target": "es6",
+		"outDir": "out",
+		"lib": [
+			"es6"
+		],
+		"sourceMap": true,
+		"rootDir": "src",
+		"strict": true,
+		"noImplicitReturns": true,
+		"noFallthroughCasesInSwitch": true,
+		"noUnusedParameters": true,
+		"noImplicitAny": true,
+		"removeComments": true,
+		"noUnusedLocals": true,
+		"noImplicitThis": true,
+		"inlineSourceMap": false,
+		"preserveConstEnums": true,
+		"strictNullChecks": true
+	},
+	"exclude": [
+		"node_modules", "tmp"
+	]
+}

+ 13 - 0
examples/game_of_life/.vscode/launch.json

@@ -0,0 +1,13 @@
+{
+    "version": "0.2.0",
+    "configurations": [		{
+        "type": "ecaldebug",
+        "request": "launch",
+        "name": "Debug ECAL script with ECAL Debug Server",
+
+        "serverURL": "localhost:43806",
+        "dir": "${workspaceFolder}",
+        "executeOnEntry": true,
+        "trace": true,
+    }]
+}