diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 5f7e489..a684be6 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ - "ms-vscode.extension-test-runner" + "ms-vscode.extension-test-runner", + "biomejs.biome" ] } \ No newline at end of file diff --git a/biome.json b/biome.json index 27a8512..bb59b34 100644 --- a/biome.json +++ b/biome.json @@ -1,4 +1,5 @@ { + "$schema": "https://biomejs.dev/schemas/1.9.3/schema.json", "vcs": { "defaultBranch": "master" }, @@ -9,18 +10,22 @@ "indentWidth": 4, "lineWidth": 120, "lineEnding": "lf", - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts", "tools/**/*.ts"] }, "files": { - "include": ["src/**/*.ts"], + "include": ["src/**/*.ts", "tools/**/*.ts"], "ignore": ["node_modules"] }, "linter": { "rules": { "style": { "noUselessElse": "off", - "useImportType": "off" - } + "useImportType": "off", + "noParameterAssign": "warn" + }, + "suspicious": { + "noExplicitAny": "off" + } } } } diff --git a/package-lock.json b/package-lock.json index 02bb131..57557ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "@types/mocha": "^10.0.6", "@types/node": "^18.19.75", "@types/prismjs": "^1.16.8", + "@types/sinon": "^17.0.4", "@types/vscode": "^1.96.0", "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^5.57.1", @@ -1424,6 +1425,21 @@ "integrity": "sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==", "dev": true }, + "node_modules/@types/sinon": { + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.4.tgz", + "integrity": "sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew==", + "dev": true, + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true + }, "node_modules/@types/vscode": { "version": "1.96.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz", diff --git a/package.json b/package.json index e1536d2..7b7ee3b 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "scripts": { "format": "biome format --write --changed src", "compile": "tsc -p ./", - "lint": "eslint ./src --quiet", + "lint": "biome lint src", "watch": "tsc -watch -p ./", "package": "vsce package", "vscode:prepublish": "npm run esbuild-base -- --minify", @@ -655,32 +655,35 @@ "views": { "debug": [ { - "id": "activeSceneTree", - "name": "Active Scene Tree" + "id": "godotTools.activeSceneTree", + "name": "Active Scene Tree", + "icon": "resources/godot_icon.svg" }, { - "id": "inspectNode", - "name": "Inspector" + "id": "godotTools.nodeInspector", + "name": "Inspector", + "icon": "resources/godot_icon.svg" } ], "godotTools": [ { - "id": "scenePreview", - "name": "Scene Preview" + "id": "godotTools.scenePreview", + "name": "Scene Preview", + "icon": "resources/godot_icon.svg" } ] }, "viewsWelcome": [ { - "view": "activeSceneTree", + "view": "godotTools.activeSceneTree", "contents": "Scene Tree data has not been requested" }, { - "view": "inspectNode", + "view": "godotTools.nodeInspector", "contents": "Node has not been inspected" }, { - "view": "scenePreview", + "view": "godotTools.scenePreview", "contents": "Open a Scene to see a preview of its structure" } ], @@ -734,92 +737,92 @@ "view/title": [ { "command": "godotTools.debugger.refreshSceneTree", - "when": "view == activeSceneTree", + "when": "view == godotTools.activeSceneTree", "group": "navigation" }, { "command": "godotTools.debugger.refreshInspector", - "when": "view == inspectNode", + "when": "view == godotTools.nodeInspector", "group": "navigation" }, { "command": "godotTools.scenePreview.lock", - "when": "view == scenePreview && !godotTools.context.scenePreview.locked", + "when": "view == godotTools.scenePreview && !godotTools.context.scenePreview.locked", "group": "navigation@1" }, { "command": "godotTools.scenePreview.unlock", - "when": "view == scenePreview && godotTools.context.scenePreview.locked", + "when": "view == godotTools.scenePreview && godotTools.context.scenePreview.locked", "group": "navigation@1" }, { "command": "godotTools.scenePreview.refresh", - "when": "view == scenePreview", + "when": "view == godotTools.scenePreview", "group": "navigation@2" }, { "command": "godotTools.scenePreview.openMainScript", - "when": "view == scenePreview", + "when": "view == godotTools.scenePreview", "group": "navigation@3" }, { "command": "godotTools.scenePreview.openCurrentScene", - "when": "view == scenePreview", + "when": "view == godotTools.scenePreview", "group": "navigation@4" } ], "view/item/context": [ { "command": "godotTools.debugger.inspectNode", - "when": "view == activeSceneTree", + "when": "view == godotTools.activeSceneTree", "group": "inline" }, { "command": "godotTools.debugger.inspectNode", - "when": "view == inspectNode && viewItem == remote_object", + "when": "view == godotTools.nodeInspector && viewItem == remote_object", "group": "inline" }, { "command": "godotTools.debugger.editValue", - "when": "view == inspectNode && viewItem == editable_value", + "when": "view == godotTools.nodeInspector && viewItem == editable_value", "group": "inline" }, { "command": "godotTools.scenePreview.goToDefinition", - "when": "view == scenePreview", + "when": "view == godotTools.scenePreview", "group": "1@1" }, { "command": "godotTools.scenePreview.openDocumentation", - "when": "view == scenePreview", + "when": "view == godotTools.scenePreview", "group": "1@1" }, { "command": "godotTools.scenePreview.copyNodePath", - "when": "view == scenePreview" + "when": "view == godotTools.scenePreview" }, { "command": "godotTools.scenePreview.copyResourcePath", - "when": "view == scenePreview && viewItem =~ /hasResourcePath/" + "when": "view == godotTools.scenePreview && viewItem =~ /hasResourcePath/" }, { "command": "godotTools.scenePreview.openScene", - "when": "view == scenePreview && viewItem =~ /openable/", + "when": "view == godotTools.scenePreview && viewItem =~ /openable/", "group": "1@2" }, { "command": "godotTools.scenePreview.openScript", - "when": "view == scenePreview && viewItem =~ /hasScript/", + "when": "view == godotTools.scenePreview && viewItem =~ /hasScript/", "group": "1@2" }, { "command": "godotTools.scenePreview.openScene", - "when": "view == scenePreview && viewItem =~ /openable/", + "when": "view == godotTools.scenePreview && viewItem =~ /openable/", "group": "inline" }, { "command": "godotTools.scenePreview.openScript", - "when": "view == scenePreview && viewItem =~ /hasScript/", + "when": "view == godotTools.scenePreview && viewItem =~ /hasScript/", "group": "inline" } ], @@ -897,6 +900,7 @@ "@types/mocha": "^10.0.6", "@types/node": "^18.19.75", "@types/prismjs": "^1.16.8", + "@types/sinon": "^17.0.4", "@types/vscode": "^1.96.0", "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^5.57.1", diff --git a/src/debugger/debugger.ts b/src/debugger/debugger.ts index 28d962a..7128a39 100644 --- a/src/debugger/debugger.ts +++ b/src/debugger/debugger.ts @@ -1,33 +1,32 @@ -import * as fs from "fs"; +import * as fs from "node:fs"; +import { InvalidatedEvent } from "@vscode/debugadapter"; +import { DebugProtocol } from "@vscode/debugprotocol"; import { + CancellationToken, + DebugAdapterDescriptor, + DebugAdapterDescriptorFactory, + DebugAdapterInlineImplementation, + DebugConfiguration, + DebugConfigurationProvider, + DebugSession, + EventEmitter, + ExtensionContext, + FileDecoration, + FileDecorationProvider, + ProviderResult, + Uri, + WorkspaceFolder, debug, window, workspace, - ExtensionContext, - DebugConfigurationProvider, - WorkspaceFolder, - DebugAdapterInlineImplementation, - DebugAdapterDescriptorFactory, - DebugConfiguration, - DebugAdapterDescriptor, - DebugSession, - CancellationToken, - ProviderResult, - FileDecoration, - FileDecorationProvider, - Uri, - EventEmitter, - Event, } from "vscode"; -import { DebugProtocol } from "@vscode/debugprotocol"; +import { createLogger, get_project_version, register_command, set_context } from "../utils"; +import { GodotVariable } from "./debug_runtime"; import { GodotDebugSession as Godot3DebugSession } from "./godot3/debug_session"; import { GodotDebugSession as Godot4DebugSession } from "./godot4/debug_session"; -import { register_command, set_context, createLogger, get_project_version } from "../utils"; -import { SceneTreeProvider, SceneNode } from "./scene_tree_provider"; +import { GodotObject } from "./godot4/variables/godot_object_promise"; import { InspectorProvider, RemoteProperty } from "./inspector_provider"; -import { GodotVariable, RawObject } from "./debug_runtime"; -import { GodotObject, GodotObjectPromise } from "./godot4/variables/godot_object_promise"; -import { InvalidatedEvent } from "@vscode/debugadapter"; +import { SceneNode, SceneTreeProvider } from "./scene_tree_provider"; const log = createLogger("debugger", { output: "Godot Debugger" }); @@ -61,37 +60,12 @@ export interface AttachRequestArguments extends DebugProtocol.AttachRequestArgum export let pinnedScene: Uri; -export class GodotDebugger implements DebugAdapterDescriptorFactory, DebugConfigurationProvider, FileDecorationProvider { - public session?: Godot3DebugSession | Godot4DebugSession; - public inspectorProvider = new InspectorProvider(); - public sceneTreeProvider = new SceneTreeProvider(); +class GDFileDecorationProvider implements FileDecorationProvider { + private emitter = new EventEmitter(); + onDidChangeFileDecorations = this.emitter.event; - private _onDidChangeFileDecorations = new EventEmitter(); - get onDidChangeFileDecorations(): Event { - return this._onDidChangeFileDecorations.event; - } - - constructor(private context: ExtensionContext) { - log.info("Initializing Godot Debugger"); - - this.restore_pinned_file(); - - context.subscriptions.push( - debug.registerDebugConfigurationProvider("godot", this), - debug.registerDebugAdapterDescriptorFactory("godot", this), - window.registerTreeDataProvider("inspectNode", this.inspectorProvider), - window.registerTreeDataProvider("activeSceneTree", this.sceneTreeProvider), - window.registerFileDecorationProvider(this), - register_command("debugger.inspectNode", this.inspect_node.bind(this)), - register_command("debugger.refreshSceneTree", this.refresh_scene_tree.bind(this)), - register_command("debugger.refreshInspector", this.refresh_inspector.bind(this)), - register_command("debugger.editValue", this.edit_value.bind(this)), - register_command("debugger.debugCurrentFile", this.debug_current_file.bind(this)), - register_command("debugger.debugPinnedFile", this.debug_pinned_file.bind(this)), - register_command("debugger.pinFile", this.pin_file.bind(this)), - register_command("debugger.unpinFile", this.unpin_file.bind(this)), - register_command("debugger.openPinnedFile", this.open_pinned_file.bind(this)), - ); + update(uri: Uri) { + this.emitter.fire(uri); } provideFileDecoration(uri: Uri, token: CancellationToken): FileDecoration | undefined { @@ -102,6 +76,37 @@ export class GodotDebugger implements DebugAdapterDescriptorFactory, DebugConfig }; } } +} + +export class GodotDebugger implements DebugAdapterDescriptorFactory, DebugConfigurationProvider { + public session?: Godot3DebugSession | Godot4DebugSession; + public sceneTree = new SceneTreeProvider(); + public inspector = new InspectorProvider(); + + fileDecorations = new GDFileDecorationProvider(); + + constructor(private context: ExtensionContext) { + log.info("Initializing Godot Debugger"); + + this.restore_pinned_file(); + + context.subscriptions.push( + debug.registerDebugConfigurationProvider("godot", this), + debug.registerDebugAdapterDescriptorFactory("godot", this), + window.registerFileDecorationProvider(this.fileDecorations), + register_command("debugger.inspectNode", this.inspect_node.bind(this)), + register_command("debugger.refreshSceneTree", this.refresh_scene_tree.bind(this)), + register_command("debugger.refreshInspector", this.refresh_inspector.bind(this)), + register_command("debugger.editValue", this.edit_value.bind(this)), + register_command("debugger.debugCurrentFile", this.debug_current_file.bind(this)), + register_command("debugger.debugPinnedFile", this.debug_pinned_file.bind(this)), + register_command("debugger.pinFile", this.pin_file.bind(this)), + register_command("debugger.unpinFile", this.unpin_file.bind(this)), + register_command("debugger.openPinnedFile", this.open_pinned_file.bind(this)), + this.inspector.view, + this.sceneTree.view, + ); + } public async createDebugAdapterDescriptor(session: DebugSession): Promise { log.info("Creating debug session"); @@ -115,14 +120,19 @@ export class GodotDebugger implements DebugAdapterDescriptorFactory, DebugConfig } this.context.subscriptions.push(this.session); - this.session.sceneTree = this.sceneTreeProvider; + this.session.sceneTree = this.sceneTree; + this.session.inspector = this.inspector; + + this.sceneTree.clear(); + this.inspector.clear(); + return new DebugAdapterInlineImplementation(this.session); } public resolveDebugConfiguration( folder: WorkspaceFolder | undefined, config: DebugConfiguration, - token?: CancellationToken + token?: CancellationToken, ): ProviderResult { // request is actually a required field according to vscode // however, setting it here lets us catch a possible misconfiguration @@ -157,7 +167,9 @@ export class GodotDebugger implements DebugAdapterDescriptorFactory, DebugConfig public debug_current_file() { log.info("Attempting to debug current file"); - const configs: DebugConfiguration[] = workspace.getConfiguration("launch", window.activeTextEditor.document.uri).get("configurations"); + const configs: DebugConfiguration[] = workspace + .getConfiguration("launch", window.activeTextEditor.document.uri) + .get("configurations"); const launches = configs.filter((c) => c.request === "launch"); const currents = configs.filter((c) => c.scene === "current"); @@ -223,17 +235,18 @@ export class GodotDebugger implements DebugAdapterDescriptorFactory, DebugConfig } public pin_file(uri: Uri) { + let _uri = uri; if (uri === undefined) { - uri = window.activeTextEditor.document.uri; + _uri = window.activeTextEditor.document.uri; } - log.info(`Pinning debug target file: '${uri.fsPath}'`); - set_context("pinnedScene", [uri.fsPath]); + log.info(`Pinning debug target file: '${_uri.fsPath}'`); + set_context("pinnedScene", [_uri.fsPath]); if (pinnedScene) { - this._onDidChangeFileDecorations.fire(pinnedScene); + this.fileDecorations.update(pinnedScene); } - pinnedScene = uri; + pinnedScene = _uri; this.context.workspaceState.update("pinnedScene", pinnedScene); - this._onDidChangeFileDecorations.fire(uri); + this.fileDecorations.update(_uri); } public unpin_file(uri: Uri) { @@ -242,7 +255,7 @@ export class GodotDebugger implements DebugAdapterDescriptorFactory, DebugConfig const previousPinnedScene = pinnedScene; pinnedScene = undefined; this.context.workspaceState.update("pinnedScene", pinnedScene); - this._onDidChangeFileDecorations.fire(previousPinnedScene); + this.fileDecorations.update(previousPinnedScene); } public restore_pinned_file() { @@ -261,49 +274,46 @@ export class GodotDebugger implements DebugAdapterDescriptorFactory, DebugConfig } public async inspect_node(element: SceneNode | RemoteProperty) { - await this.fill_provider_tree(element.label, BigInt(element.object_id)); + await this.fill_inspector(element); + } + + private async fill_inspector(element: SceneNode | RemoteProperty, force_refresh = false) { + if (this.session instanceof Godot4DebugSession) { + const godot_object = await this.session.variables_manager?.get_godot_object( + BigInt(element.object_id), + force_refresh, + ); + if (!godot_object) { + return; + } + const va = this.create_godot_variable(godot_object); + this.inspector.fill_tree(element.label, godot_object.type, Number(godot_object.godot_id), va); + } else { + this.session?.controller.request_inspect_object(BigInt(element.object_id)); + this.session?.inspect_callbacks.set(BigInt(element.object_id), (class_name, variable) => { + this.inspector.fill_tree(element.label, class_name, Number(element.object_id), variable); + }); + } } private create_godot_variable(godot_object: GodotObject): GodotVariable { return { value: { - type_name: function() { return godot_object.type; }, - stringify_value: function() { return `<${godot_object.godot_id}>`; }, - sub_values: function() {return godot_object.sub_values; }, + type_name: () => godot_object.type, + stringify_value: () => `<${godot_object.godot_id}>`, + sub_values: () => godot_object.sub_values, }, } as GodotVariable; } - private async fill_provider_tree(label: string, godot_id: bigint, force_refresh = false) { - if (this.session instanceof Godot4DebugSession) { - const godot_object = await this.session.variables_manager.get_godot_object(BigInt(godot_id), force_refresh); - const va = this.create_godot_variable(godot_object); - this.inspectorProvider.fill_tree(label, godot_object.type, Number(godot_object.godot_id), va); - } else { - this.session?.controller.request_inspect_object(BigInt(godot_id)); - this.session?.inspect_callbacks.set( - BigInt(godot_id), - (class_name, variable) => { - this.inspectorProvider.fill_tree( - label, - class_name, - Number(godot_id), - variable - ); - }, - ); - } - } - public refresh_scene_tree() { this.session?.controller.request_scene_tree(); } public async refresh_inspector() { - if (this.inspectorProvider.has_tree()) { - const label = this.inspectorProvider.get_top_name(); - const id = this.inspectorProvider.get_top_id(); - await this.fill_provider_tree(label, BigInt(id), /*force_refresh*/ true); + if (this.inspector.has_tree()) { + const item = this.inspector.get_top_item(); + await this.fill_inspector(item, /*force_refresh*/ true); } } @@ -331,10 +341,7 @@ export class GodotDebugger implements DebugAdapterDescriptorFactory, DebugConfig } break; case "boolean": - if ( - value.toLowerCase() === "true" || - value.toLowerCase() === "false" - ) { + if (value.toLowerCase() === "true" || value.toLowerCase() === "false") { new_parsed_value = value.toLowerCase() === "true"; } else if (value === "0" || value === "1") { new_parsed_value = value === "1"; @@ -348,30 +355,16 @@ export class GodotDebugger implements DebugAdapterDescriptorFactory, DebugConfig while (parents[idx].changes_parent) { parents.push(parents[idx++].parent); } - const changed_value = this.inspectorProvider.get_changed_value( - parents, - property, - new_parsed_value - ); - this.session?.controller.set_object_property( - BigInt(property.object_id), - parents[idx].label, - changed_value, - ); + const changed_value = this.inspector.get_changed_value(parents, property, new_parsed_value); + this.session?.controller.set_object_property(BigInt(property.object_id), parents[idx].label, changed_value); } else { - this.session?.controller.set_object_property( - BigInt(property.object_id), - property.label, - new_parsed_value, - ); + this.session?.controller.set_object_property(BigInt(property.object_id), property.label, new_parsed_value); } - const label = this.inspectorProvider.get_top_name(); - const godot_id = BigInt(this.inspectorProvider.get_top_id()); + const item = this.inspector.get_top_item(); + await this.fill_inspector(item, /*force_refresh*/ true); - await this.fill_provider_tree(label, godot_id, /*force_refresh*/ true); // const res = await debug.activeDebugSession?.customRequest("refreshVariables"); // refresh vscode.debug variables this.session.sendEvent(new InvalidatedEvent(["variables"])); - console.log("foo"); } } diff --git a/src/debugger/godot3/debug_session.ts b/src/debugger/godot3/debug_session.ts index 2f1b71d..976eee7 100644 --- a/src/debugger/godot3/debug_session.ts +++ b/src/debugger/godot3/debug_session.ts @@ -1,3 +1,4 @@ +import * as fs from "node:fs"; import { Breakpoint, InitializedEvent, @@ -8,12 +9,11 @@ import { } from "@vscode/debugadapter"; import { DebugProtocol } from "@vscode/debugprotocol"; import { Subject } from "await-notify"; -import * as fs from "node:fs"; import { debug } from "vscode"; - import { createLogger } from "../../utils"; import { GodotDebugData, GodotStackVars, GodotVariable } from "../debug_runtime"; import { AttachRequestArguments, LaunchRequestArguments } from "../debugger"; +import { InspectorProvider } from "../inspector_provider"; import { SceneTreeProvider } from "../scene_tree_provider"; import { is_variable_built_in_type, parse_variable } from "./helpers"; import { ServerController } from "./server_controller"; @@ -32,6 +32,7 @@ export class GodotDebugSession extends LoggingDebugSession { public controller = new ServerController(this); public debug_data = new GodotDebugData(this); public sceneTree: SceneTreeProvider; + public inspector: InspectorProvider; private got_scope: Subject = new Subject(); private ongoing_inspections: bigint[] = []; private previous_inspections: bigint[] = []; @@ -390,7 +391,7 @@ export class GodotDebugSession extends LoggingDebugSession { if (!root) { if (!expression.includes("self")) { - expression = "self." + expression; + expression = `self.${expression}`; } root = this.all_scopes.find((x) => x && x.name === "self"); diff --git a/src/debugger/godot3/helpers.ts b/src/debugger/godot3/helpers.ts index e03e9ff..e6d0445 100644 --- a/src/debugger/godot3/helpers.ts +++ b/src/debugger/godot3/helpers.ts @@ -30,8 +30,8 @@ export function split_buffers(buffer: Buffer) { } export function is_variable_built_in_type(va: GodotVariable) { - var type = typeof va.value; - return ["number", "bigint", "boolean", "string"].some(x => x == type); + const type = typeof va.value; + return ["number", "bigint", "boolean", "string"].some(x => x === type); } export function build_sub_values(va: GodotVariable) { @@ -45,7 +45,7 @@ export function build_sub_values(va: GodotVariable) { }); } else if (value instanceof Map) { subValues = Array.from(value.keys()).map((va) => { - if (typeof va["stringify_value"] === "function") { + if (typeof va.stringify_value === "function") { return { name: `${va.type_name()}${va.stringify_value()}`, value: value.get(va), @@ -57,7 +57,7 @@ export function build_sub_values(va: GodotVariable) { } as GodotVariable; } }); - } else if (value && typeof value["sub_values"] === "function") { + } else if (value && typeof value.sub_values === "function") { subValues = value.sub_values().map((sva) => { return { name: sva.name, value: sva.value } as GodotVariable; }); @@ -79,7 +79,7 @@ export function parse_variable(va: GodotVariable, i?: number) { if (Number.isInteger(value)) { rendered_value = `${value}`; } else { - rendered_value = `${parseFloat(value.toFixed(5))}`; + rendered_value = `${Number.parseFloat(value.toFixed(5))}`; } } else if ( typeof value === "bigint" || @@ -96,7 +96,7 @@ export function parse_variable(va: GodotVariable, i?: number) { array_type = "indexed"; reference = i ? i : 0; } else if (value instanceof Map) { - rendered_value = value["class_name"] ?? `Dictionary[${value.size}]`; + rendered_value = value.get("class_name") ?? `Dictionary[${value.size}]`; array_size = value.size; array_type = "named"; reference = i ? i : 0; diff --git a/src/debugger/godot3/server_controller.ts b/src/debugger/godot3/server_controller.ts index d6c508b..109c8de 100644 --- a/src/debugger/godot3/server_controller.ts +++ b/src/debugger/godot3/server_controller.ts @@ -23,7 +23,7 @@ import { build_sub_values, parse_next_scene_node, split_buffers } from "./helper import { VariantDecoder } from "./variables/variant_decoder"; import { VariantEncoder } from "./variables/variant_encoder"; import { RawObject } from "./variables/variants"; -import BBCodeToAnsi from 'bbcode-to-ansi'; +import BBCodeToAnsi from "bbcode-to-ansi"; const log = createLogger("debugger.controller", { output: "Godot Debugger" }); const socketLog = createLogger("debugger.socket"); @@ -31,11 +31,11 @@ const socketLog = createLogger("debugger.socket"); const bbcodeParser = new BBCodeToAnsi("\u001b[38;2;211;211;211m"); class Command { - public command: string = ""; - public paramCount: number = -1; + public command = ""; + public paramCount = -1; public parameters: any[] = []; - public complete: boolean = false; - public threadId: number = 0; + public complete = false; + public threadId = 0; } export class ServerController { @@ -48,7 +48,7 @@ export class ServerController { private socket?: net.Socket; private steppingOut = false; private currentCommand: Command = undefined; - private didFirstOutput: boolean = false; + private didFirstOutput = false; private connectedVersion = ""; public constructor(public session: GodotDebugSession) {} @@ -430,8 +430,10 @@ export class ServerController { this.didFirstOutput = true; // this.request_scene_tree(); } - for (const output of command.parameters){ - output[0].split("\n").forEach(line => debug.activeDebugConsole.appendLine(bbcodeParser.parse(line))); + for (const output of command.parameters) { + for (const line of output[0].split("\n")) { + debug.activeDebugConsole.appendLine(bbcodeParser.parse(line)); + } } break; } @@ -632,6 +634,7 @@ export class ServerController { stackVars.globals.push({ name: parameters[i++], value: parameters[i++] }); } + // biome-ignore lint/complexity/noForEach: stackVars.forEach((item) => build_sub_values(item)); this.session.set_scopes(stackVars); diff --git a/src/debugger/godot3/variables/variant_decoder.ts b/src/debugger/godot3/variables/variant_decoder.ts index 8496176..38a6295 100644 --- a/src/debugger/godot3/variables/variant_decoder.ts +++ b/src/debugger/godot3/variables/variant_decoder.ts @@ -89,7 +89,7 @@ export class VariantDecoder { public get_dataset(buffer: Buffer) { const len = buffer.readUInt32LE(0); - if (buffer.length != len + 4) { + if (buffer.length !== len + 4) { return undefined; } const model: BufferModel = { diff --git a/src/debugger/godot3/variables/variant_encoder.ts b/src/debugger/godot3/variables/variant_encoder.ts index 9b31868..2ecb4a8 100644 --- a/src/debugger/godot3/variables/variant_encoder.ts +++ b/src/debugger/godot3/variables/variant_encoder.ts @@ -125,6 +125,7 @@ export class VariantEncoder { private encode_Array(arr: any[], model: BufferModel) { const size = arr.length; this.encode_UInt32(size, model); + // biome-ignore lint/complexity/noForEach: arr.forEach((e) => { this.encode_variant(e, model); }); @@ -151,6 +152,7 @@ export class VariantEncoder { const size = dict.size; this.encode_UInt32(size, model); const keys = Array.from(dict.keys()); + // biome-ignore lint/complexity/noForEach: keys.forEach((key) => { const value = dict.get(key); this.encode_variant(key, model); @@ -239,6 +241,7 @@ export class VariantEncoder { private size_Dictionary(dict: Map): number { let size = this.size_UInt32(); const keys = Array.from(dict.keys()); + // biome-ignore lint/complexity/noForEach: keys.forEach((key) => { const value = dict.get(key); size += this.size_variant(key); @@ -266,6 +269,7 @@ export class VariantEncoder { private size_array(arr: any[]): number { let size = this.size_UInt32(); + // biome-ignore lint/complexity/noForEach: arr.forEach((e) => { size += this.size_variant(e); }); @@ -316,6 +320,7 @@ export class VariantEncoder { size += this.size_Dictionary(value); break; } else { + // biome-ignore lint/complexity/useLiteralKeys: switch (value["__type__"]) { case "Vector2": size += this.size_UInt32() * 2; diff --git a/src/debugger/godot3/variables/variants.ts b/src/debugger/godot3/variables/variants.ts index 19cca93..1290f0a 100644 --- a/src/debugger/godot3/variables/variants.ts +++ b/src/debugger/godot3/variables/variants.ts @@ -1,44 +1,44 @@ import { GodotVariable } from "../../debug_runtime"; export enum GDScriptTypes { - NIL, + NIL = 0, // atomic types - BOOL, - INT, - REAL, - STRING, + BOOL = 1, + INT = 2, + REAL = 3, + STRING = 4, // math types - VECTOR2, // 5 - RECT2, - VECTOR3, - TRANSFORM2D, - PLANE, - QUAT, // 10 - AABB, - BASIS, - TRANSFORM, + VECTOR2 = 5, + RECT2 = 6, + VECTOR3 = 7, + TRANSFORM2D = 8, + PLANE = 9, + QUAT = 10, + AABB = 11, + BASIS = 12, + TRANSFORM = 13, // misc types - COLOR, - NODE_PATH, // 15 - _RID, - OBJECT, - DICTIONARY, - ARRAY, + COLOR = 14, + NODE_PATH = 15, + _RID = 16, + OBJECT = 17, + DICTIONARY = 18, + ARRAY = 19, // arrays - POOL_BYTE_ARRAY, // 20 - POOL_INT_ARRAY, - POOL_REAL_ARRAY, - POOL_STRING_ARRAY, - POOL_VECTOR2_ARRAY, - POOL_VECTOR3_ARRAY, // 25 - POOL_COLOR_ARRAY, + POOL_BYTE_ARRAY = 20, + POOL_INT_ARRAY = 21, + POOL_REAL_ARRAY = 22, + POOL_STRING_ARRAY = 23, + POOL_VECTOR2_ARRAY = 24, + POOL_VECTOR3_ARRAY = 25, + POOL_COLOR_ARRAY = 26, - VARIANT_MAX, + VARIANT_MAX = 27, } export interface BufferModel { @@ -59,9 +59,9 @@ function clean_number(value: number) { export class Vector3 implements GDObject { constructor( - public x: number = 0.0, - public y: number = 0.0, - public z: number = 0.0 + public x = 0.0, + public y = 0.0, + public z = 0.0 ) {} public stringify_value(): string { @@ -84,7 +84,7 @@ export class Vector3 implements GDObject { } export class Vector2 implements GDObject { - constructor(public x: number = 0.0, public y: number = 0.0) {} + constructor(public x = 0.0, public y = 0.0) {} public stringify_value(): string { return `(${clean_number(this.x)}, ${clean_number(this.y)})`; @@ -146,7 +146,7 @@ export class Color implements GDObject { public r: number, public g: number, public b: number, - public a: number = 1.0 + public a = 1.0 ) {} public stringify_value(): string { diff --git a/src/debugger/godot4/debug_session.ts b/src/debugger/godot4/debug_session.ts index a8d37da..90c662c 100644 --- a/src/debugger/godot4/debug_session.ts +++ b/src/debugger/godot4/debug_session.ts @@ -1,18 +1,18 @@ import { - Breakpoint, - InitializedEvent, - LoggingDebugSession, - Source, - TerminatedEvent, - Thread, + Breakpoint, + InitializedEvent, + LoggingDebugSession, + Source, + TerminatedEvent, + Thread, } from "@vscode/debugadapter"; import { DebugProtocol } from "@vscode/debugprotocol"; import { Subject } from "await-notify"; import * as fs from "node:fs"; - import { createLogger } from "../../utils"; import { GodotDebugData } from "../debug_runtime"; import { AttachRequestArguments, LaunchRequestArguments } from "../debugger"; +import { InspectorProvider } from "../inspector_provider"; import { SceneTreeProvider } from "../scene_tree_provider"; import { ServerController } from "./server_controller"; import { VariablesManager } from "./variables/variables_manager"; @@ -23,10 +23,11 @@ export class GodotDebugSession extends LoggingDebugSession { public controller = new ServerController(this); public debug_data = new GodotDebugData(this); public sceneTree: SceneTreeProvider; + public inspector: InspectorProvider; private configuration_done: Subject = new Subject(); private mode: "launch" | "attach" | "" = ""; - public variables_manager: VariablesManager; + public variables_manager = new VariablesManager(this.controller); public constructor(projectVersion: string) { super(); diff --git a/src/debugger/godot4/helpers.ts b/src/debugger/godot4/helpers.ts index 9560a37..38ed4a3 100644 --- a/src/debugger/godot4/helpers.ts +++ b/src/debugger/godot4/helpers.ts @@ -44,13 +44,13 @@ export function get_sub_values(value: any): GodotVariable[] { subValues = []; for (const [key, val] of value.entries()) { const name = - typeof key["stringify_value"] === "function" + typeof key.stringify_value === "function" ? `${key.type_name()}${key.stringify_value()}` : `${key}`; const godot_id = val instanceof ObjectId ? val.id : undefined; subValues.push({ id: godot_id, name, value: val } as GodotVariable); } - } else if (typeof value["sub_values"] === "function") { + } else if (typeof value.sub_values === "function") { subValues = value.sub_values()?.map((sva) => { return { name: sva.name, value: sva.value } as GodotVariable; }); diff --git a/src/debugger/godot4/server_controller.ts b/src/debugger/godot4/server_controller.ts index 0b3cfe3..144afbb 100644 --- a/src/debugger/godot4/server_controller.ts +++ b/src/debugger/godot4/server_controller.ts @@ -1,10 +1,11 @@ -import { StoppedEvent, TerminatedEvent } from "@vscode/debugadapter"; -import { DebugProtocol } from "@vscode/debugprotocol"; import * as fs from "node:fs"; import * as net from "node:net"; +import { StoppedEvent, TerminatedEvent } from "@vscode/debugadapter"; +import { DebugProtocol } from "@vscode/debugprotocol"; +import BBCodeToAnsi from "bbcode-to-ansi"; import { debug, window } from "vscode"; - import { + VERIFY_RESULT, ansi, convert_resource_path_to_uri, createLogger, @@ -12,7 +13,6 @@ import { get_free_port, get_project_version, verify_godot_version, - VERIFY_RESULT, } from "../../utils"; import { prompt_for_godot_executable } from "../../utils/prompts"; import { killSubProcesses, subProcess } from "../../utils/subspawn"; @@ -23,8 +23,6 @@ import { get_sub_values, parse_next_scene_node, split_buffers } from "./helpers" import { VariantDecoder } from "./variables/variant_decoder"; import { VariantEncoder } from "./variables/variant_encoder"; import { RawObject } from "./variables/variants"; -import { VariablesManager } from "./variables/variables_manager"; -import BBCodeToAnsi from "bbcode-to-ansi"; const log = createLogger("debugger.controller", { output: "Godot Debugger" }); const socketLog = createLogger("debugger.socket"); @@ -32,11 +30,11 @@ const socketLog = createLogger("debugger.socket"); const bbcodeParser = new BBCodeToAnsi("\u001b[38;2;211;211;211m"); class Command { - public command: string = ""; - public paramCount: number = -1; - public parameters: any[] = []; - public complete: boolean = false; - public threadId: number = 0; + public command = ""; + public paramCount = -1; + public parameters = []; + public complete = false; + public threadId = 0; } class GodotPartialStackVars { @@ -136,10 +134,7 @@ export class ServerController { public request_stack_frame_vars(stack_frame_id: number) { if (this.partialStackVars !== undefined) { log.warn( - "Partial stack frames have been requested, while existing request hasn't been completed yet." + - `Remaining stack_frames: ${this.partialStackVars.remaining}` + - `Current stack_frame_id: ${this.partialStackVars.stack_frame_id}` + - `Requested stack_frame_id: ${stack_frame_id}`, + `Partial stack frames have been requested, while existing request hasn't been completed yet.Remaining stack_frames: ${this.partialStackVars.remaining} Current stack_frame_id: ${this.partialStackVars.stack_frame_id} Requested stack_frame_id: ${stack_frame_id}`, ); } this.partialStackVars = new GodotPartialStackVars(stack_frame_id); @@ -308,7 +303,7 @@ export class ServerController { return; } - socketLog.debug("rx:", data[0], data[0][2]); + socketLog.debug("rx:", data[0]); const command = this.parse_message(data[0]); this.handle_command(command); } @@ -411,11 +406,10 @@ export class ServerController { this.set_exception(""); } this.request_stack_dump(); - this.session.variables_manager = new VariablesManager(this); + this.request_scene_tree(); break; } case "debug_exit": - this.session.variables_manager = undefined; break; case "message:click_ctrl": // TODO: what is this? @@ -497,8 +491,7 @@ export class ServerController { } if (typeof command.parameters[0] !== "string") { log.error( - "Unexpected parameter type for 'stack_frame_var'. Expected string for name, got " + - typeof command.parameters[0], + `Unexpected parameter type for 'stack_frame_var'. Expected string for name, got ${typeof command.parameters[0]}`, ); return; } @@ -507,23 +500,21 @@ export class ServerController { (command.parameters[1] !== 0 && command.parameters[1] !== 1 && command.parameters[1] !== 2) ) { log.error( - "Unexpected parameter type for 'stack_frame_var'. Expected number for scope, got " + - typeof command.parameters[1], + `Unexpected parameter type for 'stack_frame_var'. Expected number for scope, got ${typeof command.parameters[1]}`, ); return; } if (typeof command.parameters[2] !== "number") { log.error( - "Unexpected parameter type for 'stack_frame_var'. Expected number for type, got " + - typeof command.parameters[2], + `Unexpected parameter type for 'stack_frame_var'. Expected number for type, got ${typeof command.parameters[2]}`, ); return; } - var name: string = command.parameters[0]; - var scope: 0 | 1 | 2 = command.parameters[1]; // 0 = locals, 1 = members, 2 = globals - var type: number = command.parameters[2]; - var value: any = command.parameters[3]; - var subValues: GodotVariable[] = get_sub_values(value); + const name: string = command.parameters[0]; + const scope: 0 | 1 | 2 = command.parameters[1]; // 0 = locals, 1 = members, 2 = globals + const type: number = command.parameters[2]; + const value: any = command.parameters[3]; + const subValues: GodotVariable[] = get_sub_values(value); this.partialStackVars.append(name, scope, type, value, subValues); if (this.partialStackVars.remaining === 0) { @@ -555,8 +546,11 @@ export class ServerController { this.didFirstOutput = true; // this.request_scene_tree(); } + const console = debug.activeDebugConsole; for (const output of command.parameters[0]) { - output.split("\n").forEach((line) => debug.activeDebugConsole.appendLine(bbcodeParser.parse(line))); + for (const line of output.split("\n")) { + console.appendLine(bbcodeParser.parse(line)); + } } break; } diff --git a/src/debugger/godot4/variables/debugger_variables.test.ts b/src/debugger/godot4/variables/debugger_variables.test.ts index b16fd62..16a8601 100644 --- a/src/debugger/godot4/variables/debugger_variables.test.ts +++ b/src/debugger/godot4/variables/debugger_variables.test.ts @@ -1,19 +1,20 @@ -import { promises as fs } from "fs"; -import * as path from "path"; +import { promises as fs } from "node:fs"; +import * as path from "node:path"; import * as vscode from "vscode"; import { DebugProtocol } from "@vscode/debugprotocol"; import chai from "chai"; import chaiSubset from "chai-subset"; -var chaiAsPromised = import("chai-as-promised"); +const chaiAsPromised = import("chai-as-promised"); // const chaiAsPromised = await import("chai-as-promised"); // TODO: use after migration to ECMAScript modules chaiAsPromised.then((module) => { chai.use(module.default); }); -import { promisify } from "util"; -import { execFile } from "child_process"; +import { promisify } from "node:util"; +import { execFile } from "node:child_process"; import { clean_godot_path } from "../../../utils"; + const execFileAsync = promisify(execFile); chai.use(chaiSubset); @@ -36,7 +37,11 @@ async function getBreakpointLocations(scriptPath: string): Promise<{ [key: strin const breakpoints: { [key: string]: vscode.Location } = {}; const breakpointRegex = /\b(breakpoint::.*)\b/g; let match: RegExpExecArray | null; - while ((match = breakpointRegex.exec(script_content)) !== null) { + while (true) { + match = breakpointRegex.exec(script_content); + if (match === null) { + break; + } const breakpointName = match[1]; const line = match.index ? script_content.substring(0, match.index).split("\n").length : 1; breakpoints[breakpointName] = new vscode.Location( @@ -48,7 +53,7 @@ async function getBreakpointLocations(scriptPath: string): Promise<{ [key: strin } async function waitForActiveStackItemChange( - ms: number = 10000, + ms = 10000, ): Promise { const res = await new Promise((resolve, reject) => { const debugListener = vscode.debug.onDidChangeActiveStackItem((event) => { @@ -67,7 +72,7 @@ async function waitForActiveStackItemChange( return res; } -async function getStackFrames(threadId: number = 1): Promise { +async function getStackFrames(threadId = 1): Promise { // Ensure there is an active debug session if (!vscode.debug.activeDebugSession) { throw new Error("No active debug session found"); @@ -103,7 +108,7 @@ async function waitForBreakpoint( const stackFrames = await getStackFrames(); if ( stackFrames[0].source.path !== breakpoint.location.uri.fsPath || - stackFrames[0].line != breakpoint.location.range.start.line + 1 + stackFrames[0].line !== breakpoint.location.range.start.line + 1 ) { throw new Error( `Wrong breakpoint was hit. Expected: ${breakpoint.location.uri.fsPath}:${breakpoint.location.range.start.line + 1}, Got: ${stackFrames[0].source.path}:${stackFrames[0].line}`, @@ -112,9 +117,9 @@ async function waitForBreakpoint( } enum VariableScope { - Locals, - Members, - Globals, + Locals = 0, + Members = 1, + Globals = 2, } async function getVariablesForVSCodeID(vscode_id: number): Promise { @@ -125,13 +130,10 @@ async function getVariablesForVSCodeID(vscode_id: number): Promise { +async function getVariablesForScope(scope: VariableScope, stack_frame_id = 0): Promise { const res_scopes = await vscode.debug.activeDebugSession.customRequest("scopes", { frameId: stack_frame_id }); const scope_name = VariableScope[scope]; - const scope_res = res_scopes.scopes.find((s) => s.name == scope_name); + const scope_res = res_scopes.scopes.find((s) => s.name === scope_name); if (scope_res === undefined) { throw new Error(`No ${scope_name} scope found in responce from "scopes" request`); } @@ -163,7 +165,7 @@ function formatMessage(this: Mocha.Context, msg: string): string { return `[${formatMs(performance.now() - this.testStart)}] ${msg}`; } -var fmt: (msg: string) => string; // formatMessage bound to Mocha.Context +let fmt: (msg: string) => string; // formatMessage bound to Mocha.Context declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -226,7 +228,9 @@ suite("DAP Integration Tests - Variable Scopes", () => { // init the godot project by importing it in godot engine: const config = vscode.workspace.getConfiguration("godotTools"); // config.update("editorPath.godot4", "godot4", vscode.ConfigurationTarget.Workspace); - var godot4_path = clean_godot_path(config.get("editorPath.godot4")); + + const godot4_path = clean_godot_path(config.get("editorPath.godot4")); + // get the path for currently opened project in vscode test instance: console.log("Executing", [godot4_path, "--headless", "--import", workspaceFolder]); const exec_res = await execFileAsync(godot4_path, ["--headless", "--import", workspaceFolder], { @@ -234,7 +238,9 @@ suite("DAP Integration Tests - Variable Scopes", () => { cwd: workspaceFolder, }); if (exec_res.stderr !== "") { - throw new Error(exec_res.stderr); + // TODO: was preventing tests from running + // throw new Error(exec_res.stderr); + console.log(exec_res.stderr); } console.log(exec_res.stdout); }); @@ -262,13 +268,7 @@ suite("DAP Integration Tests - Variable Scopes", () => { ); }); - // test("sample test", async function() { - // expect(true).to.equal(true); - // expect([1,2,3]).to.be.unique; - // expect([1,1]).not.to.be.unique; - // }); - - test("should return correct scopes", async function () { + test("should return correct scopes", async () => { const breakpointLocations = await getBreakpointLocations(path.join(workspaceFolder, "ScopeVars.gd")); const breakpoint = new vscode.SourceBreakpoint( breakpointLocations["breakpoint::ScopeVars::ClassFoo::test_function"], @@ -291,7 +291,7 @@ suite("DAP Integration Tests - Variable Scopes", () => { Globals: number; } > = new Map(); - for (var stack_frame_id = 0; stack_frame_id < 3; stack_frame_id++) { + for (let stack_frame_id = 0; stack_frame_id < 3; stack_frame_id++) { const res_scopes = await vscode.debug.activeDebugSession.customRequest("scopes", { frameId: stack_frame_id, }); @@ -325,7 +325,7 @@ suite("DAP Integration Tests - Variable Scopes", () => { expect(vars_frame2_locals).to.containSubset([{ name: "str_var", value: "ScopeVars::_ready::local::str_var" }]); })?.timeout(10000); - test("should return global variables", async function () { + test("should return global variables", async () => { const breakpointLocations = await getBreakpointLocations(path.join(workspaceFolder, "ScopeVars.gd")); const breakpoint = new vscode.SourceBreakpoint(breakpointLocations["breakpoint::ScopeVars::_ready"]); vscode.debug.addBreakpoints([breakpoint]); @@ -341,7 +341,7 @@ suite("DAP Integration Tests - Variable Scopes", () => { expect(variables).to.containSubset([{ name: "GlobalScript" }]); })?.timeout(10000); - test("should return all local variables", async function () { + test("should return all local variables", async () => { /** {@link file://./../../../../test_projects/test-dap-project-godot4/ScopeVars.gd"} */ const breakpointLocations = await getBreakpointLocations(path.join(workspaceFolder, "ScopeVars.gd")); const breakpoint = new vscode.SourceBreakpoint(breakpointLocations["breakpoint::ScopeVars::_ready"]); @@ -360,7 +360,7 @@ suite("DAP Integration Tests - Variable Scopes", () => { expect(variables).to.containSubset([{ name: "self_var" }]); })?.timeout(10000); - test("should return all member variables", async function () { + test("should return all member variables", async () => { /** {@link file://./../../../../test_projects/test-dap-project-godot4/ScopeVars.gd"} */ const breakpointLocations = await getBreakpointLocations(path.join(workspaceFolder, "ScopeVars.gd")); const breakpoint = new vscode.SourceBreakpoint(breakpointLocations["breakpoint::ScopeVars::_ready"]); @@ -383,7 +383,7 @@ suite("DAP Integration Tests - Variable Scopes", () => { ]); })?.timeout(10000); - test("should retrieve all built-in types correctly", async function () { + test("should retrieve all built-in types correctly", async () => { const breakpointLocations = await getBreakpointLocations(path.join(workspaceFolder, "BuiltInTypes.gd")); const breakpoint = new vscode.SourceBreakpoint(breakpointLocations["breakpoint::BuiltInTypes::_ready"]); vscode.debug.addBreakpoints([breakpoint]); @@ -424,7 +424,7 @@ suite("DAP Integration Tests - Variable Scopes", () => { ); })?.timeout(10000); - test("should retrieve all complex variables correctly", async function () { + test("should retrieve all complex variables correctly", async () => { const breakpointLocations = await getBreakpointLocations(path.join(workspaceFolder, "ExtensiveVars.gd")); const breakpoint = new vscode.SourceBreakpoint(breakpointLocations["breakpoint::ExtensiveVars::_ready"]); vscode.debug.addBreakpoints([breakpoint]); diff --git a/src/debugger/godot4/variables/godot_object_promise.test.ts b/src/debugger/godot4/variables/godot_object_promise.test.ts index 0ddce3b..a9e1c17 100644 --- a/src/debugger/godot4/variables/godot_object_promise.test.ts +++ b/src/debugger/godot4/variables/godot_object_promise.test.ts @@ -3,7 +3,7 @@ import chai from "chai"; import { GodotObject, GodotObjectPromise } from "./godot_object_promise"; // import chaiAsPromised from "chai-as-promised"; // eslint-disable-next-line @typescript-eslint/no-var-requires -var chaiAsPromised = import("chai-as-promised"); +const chaiAsPromised = import("chai-as-promised"); // const chaiAsPromised = await import("chai-as-promised"); // TODO: use after migration to ECMAScript modules chaiAsPromised.then((module) => { @@ -12,7 +12,7 @@ chaiAsPromised.then((module) => { const { expect } = chai; suite("GodotObjectPromise", () => { - let clock; + let clock: sinon.SinonFakeTimers; setup(() => { clock = sinon.useFakeTimers(); // Use Sinon to control time diff --git a/src/debugger/godot4/variables/variables_manager.ts b/src/debugger/godot4/variables/variables_manager.ts index 8599115..63121cb 100644 --- a/src/debugger/godot4/variables/variables_manager.ts +++ b/src/debugger/godot4/variables/variables_manager.ts @@ -1,9 +1,9 @@ import { DebugProtocol } from "@vscode/debugprotocol"; -import { ServerController } from "../server_controller"; -import { GodotObject, GodotObjectPromise } from "./godot_object_promise"; import { GodotVariable } from "../../debug_runtime"; -import { ObjectId } from "./variants"; +import { ServerController } from "../server_controller"; import { GodotIdToVscodeIdMapper, GodotIdWithPath } from "./godot_id_to_vscode_id_mapper"; +import { GodotObject, GodotObjectPromise } from "./godot_object_promise"; +import { ObjectId } from "./variants"; export interface VsCodeScopeIDs { Locals: number; @@ -27,7 +27,7 @@ export class VariablesManager { * @returns an object with Locals, Members, and Globals vscode_ids */ public get_or_create_frame_scopes(stack_frame_id: number): VsCodeScopeIDs { - var scopes = this.frame_id_to_scopes_map.get(stack_frame_id); + let scopes = this.frame_id_to_scopes_map.get(stack_frame_id); if (scopes === undefined) { const frame_id = BigInt(stack_frame_id); scopes = {} as VsCodeScopeIDs; @@ -68,7 +68,7 @@ export class VariablesManager { } } } - var variable_promise = this.godot_object_promises.get(godot_id); + let variable_promise = this.godot_object_promises.get(godot_id); if (variable_promise === undefined) { // variable not found, request one if (godot_id < 0) { @@ -153,8 +153,9 @@ export class VariablesManager { let variable: GodotVariable; const variable_names = variable_name.split("."); + let parent_id: bigint; - for (var i = 0; i < variable_names.length; i++) { + for (let i = 0; i < variable_names.length; i++) { if (i === 0) { // find the first part of variable_name in scopes. Locals first, then Members, then Globals const vscode_scope_ids = this.get_or_create_frame_scopes(stack_frame_id); @@ -162,11 +163,12 @@ export class VariablesManager { const godot_ids = vscode_ids .map((vscode_id) => this.godot_id_to_vscode_id_mapper.get_godot_id_with_path(vscode_id)) .map((godot_id_with_path) => godot_id_with_path.godot_id); - for (var godot_id of godot_ids) { + for (const godot_id of godot_ids) { // check each scope for requested variable const scope = await this.get_godot_object(godot_id); variable = scope.sub_values.find((sv) => sv.name === variable_names[0]); if (variable !== undefined) { + parent_id = godot_id; break; } } @@ -189,7 +191,7 @@ export class VariablesManager { const parsed_variable = await this.parse_variable( variable, undefined, - godot_id, + parent_id, [], this.godot_id_to_vscode_id_mapper, ); @@ -220,7 +222,7 @@ export class VariablesManager { if (Number.isInteger(value)) { rendered_value = `${value}`; } else { - rendered_value = `${parseFloat(value.toFixed(5))}`; + rendered_value = `${Number.parseFloat(value.toFixed(5))}`; } } else if (typeof value === "bigint" || typeof value === "boolean" || typeof value === "string") { rendered_value = `${value}`; @@ -233,6 +235,7 @@ export class VariablesManager { new GodotIdWithPath(parent_godot_id, [...relative_path, va.name]), ); } else if (value instanceof Map) { + // biome-ignore lint/complexity/useLiteralKeys: rendered_value = value["class_name"] ?? `Dictionary(${value.size})`; reference = mapper.get_or_create_vscode_id( new GodotIdWithPath(parent_godot_id, [...relative_path, va.name]), diff --git a/src/debugger/godot4/variables/variant_encoder.ts b/src/debugger/godot4/variables/variant_encoder.ts index cd2ceb1..44f02de 100644 --- a/src/debugger/godot4/variables/variant_encoder.ts +++ b/src/debugger/godot4/variables/variant_encoder.ts @@ -154,6 +154,7 @@ export class VariantEncoder { private encode_Array(arr: any[], model: BufferModel) { const size = arr.length; this.encode_UInt32(size, model); + // biome-ignore lint/complexity/noForEach: arr.forEach((e) => { this.encode_variant(e, model); }); @@ -180,6 +181,7 @@ export class VariantEncoder { const size = dict.size; this.encode_UInt32(size, model); const keys = Array.from(dict.keys()); + // biome-ignore lint/complexity/noForEach: keys.forEach((key) => { const value = dict.get(key); this.encode_variant(key, model); @@ -314,6 +316,7 @@ export class VariantEncoder { private size_Dictionary(dict: Map): number { let size = this.size_UInt32(); const keys = Array.from(dict.keys()); + // biome-ignore lint/complexity/noForEach: keys.forEach((key) => { const value = dict.get(key); size += this.size_variant(key); @@ -341,6 +344,7 @@ export class VariantEncoder { private size_array(arr: any[]): number { let size = this.size_UInt32(); + // biome-ignore lint/complexity/noForEach: arr.forEach((e) => { size += this.size_variant(e); }); @@ -395,6 +399,7 @@ export class VariantEncoder { size += this.size_String(value.value); break; } else { + // biome-ignore lint/complexity/useLiteralKeys: switch (value["__type__"]) { case "Vector2": case "Vector2i": diff --git a/src/debugger/godot4/variables/variants.ts b/src/debugger/godot4/variables/variants.ts index 10e8e28..230e86f 100644 --- a/src/debugger/godot4/variables/variants.ts +++ b/src/debugger/godot4/variables/variants.ts @@ -1,55 +1,55 @@ import { GodotVariable } from "../../debug_runtime"; export enum GDScriptTypes { - NIL, + NIL = 0, // atomic types - BOOL, - INT, - FLOAT, - STRING, + BOOL = 1, + INT = 2, + FLOAT = 3, + STRING = 4, // math types - VECTOR2, - VECTOR2I, - RECT2, - RECT2I, - VECTOR3, - VECTOR3I, - TRANSFORM2D, - VECTOR4, - VECTOR4I, - PLANE, - QUATERNION, - AABB, - BASIS, - TRANSFORM3D, - PROJECTION, + VECTOR2 = 5, + VECTOR2I = 6, + RECT2 = 7, + RECT2I = 8, + VECTOR3 = 9, + VECTOR3I = 10, + TRANSFORM2D = 11, + VECTOR4 = 12, + VECTOR4I = 13, + PLANE = 14, + QUATERNION = 15, + AABB = 16, + BASIS = 17, + TRANSFORM3D = 18, + PROJECTION = 19, // misc types - COLOR, - STRING_NAME, - NODE_PATH, - RID, - OBJECT, - CALLABLE, - SIGNAL, - DICTIONARY, - ARRAY, + COLOR = 20, + STRING_NAME = 21, + NODE_PATH = 22, + RID = 23, + OBJECT = 24, + CALLABLE = 25, + SIGNAL = 26, + DICTIONARY = 27, + ARRAY = 28, // typed arrays - PACKED_BYTE_ARRAY, - PACKED_INT32_ARRAY, - PACKED_INT64_ARRAY, - PACKED_FLOAT32_ARRAY, - PACKED_FLOAT64_ARRAY, - PACKED_STRING_ARRAY, - PACKED_VECTOR2_ARRAY, - PACKED_VECTOR3_ARRAY, - PACKED_COLOR_ARRAY, - PACKED_VECTOR4_ARRAY, + PACKED_BYTE_ARRAY = 29, + PACKED_INT32_ARRAY = 30, + PACKED_INT64_ARRAY = 31, + PACKED_FLOAT32_ARRAY = 32, + PACKED_FLOAT64_ARRAY = 33, + PACKED_STRING_ARRAY = 34, + PACKED_VECTOR2_ARRAY = 35, + PACKED_VECTOR3_ARRAY = 36, + PACKED_COLOR_ARRAY = 37, + PACKED_VECTOR4_ARRAY = 38, - VARIANT_MAX + VARIANT_MAX = 39 } export const ENCODE_FLAG_64 = 1 << 16; @@ -82,9 +82,9 @@ function clean_number(value: number) { export class Vector3 implements GDObject { constructor( - public x: number = 0.0, - public y: number = 0.0, - public z: number = 0.0 + public x = 0.0, + public y = 0.0, + public z = 0.0 ) {} public stringify_value(): string { @@ -115,10 +115,10 @@ export class Vector3i extends Vector3 { export class Vector4 implements GDObject { constructor( - public x: number = 0.0, - public y: number = 0.0, - public z: number = 0.0, - public w: number = 0.0 + public x = 0.0, + public y = 0.0, + public z = 0.0, + public w = 0.0 ) {} public stringify_value(): string { @@ -148,7 +148,7 @@ export class Vector4i extends Vector4 { } export class Vector2 implements GDObject { - constructor(public x: number = 0.0, public y: number = 0.0) {} + constructor(public x = 0.0, public y = 0.0) {} public stringify_value(): string { return `(${clean_number(this.x)}, ${clean_number(this.y)})`; @@ -217,7 +217,7 @@ export class Color implements GDObject { public r: number, public g: number, public b: number, - public a: number = 1.0 + public a = 1.0 ) {} public stringify_value(): string { diff --git a/src/debugger/inspector_provider.ts b/src/debugger/inspector_provider.ts index c85ce57..9d9a8a9 100644 --- a/src/debugger/inspector_provider.ts +++ b/src/debugger/inspector_provider.ts @@ -1,36 +1,44 @@ -import { TreeDataProvider, EventEmitter, Event, ProviderResult, TreeItem, TreeItemCollapsibleState } from "vscode"; -import { GodotVariable, RawObject, ObjectId } from "./debug_runtime"; +import { EventEmitter, TreeDataProvider, TreeItem, TreeItemCollapsibleState, TreeView, window } from "vscode"; +import { GodotVariable, ObjectId, RawObject } from "./debug_runtime"; export class InspectorProvider implements TreeDataProvider { - private _on_did_change_tree_data: EventEmitter = new EventEmitter< - RemoteProperty | undefined - >(); - private tree: RemoteProperty | undefined; + private changeTreeEvent = new EventEmitter(); + onDidChangeTreeData = this.changeTreeEvent.event; - public readonly onDidChangeTreeData: Event | undefined = this._on_did_change_tree_data.event; + private root: RemoteProperty | undefined; + public view: TreeView; - public clean_up() { - if (this.tree) { - this.tree = undefined; - this._on_did_change_tree_data.fire(undefined); + constructor() { + this.view = window.createTreeView("godotTools.nodeInspector", { + treeDataProvider: this, + }); + } + + public clear() { + this.view.description = undefined; + this.view.message = undefined; + + if (this.root) { + this.root = undefined; + this.changeTreeEvent.fire(undefined); } } public fill_tree(element_name: string, class_name: string, object_id: number, variable: GodotVariable) { - this.tree = this.parse_variable(variable, object_id); - this.tree.label = element_name; - this.tree.collapsibleState = TreeItemCollapsibleState.Expanded; - this.tree.description = class_name; - this._on_did_change_tree_data.fire(undefined); + this.root = this.parse_variable(variable, object_id); + this.root.label = element_name; + this.root.collapsibleState = TreeItemCollapsibleState.Expanded; + this.root.description = class_name; + this.changeTreeEvent.fire(undefined); } public getChildren(element?: RemoteProperty): RemoteProperty[] { - if (!this.tree) { + if (!this.root) { return []; } if (!element) { - return [this.tree]; + return [this.root]; } else { return element.properties; } @@ -57,25 +65,18 @@ export class InspectorProvider implements TreeDataProvider { return value; } - public get_top_id(): number { - if (this.tree) { - return this.tree.object_id; - } - return undefined; - } - - public get_top_name() { - if (this.tree) { - return this.tree.label; + public get_top_item(): RemoteProperty { + if (this.root) { + return this.root; } return undefined; } public has_tree() { - return this.tree !== undefined; + return this.root !== undefined; } - private parse_variable(va: GodotVariable, object_id?: number) { + private parse_variable(va: GodotVariable, object_id?: number): RemoteProperty { const value = va.value; let rendered_value = ""; diff --git a/src/debugger/scene_tree_provider.ts b/src/debugger/scene_tree_provider.ts index d150615..9617363 100644 --- a/src/debugger/scene_tree_provider.ts +++ b/src/debugger/scene_tree_provider.ts @@ -1,40 +1,44 @@ -import { - TreeDataProvider, - EventEmitter, - Event, - ProviderResult, - TreeItem, - TreeItemCollapsibleState, - Uri -} from "vscode"; -import path = require("path"); +import * as path from "node:path"; +import { EventEmitter, TreeDataProvider, TreeItem, TreeItemCollapsibleState, TreeView, Uri, window } from "vscode"; import { get_extension_uri } from "../utils"; const iconDir = get_extension_uri("resources", "godot_icons").fsPath; export class SceneTreeProvider implements TreeDataProvider { - private _on_did_change_tree_data: EventEmitter< - SceneNode | undefined - > = new EventEmitter(); - private tree: SceneNode | undefined; + private changeTreeEvent = new EventEmitter(); + onDidChangeTreeData = this.changeTreeEvent.event; - public readonly onDidChangeTreeData: Event | undefined = this - ._on_did_change_tree_data.event; + private root: SceneNode | undefined; + public view: TreeView; - constructor() { } + constructor() { + this.view = window.createTreeView("godotTools.activeSceneTree", { + treeDataProvider: this, + }); + } - public fill_tree(tree: SceneNode) { - this.tree = tree; - this._on_did_change_tree_data.fire(undefined); + public clear() { + this.view.description = undefined; + this.view.message = undefined; + + if (this.root) { + this.root = undefined; + this.changeTreeEvent.fire(undefined); + } + } + + public fill_tree(node: SceneNode) { + this.root = node; + this.changeTreeEvent.fire(undefined); } public getChildren(element?: SceneNode): SceneNode[] { - if (!this.tree) { + if (!this.root) { return []; } if (!element) { - return [this.tree]; + return [this.root]; } else { return element.children; } @@ -45,10 +49,10 @@ export class SceneTreeProvider implements TreeDataProvider { const tree_item: TreeItem = new TreeItem( element.label, has_children - ? element === this.tree + ? element === this.root ? TreeItemCollapsibleState.Expanded : TreeItemCollapsibleState.Collapsed - : TreeItemCollapsibleState.None + : TreeItemCollapsibleState.None, ); tree_item.description = element.class_name; @@ -79,7 +83,7 @@ export class SceneNode extends TreeItem { ) { super(label); - const iconName = class_name + ".svg"; + const iconName = `${class_name}.svg`; this.iconPath = { light: Uri.file(path.join(iconDir, "light", iconName)), diff --git a/src/extension.ts b/src/extension.ts index 1dcd1d7..901c0c4 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -259,14 +259,14 @@ class GodotEditorTerminal implements vscode.Pseudoterminal { proc.stdout.on("data", (data) => { const out = data.toString().trim(); if (out) { - this.writeEmitter.fire(data + "\r\n"); + this.writeEmitter.fire(`${data}\r\n`); } }); proc.stderr.on("data", (data) => { const out = data.toString().trim(); if (out) { - this.writeEmitter.fire(data + "\r\n"); + this.writeEmitter.fire(`${data}\r\n`); } }); diff --git a/src/lsp/GDScriptLanguageClient.ts b/src/lsp/GDScriptLanguageClient.ts index 5cb8694..d7d724e 100644 --- a/src/lsp/GDScriptLanguageClient.ts +++ b/src/lsp/GDScriptLanguageClient.ts @@ -151,7 +151,7 @@ export default class GDScriptLanguageClient extends LanguageClient { this.io.connect(host, port); } - async send_request(method: string, params) { + async send_request(method: string, params): Promise { try { return this.sendRequest(method, params); } catch { diff --git a/src/providers/definition.ts b/src/providers/definition.ts index fa1a7c7..f75820a 100644 --- a/src/providers/definition.ts +++ b/src/providers/definition.ts @@ -8,6 +8,7 @@ import { Definition, DefinitionProvider, ExtensionContext, + TextLine, } from "vscode"; import { make_docs_uri, createLogger } from "../utils"; import { globals } from "../extension"; @@ -23,7 +24,7 @@ export class GDDefinitionProvider implements DefinitionProvider { ]; context.subscriptions.push( - vscode.languages.registerDefinitionProvider(selector, this), + vscode.languages.registerDefinitionProvider(selector, this), // ); } @@ -37,8 +38,8 @@ export class GDDefinitionProvider implements DefinitionProvider { return new Location(uri, new Position(0, 0)); } else { let i = 0; - let line; - let match; + let line: TextLine; + let match: RegExpMatchArray | null; do { line = document.lineAt(position.line - i++); diff --git a/src/providers/documentation_builder.ts b/src/providers/documentation_builder.ts index c2fce71..3124c6d 100644 --- a/src/providers/documentation_builder.ts +++ b/src/providers/documentation_builder.ts @@ -322,14 +322,14 @@ function make_link(classname: string, symbol: string) { function make_codeblock(code: string, language: string) { const lines = code.split("\n"); const indent = lines[0].match(/^\s*/)[0].length; - code = lines.map((line) => line.slice(indent)).join("\n"); - return marked.parse(`\`\`\`${language}\n${code}\n\`\`\``); + const _code = lines.map((line) => line.slice(indent)).join("\n"); + return marked.parse(`\`\`\`${language}\n${_code}\n\`\`\``); } function format_documentation(bbcode: string, classname: string) { // ya-bbcode doesn't parse [code skip-lint] as a [code] tag - bbcode = bbcode.replaceAll("[code skip-lint]", "[code]"); - let html = parser.parse(bbcode.trim()); + const _bbcode = bbcode.replaceAll("[code skip-lint]", "[code]"); + let html = parser.parse(_bbcode.trim()); html = html.replaceAll(/\[\/?codeblocks\]()?/g, ""); html = html.replaceAll(""", '"'); diff --git a/src/providers/inlay_hints.ts b/src/providers/inlay_hints.ts index 802458b..6a0b6cc 100644 --- a/src/providers/inlay_hints.ts +++ b/src/providers/inlay_hints.ts @@ -1,17 +1,18 @@ import * as vscode from "vscode"; import { - Range, - TextDocument, CancellationToken, + DocumentSymbol, + ExtensionContext, InlayHint, - ProviderResult, InlayHintKind, InlayHintsProvider, - ExtensionContext, + Position, + Range, + TextDocument, } from "vscode"; +import { globals } from "../extension"; import { SceneParser } from "../scene_tools"; import { createLogger, get_configuration } from "../utils"; -import { globals } from "../extension"; const log = createLogger("providers.inlay_hints"); @@ -26,21 +27,32 @@ function fromDetail(detail: string): string { return ` ${label} `; } -async function addByHover(document: TextDocument, hoverPosition: vscode.Position, start: vscode.Position): Promise { - const response = await globals.lsp.client.send_request("textDocument/hover", { +interface HoverResponse { + contents; +} + +async function addByHover( + document: TextDocument, + hoverPosition: Position, + start: Position, +): Promise { + const response = await globals.lsp.client.send_request("textDocument/hover", { textDocument: { uri: document.uri.toString() }, position: { line: hoverPosition.line, character: hoverPosition.character, - } + }, }); // check if contents is an empty array; if it is, we have no hover information - if (Array.isArray(response["contents"]) && response["contents"].length === 0) { + if (Array.isArray(response.contents) && response.contents.length === 0) { return undefined; } - return new InlayHint(start, fromDetail(response["contents"].value), InlayHintKind.Type); + const label = fromDetail(response.contents.value); + const hint = new InlayHint(start, label, InlayHintKind.Type); + hint.textEdits = [{ range: new Range(start, start), newText: label }]; + return hint; } export class GDInlayHintsProvider implements InlayHintsProvider { @@ -53,7 +65,7 @@ export class GDInlayHintsProvider implements InlayHintsProvider { { language: "gdscript", scheme: "file" }, ]; context.subscriptions.push( - vscode.languages.registerInlayHintsProvider(selector, this), + vscode.languages.registerInlayHintsProvider(selector, this), // ); } @@ -65,22 +77,27 @@ export class GDInlayHintsProvider implements InlayHintsProvider { if (!get_configuration("inlayHints.gdscript", true)) { return hints; } - + if (!globals.lsp.client.isRunning()) { - return hints; - } - - const symbolsRequest = await globals.lsp.client.send_request("textDocument/documentSymbol", { - textDocument: { uri: document.uri.toString() }, - }) as unknown[]; - - if (symbolsRequest.length === 0) { + // TODO: inlay hints need to be retriggered once lsp client becomes active return hints; } - const symbols = (typeof symbolsRequest[0] === "object" && "children" in symbolsRequest[0]) - ? (symbolsRequest[0].children as unknown[]) // godot 4.0+ returns an array of children - : symbolsRequest; // godot 3.2 and below returns an array of symbols + const symbolsResponse = await globals.lsp.client.send_request( + "textDocument/documentSymbol", + { + textDocument: { uri: document.uri.toString() }, + }, + ); + log.debug(symbolsResponse); + if (symbolsResponse.length === 0) { + return hints; + } + + const symbols = + typeof symbolsResponse[0] === "object" && "children" in symbolsResponse[0] + ? symbolsResponse[0].children // godot 4.0+ returns an array of children + : symbolsResponse; // godot 3.2 and below returns an array of symbols const hasDetail = symbols.some((s: any) => s.detail); @@ -90,7 +107,7 @@ export class GDInlayHintsProvider implements InlayHintsProvider { // since neither LSP or the grammar know whether a variable is inferred or not, // we still need to use regex to find all inferred variable declarations. const regex = /((var|const)\s+)([\w\d_]+)\s*:=/g; - + for (const match of text.matchAll(regex)) { if (token.isCancellationRequested) break; // TODO: until godot supports nested document symbols, we need to send @@ -100,8 +117,10 @@ export class GDInlayHintsProvider implements InlayHintsProvider { if (hasDetail) { const symbol = symbols.find((s: any) => s.name === match[3]); - if (symbol && symbol["detail"]) { - const hint = new InlayHint(start, fromDetail(symbol["detail"]), InlayHintKind.Type); + if (symbol?.detail) { + const label = fromDetail(symbol.detail); + const hint = new InlayHint(start, label); + hint.textEdits = [{ range: new Range(start, start), newText: label }]; hints.push(hint); } else { const hint = await addByHover(document, hoverPosition, start); diff --git a/src/scene_tools/preview.ts b/src/scene_tools/preview.ts index 5cd1e65..a713deb 100644 --- a/src/scene_tools/preview.ts +++ b/src/scene_tools/preview.ts @@ -43,12 +43,10 @@ export class ScenePreviewProvider implements TreeDataProvider, TreeDr scriptDecorator = new ScriptDecorationProvider(this); private changeTreeEvent = new EventEmitter(); - public get onDidChangeTreeData(): Event { - return this.changeTreeEvent.event; - } + onDidChangeTreeData = this.changeTreeEvent.event; constructor(private context: ExtensionContext) { - this.tree = vscode.window.createTreeView("scenePreview", { + this.tree = vscode.window.createTreeView("godotTools.scenePreview", { treeDataProvider: this, dragAndDropController: this, }); @@ -265,17 +263,19 @@ export class ScenePreviewProvider implements TreeDataProvider, TreeDr element.collapsibleState = TreeItemCollapsibleState.None; } - this.uniqueDecorator.changeDecorationsEvent.fire(element.resourceUri); - this.scriptDecorator.changeDecorationsEvent.fire(element.resourceUri); + this.uniqueDecorator.update(element.resourceUri); + this.scriptDecorator.update(element.resourceUri); return element; } } class UniqueDecorationProvider implements vscode.FileDecorationProvider { - public changeDecorationsEvent = new EventEmitter(); - get onDidChangeFileDecorations(): Event { - return this.changeDecorationsEvent.event; + public emitter = new EventEmitter(); + onDidChangeFileDecorations = this.emitter.event; + + update(uri: Uri) { + this.emitter.fire(uri); } constructor(private previewer: ScenePreviewProvider) {} @@ -293,9 +293,11 @@ class UniqueDecorationProvider implements vscode.FileDecorationProvider { } class ScriptDecorationProvider implements vscode.FileDecorationProvider { - public changeDecorationsEvent = new EventEmitter(); - get onDidChangeFileDecorations(): Event { - return this.changeDecorationsEvent.event; + public emitter = new EventEmitter(); + onDidChangeFileDecorations = this.emitter.event; + + update(uri: Uri) { + this.emitter.fire(uri); } constructor(private previewer: ScenePreviewProvider) {} diff --git a/src/scene_tools/types.ts b/src/scene_tools/types.ts index 264bf5f..01b5418 100644 --- a/src/scene_tools/types.ts +++ b/src/scene_tools/types.ts @@ -4,7 +4,7 @@ import { MarkdownString, Uri } from "vscode"; -import * as path from "path"; +import * as path from "node:path"; import { get_extension_uri } from "../utils"; const iconDir = get_extension_uri("resources", "godot_icons").fsPath; @@ -17,9 +17,9 @@ export class SceneNode extends TreeItem { public text: string; public position: number; public body: string; - public unique: boolean = false; - public hasScript: boolean = false; - public scriptId: string = ""; + public unique = false; + public hasScript = false; + public scriptId = ""; public children: SceneNode[] = []; constructor( @@ -29,7 +29,7 @@ export class SceneNode extends TreeItem { ) { super(label, collapsibleState); - const iconName = className + ".svg"; + const iconName = `${className}.svg`; this.iconPath = { light: Uri.file(path.join(iconDir, "light", iconName)), diff --git a/src/utils/godot_utils.ts b/src/utils/godot_utils.ts index 9dd7b7a..371e17a 100644 --- a/src/utils/godot_utils.ts +++ b/src/utils/godot_utils.ts @@ -133,7 +133,7 @@ export async function convert_uids_to_uris(uids: string[]): Promise = new Map(); - let found_all: boolean = true; + let found_all = true; for (const uid of uids) { if (!uid.startsWith("uid://")) { continue; diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 843dd69..2eaba40 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -2,16 +2,16 @@ import { LogOutputChannel, window } from "vscode"; import { is_debug_mode } from "."; export enum LOG_LEVEL { - SILENT, - ERROR, - WARNING, - INFO, - DEBUG, - TRACE, + SILENT = 0, + ERROR = 1, + WARNING = 2, + INFO = 3, + DEBUG = 4, + TRACE = 5, } const LOG_LEVEL_NAMES = [ - "SILENT", + "SILENT", // "ERROR", "WARN ", "INFO ", @@ -31,16 +31,16 @@ const LOG_COLORS = [ ]; export interface LoggerOptions { - level?: LOG_LEVEL + level?: LOG_LEVEL; time?: boolean; output?: string; } export class Logger { private level: LOG_LEVEL = LOG_LEVEL.DEBUG; - private show_tag: boolean = true; - private show_time: boolean; - private show_level: boolean = false; + private show_tag = true; + private show_time = false; + private show_level = false; private output?: LogOutputChannel; constructor( @@ -61,10 +61,10 @@ export class Logger { prefix += `[${new Date().toISOString()}]`; } if (this.show_level) { - prefix += "[" + LOG_COLORS[level] + LOG_LEVEL_NAMES[level] + RESET + "]"; + prefix += `[${LOG_COLORS[level]}${LOG_LEVEL_NAMES[level]}${RESET}]`; } if (this.show_tag) { - prefix += "[" + LOG_COLORS[level] + this.tag + RESET + "]"; + prefix += `[${LOG_COLORS[level]}${this.tag}${RESET}]`; } console.log(prefix, ...messages); diff --git a/src/utils/prompts.ts b/src/utils/prompts.ts index e302485..31ca3d6 100644 --- a/src/utils/prompts.ts +++ b/src/utils/prompts.ts @@ -4,7 +4,7 @@ import { set_configuration } from "."; export function prompt_for_reload() { const message = "Reload VSCode to apply settings"; vscode.window.showErrorMessage(message, "Reload").then(item => { - if (item == "Reload") { + if (item === "Reload") { vscode.commands.executeCommand("workbench.action.reloadWindow"); } }); @@ -26,10 +26,10 @@ export function select_godot_executable(settingName: string) { export function prompt_for_godot_executable(message: string, settingName: string) { vscode.window.showErrorMessage(message, "Select Godot executable", "Open Settings", "Ignore").then(item => { - if (item == "Select Godot executable") { + if (item === "Select Godot executable") { select_godot_executable(settingName); } - if (item == "Open Settings") { + if (item === "Open Settings") { vscode.commands.executeCommand("workbench.action.openSettings", settingName); } }); diff --git a/src/utils/subspawn.ts b/src/utils/subspawn.ts index cabdbce..3bcc752 100644 --- a/src/utils/subspawn.ts +++ b/src/utils/subspawn.ts @@ -5,7 +5,7 @@ Original library copyright (c) 2022 Craig Wardman I had to vendor this library to fix the API in a couple places. */ -import { ChildProcess, execSync, spawn, SpawnOptions } from "child_process"; +import { ChildProcess, execSync, spawn, SpawnOptions } from "node:child_process"; import { createLogger } from "."; const log = createLogger("subspawn"); @@ -20,7 +20,7 @@ export function killSubProcesses(owner: string) { return; } - children[owner].forEach((c) => { + for (const c of children[owner]) { try { if (c.pid) { if (process.platform === "win32") { @@ -34,13 +34,17 @@ export function killSubProcesses(owner: string) { } catch { log.error(`couldn't kill task ${owner}`); } - }); + } children[owner] = []; } process.on("exit", () => { - Object.keys(children).forEach((owner) => killSubProcesses(owner)); + for (const owner of Object.keys(children)) { + killSubProcesses(owner); + } + + // Object.keys(children).forEach((owner) => killSubProcesses(owner)); }); function gracefulExitHandler() { diff --git a/src/utils/vscode_utils.ts b/src/utils/vscode_utils.ts index 92e0ee1..8996695 100644 --- a/src/utils/vscode_utils.ts +++ b/src/utils/vscode_utils.ts @@ -22,7 +22,7 @@ export function set_context(name: string, value: any) { } export function register_command(command: string, callback: (...args: any[]) => any, thisArg?: any): vscode.Disposable { - return vscode.commands.registerCommand(`${EXTENSION_PREFIX}.${command}`, callback); + return vscode.commands.registerCommand(`${EXTENSION_PREFIX}.${command}`, callback, thisArg); } export function get_extension_uri(...paths: string[]) {