diff --git a/lua/plugins/debugger.lua b/lua/plugins/debugger.lua index 4066670..5bfd5e3 100644 --- a/lua/plugins/debugger.lua +++ b/lua/plugins/debugger.lua @@ -268,6 +268,62 @@ return { end dap.configurations.c = dap.configurations.cpp + + -- Unity (Mono soft debugger) via Unity's MIT-licensed vscode-unity-debug + -- adapter, extracted from its .vsix into stdpath("data")/unity-debug. + -- Avoids vstuc, whose license (like vsdbg's) is VS Code only. + -- + -- UnityDebug.exe is patched by unity-debug/install.sh (see patch.cs there; + -- original kept as UnityDebug.exe.orig): stock, it only matches editor + -- processes named exactly "Unity"/"Unity Editor", but Unity Hub launches + -- the editor as "unityhub-unity-editor-". The patch makes it a + -- case-insensitive "Unity" prefix match, covering both launch methods and + -- any version. Helper processes (UnityPackageManager, import workers...) + -- match too, so the adapter picks the real editor by the process_id in + -- EditorInstance.json -- which needs the config name to contain "Editor". + local unity_debug_exe = vim.fn.stdpath("data") .. "/unity-debug/extension/bin/UnityDebug.exe" + local unity_debug_install = vim.fn.stdpath("config") .. "/unity-debug/install.sh" + dap.adapters.unity = function(callback) + if vim.fn.filereadable(unity_debug_exe) == 0 then + vim.notify("Installing Unity debug adapter...") + local result = vim.system({ unity_debug_install }, { text = true }):wait() + if result.code ~= 0 then + vim.notify("Unity debug adapter install failed:\n" .. result.stderr, vim.log.levels.ERROR) + return + end + end + callback({ + type = "executable", + command = "mono", + args = { unity_debug_exe }, + -- The adapter strips the leading "/" from `path` (a Windows-ism) + -- before reading it, so run it from / for the now-relative path + -- to resolve. + options = { cwd = "/" }, + }) + end + + -- Library/EditorInstance.json only exists while the editor is open; the + -- adapter reads the editor PID from it to derive the debugger port. + -- The Unity project may sit in a repo subfolder, so search upward from + -- the buffer first, then downward from cwd. + local function unity_editor_instance() + local root = vim.fs.root(0, "ProjectSettings") + if root then + return root .. "/Library/EditorInstance.json" + end + local found = vim.fs.find("EditorInstance.json", { path = vim.fn.getcwd(), type = "file", limit = 1 }) + return found[1] + end + + dap.configurations.cs = { + { + name = "Unity Editor", + type = "unity", + request = "launch", -- unity-debug attaches to the running editor despite "launch" + path = unity_editor_instance, + }, + } end } diff --git a/unity-debug/install.sh b/unity-debug/install.sh new file mode 100755 index 0000000..ff9bcb4 --- /dev/null +++ b/unity-debug/install.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Installs Unity's MIT-licensed vscode-unity-debug adapter for nvim-dap and +# patches it to find editors however they were launched (see patch.cs). +# Re-runnable: always re-extracts and re-patches from the pristine .vsix. +set -euo pipefail + +VERSION=3.0.1 +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEST="${1:-${XDG_DATA_HOME:-$HOME/.local/share}/nvim/unity-debug}" +BIN="$DEST/extension/bin" + +for cmd in curl unzip mono mcs; do + command -v "$cmd" >/dev/null || { echo "unity-debug install: missing '$cmd'" >&2; exit 1; } +done + +mkdir -p "$DEST" +VSIX="$DEST/unity-debug-$VERSION.vsix" +[ -f "$VSIX" ] || curl -fsSL -o "$VSIX" \ + "https://github.com/Unity-Technologies/vscode-unity-debug/releases/download/Version-$VERSION/unity-debug-$VERSION.vsix" +rm -rf "$DEST/extension" +unzip -qo "$VSIX" -d "$DEST" + +TMP="$(mktemp -d)" +trap 'rm -rf "$TMP"' EXIT +mcs "$HERE/patch.cs" -r:"$BIN/Mono.Cecil.dll" -out:"$TMP/patch.exe" +cp "$BIN/UnityDebug.exe" "$BIN/UnityDebug.exe.orig" +MONO_PATH="$BIN" mono "$TMP/patch.exe" "$BIN/UnityDebug.exe.orig" "$TMP/UnityDebug.exe" +cp "$TMP/UnityDebug.exe" "$BIN/UnityDebug.exe" +echo "unity-debug installed and patched in $DEST" diff --git a/unity-debug/patch.cs b/unity-debug/patch.cs new file mode 100644 index 0000000..e5a85e3 --- /dev/null +++ b/unity-debug/patch.cs @@ -0,0 +1,20 @@ +// Patches vscode-unity-debug's UnityProcessDiscovery.GetUnityEditorProcesses to +// prefix-match process names instead of requiring an exact "Unity"/"Unity Editor". +// The comparison is OrdinalIgnoreCase, so the "Unity" prefix then matches editors +// launched directly ("Unity") and via Unity Hub ("unityhub-unity-editor-"). +// Extra matches (UnityPackageManager etc.) are fine: with a config name containing +// "Editor", the adapter picks the editor by process_id from EditorInstance.json. +using System; using System.Linq; using Mono.Cecil; +class Patch { + static int Main(string[] a) { + var asm = AssemblyDefinition.ReadAssembly(a[0]); + var m = asm.MainModule.Types.SelectMany(t => t.Methods).Single(x => x.Name == "GetUnityEditorProcesses"); + var startsWith = asm.MainModule.ImportReference(typeof(string).GetMethod("StartsWith", new[] { typeof(string), typeof(StringComparison) })); + int c = 0; + foreach (var i in m.Body.Instructions) + if (i.Operand is MethodReference r && r.DeclaringType.FullName == "System.String" && r.Name == "Equals" && r.Parameters.Count == 2) { i.Operand = startsWith; c++; } + if (c != 1) { Console.Error.WriteLine($"expected 1 string.Equals call in {m.Name}, found {c}"); return 1; } + asm.Write(a[1]); + return 0; + } +}