From d09c58817c85638d29dcd2b2b0856ef15f7d9795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=ADla?= <125702241+danielsila@users.noreply.github.com> Date: Fri, 28 Nov 2025 19:35:01 +0100 Subject: [PATCH 1/3] Add 'konsole' to available terminals for Linux --- Editor/NeovimPreferences.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Editor/NeovimPreferences.cs b/Editor/NeovimPreferences.cs index 09fbe18..fa5a887 100644 --- a/Editor/NeovimPreferences.cs +++ b/Editor/NeovimPreferences.cs @@ -12,6 +12,7 @@ public static class NeovimPreferences { ["ghostty"] = "", // Binary #if UNITY_EDITOR_LINUX + ["konsole"] = "", ["xdg-terminal-exec"] = "", ["kitty"] = "", #else From 670cc712224ff27c663ec7f8887e71eab9efde9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=ADla?= <125702241+danielsila@users.noreply.github.com> Date: Fri, 28 Nov 2025 19:56:04 +0100 Subject: [PATCH 2/3] Update debug log message for Windows execution --- Editor/NeovimEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Editor/NeovimEditor.cs b/Editor/NeovimEditor.cs index 67d901c..fe5938d 100644 --- a/Editor/NeovimEditor.cs +++ b/Editor/NeovimEditor.cs @@ -78,7 +78,7 @@ public bool OpenProject(string path, int line, int column) }; if(debugging) - UnityEngine.Debug.Log($"[NvimUnity] Executing: {psi.FileName} {psi.Arguments}"); + UnityEngine.Debug.Log($"[NvimUnity] Executing on Windows: {psi.FileName} {psi.Arguments}"); Process.Start(defaultApp, $"{path} {line}"); } else From 94d5c69101b6b233312f46369134784e311a0c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=ADla?= Date: Fri, 28 Nov 2025 22:06:16 +0100 Subject: [PATCH 3/3] update --- Editor/NeovimEditor.cs | 390 +++++++++++++++++++++++------------------ Editor/Utils.cs | 267 ++++++++++++++-------------- 2 files changed, 352 insertions(+), 305 deletions(-) diff --git a/Editor/NeovimEditor.cs b/Editor/NeovimEditor.cs index fe5938d..ed3d82e 100644 --- a/Editor/NeovimEditor.cs +++ b/Editor/NeovimEditor.cs @@ -9,151 +9,199 @@ namespace NvimUnity { - [InitializeOnLoad] - public class NeovimEditor : IExternalCodeEditor + [InitializeOnLoad] + public class NeovimEditor : IExternalCodeEditor + { + public static string defaultApp => EditorPrefs.GetString("kScriptsDefaultApp"); + public static string OS = Utils.GetCurrentOS(); + public static string RootFolder = Utils.GetProjectRoot(); + + private static Config config; + private static bool needSaveConfig = false; + private static bool debugging = false; + + private static string EditorName = "Neovim Code Editor"; + private static string Socket => + OS == "Windows" ? @"\\.\pipe\unity2025" : + $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/.cache/nvimunity.sock"; + + static NeovimEditor() { - public static string defaultApp => EditorPrefs.GetString("kScriptsDefaultApp"); - public static string OS = Utils.GetCurrentOS(); - public static string RootFolder = Utils.GetProjectRoot(); + CodeEditor.Register(new NeovimEditor()); + config = ConfigManager.LoadConfig(); + config.last_project = RootFolder; + ConfigManager.SaveConfig(config); + } - private static Config config; - private static bool needSaveConfig = false; - private static bool debugging = false; + public string GetDisplayName() => EditorName; - private static string EditorName = "Neovim Code Editor"; - private static string Socket => - OS == "Windows" ? @"\\.\pipe\unity2025" : - $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/.cache/nvimunity.sock"; + public static bool IsNvimUnityDefaultEditor() + { + return string.Equals(defaultApp, Utils.GetLauncherPath()); + } - static NeovimEditor() - { - CodeEditor.Register(new NeovimEditor()); - config = ConfigManager.LoadConfig(); - config.last_project = RootFolder; - ConfigManager.SaveConfig(config); - } + private bool IsNvimActuallyRunning() + { + if (!SocketChecker.IsSocketActive(Socket)) + return false; - public string GetDisplayName() => EditorName; + try + { + string nvimPath = Utils.GetNeovimPath(); + var psi = new ProcessStartInfo + { + FileName = nvimPath, + Arguments = $"--server {Socket} --remote-expr \"1\"", + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + }; - public static bool IsNvimUnityDefaultEditor() + using (var process = Process.Start(psi)) { - return string.Equals(defaultApp, Utils.GetLauncherPath()); + process.WaitForExit(1000); + return process.ExitCode == 0; } + } + catch + { + return false; + } + } - public bool OpenProject(string path, int line, int column) + private void CleanupSocket() + { + try + { + if (OS != "Windows" && File.Exists(Socket)) { - if (string.IsNullOrEmpty(path)) - { - path = RootFolder; - } - else if (!Project.SupportsFile(path)) - { - return false; - } + File.Delete(Socket); + UnityEngine.Debug.Log($"[NvimUnity] Cleaned up stale socket: {Socket}"); + } + } + catch (Exception ex) + { + UnityEngine.Debug.LogWarning($"[NvimUnity] Failed to cleanup socket: {ex.Message}"); + } + } - if (!IsNvimUnityDefaultEditor()) - { - return false; - } + public bool OpenProject(string path, int line, int column) + { + if (string.IsNullOrEmpty(path)) + { + path = RootFolder; + } + else if (!Project.SupportsFile(path)) + { + return false; + } - if (!Project.Exists()) - SyncAll(); + if (!IsNvimUnityDefaultEditor()) + { + return false; + } - bool IsRunnigInNeovim = SocketChecker.IsSocketActive(Socket); + if (!Project.Exists()) + SyncAll(); - if (line <= 0) line = 1; + bool IsRunnigInNeovim = IsNvimActuallyRunning(); - if (!IsRunnigInNeovim) - { - try - { - if (OS == "Windows") - { - var psi = new ProcessStartInfo - { - FileName = defaultApp, - Arguments = $"{path} {line}", - UseShellExecute = true, - CreateNoWindow = false, - }; - - if(debugging) - UnityEngine.Debug.Log($"[NvimUnity] Executing on Windows: {psi.FileName} {psi.Arguments}"); - Process.Start(defaultApp, $"{path} {line}"); - } - else - { - // Original behavior for other OSes - ProcessStartInfo psi = Utils.BuildProcessStartInfo(defaultApp, path, line); - if(debugging) - UnityEngine.Debug.Log($"[NvimUnity] Executing in terminal: {psi.FileName} {psi.Arguments}"); - Process.Start(psi); - } - return true; - } - catch (Exception ex) - { - UnityEngine.Debug.LogError($"[NvimUnity] Failed to start App: {ex.Message}"); - return false; - } - } - else - { - return OpenFile(path, line); - } - } + if (line <= 0) line = 1; + + if (!IsRunnigInNeovim) + { + CleanupSocket(); - public bool OpenFile(string filePath, int line) + try { - try + if (OS == "Windows") + { + var psi = new ProcessStartInfo { - string cmd = $"e +{line} {filePath}"; - string nvimArgs = $"--server {Socket} --remote-send \"{cmd}\""; - string nvimPath = Utils.GetNeovimPath(); - - var psi = new ProcessStartInfo - { - FileName = nvimPath, - Arguments = nvimArgs, - UseShellExecute = false, - CreateNoWindow = true, - }; - - Process.Start(nvimPath, nvimArgs); - return true; - } - catch (Exception ex) - { - UnityEngine.Debug.LogError($"[NvimUnity] Failed to start App: {ex.Message}"); - return false; - } + FileName = defaultApp, + Arguments = $"{path} {line}", + UseShellExecute = true, + CreateNoWindow = false, + }; + + if (debugging) + UnityEngine.Debug.Log($"[NvimUnity] Executing: {psi.FileName} {psi.Arguments}"); + Process.Start(defaultApp, $"{path} {line}"); + } + else + { + // Original behavior for other OSes + ProcessStartInfo psi = Utils.BuildProcessStartInfo(defaultApp, path, line); + if (debugging) + UnityEngine.Debug.Log($"[NvimUnity] Executing in terminal: {psi.FileName} {psi.Arguments}"); + Process.Start(psi); + } + return true; + } + catch (Exception ex) + { + UnityEngine.Debug.LogError($"[NvimUnity] Failed to start App: {ex.Message}"); + return false; } + } + else + { + return OpenFile(path, line); + } + } + + public bool OpenFile(string filePath, int line) + { + try + { + string cmd = $"e +{line} {filePath}"; + string nvimArgs = $"--server {Socket} --remote-send \"{cmd}\""; + string nvimPath = Utils.GetNeovimPath(); - public void OnGUI() + var psi = new ProcessStartInfo { - GUILayout.Space(10); + FileName = nvimPath, + Arguments = nvimArgs, + UseShellExecute = false, + CreateNoWindow = true, + }; - EditorGUILayout.BeginHorizontal(); + Process.Start(nvimPath, nvimArgs); + return true; + } + catch (Exception ex) + { + UnityEngine.Debug.LogError($"[NvimUnity] Failed to start App: {ex.Message}"); + return false; + } + } - GUILayout.Label("Project Files", EditorStyles.boldLabel); + public void OnGUI() + { + GUILayout.Space(10); - if (GUILayout.Button("Regenerate project files")) - { - SyncAll(); - } + EditorGUILayout.BeginHorizontal(); - EditorGUILayout.EndHorizontal(); + GUILayout.Label("Project Files", EditorStyles.boldLabel); - GUILayout.Space(10); - } + if (GUILayout.Button("Regenerate project files")) + { + SyncAll(); + } - public void Initialize(string editorInstallationPath) - { - // Not used by NvimUnity, but required by interface - } - - public CodeEditor.Installation[] Installations => new[] - { + EditorGUILayout.EndHorizontal(); + + GUILayout.Space(10); + } + + public void Initialize(string editorInstallationPath) + { + // Not used by NvimUnity, but required by interface + } + + public CodeEditor.Installation[] Installations => new[] + { new CodeEditor.Installation { Name = EditorName, @@ -161,65 +209,65 @@ public void Initialize(string editorInstallationPath) } }; - public void SyncAll() - { - AssetDatabase.Refresh(); - Project.GenerateAll(); - } - - public void SyncIfNeeded(string[] addedFiles, string[] deletedFiles, string[] movedFiles, string[] movedFromFiles, string[] importedFiles) - { - if (!Project.Exists()) - { - SyncAll(); - return; - } - - if (Project.HasFilesBeenDeletedOrMoved()) - { - Project.GenerateCompileIncludes(); - return; - } - - var fileList = addedFiles.Concat(importedFiles); - - bool hasCsInAssets = - fileList.Any(path => - path.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) && - Utils.IsInAssetsFolder(path)); + public void SyncAll() + { + AssetDatabase.Refresh(); + Project.GenerateAll(); + } - if (hasCsInAssets) - { - if (Project.NeedRegenerateCompileIncludes(fileList.ToList())) - Project.GenerateCompileIncludes(); - } - } + public void SyncIfNeeded(string[] addedFiles, string[] deletedFiles, string[] movedFiles, string[] movedFromFiles, string[] importedFiles) + { + if (!Project.Exists()) + { + SyncAll(); + return; + } + + if (Project.HasFilesBeenDeletedOrMoved()) + { + Project.GenerateCompileIncludes(); + return; + } + + var fileList = addedFiles.Concat(importedFiles); + + bool hasCsInAssets = + fileList.Any(path => + path.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) && + Utils.IsInAssetsFolder(path)); + + if (hasCsInAssets) + { + if (Project.NeedRegenerateCompileIncludes(fileList.ToList())) + Project.GenerateCompileIncludes(); + } + } - public bool TryGetInstallationForPath(string path, out CodeEditor.Installation installation) + public bool TryGetInstallationForPath(string path, out CodeEditor.Installation installation) + { + if (path == Utils.GetLauncherPath()) + { + installation = new CodeEditor.Installation { - if (path == Utils.GetLauncherPath()) - { - installation = new CodeEditor.Installation - { - Name = EditorName, - Path = Utils.GetLauncherPath() - }; - return true; - } + Name = EditorName, + Path = Utils.GetLauncherPath() + }; + return true; + } - installation = default; - return false; - } + installation = default; + return false; + } - public void Save() - { - if (needSaveConfig) - { - ConfigManager.SaveConfig(config); - needSaveConfig = false; - } - } + public void Save() + { + if (needSaveConfig) + { + ConfigManager.SaveConfig(config); + needSaveConfig = false; + } } + } } diff --git a/Editor/Utils.cs b/Editor/Utils.cs index 90d26e7..1a6e59d 100644 --- a/Editor/Utils.cs +++ b/Editor/Utils.cs @@ -9,80 +9,80 @@ namespace NvimUnity { - public static class Utils + public static class Utils + { + public static string GetCurrentOS() { - public static string GetCurrentOS() - { #if UNITY_EDITOR_WIN return "Windows"; #elif UNITY_EDITOR_OSX return "OSX"; #else - return "Linux"; + return "Linux"; #endif - } + } - public static string GetUnityInstallRoot() - { - string editorExe = EditorApplication.applicationPath; - string editorDir = Path.GetDirectoryName(editorExe); - string root = Path.GetDirectoryName(editorDir); // Up in editor folder - return root; - } + public static string GetUnityInstallRoot() + { + string editorExe = EditorApplication.applicationPath; + string editorDir = Path.GetDirectoryName(editorExe); + string root = Path.GetDirectoryName(editorDir); // Up in editor folder + return root; + } - public static string GetProjectRoot() - { - return Path.GetDirectoryName(Application.dataPath); - } + public static string GetProjectRoot() + { + return Path.GetDirectoryName(Application.dataPath); + } - public static string NormalizePath(string path) - { + public static string NormalizePath(string path) + { #if UNITY_EDITOR_WIN return path.Replace("/", "\\"); #else - return path.Replace("\\", "/"); + return path.Replace("\\", "/"); #endif - } + } - public static bool IsInAssetsFolder(string path) - { - return path.Replace('\\', '/').Contains("Assets/"); - } + public static bool IsInAssetsFolder(string path) + { + return path.Replace('\\', '/').Contains("Assets/"); + } - //-------------- Launcher -------------- + //-------------- Launcher -------------- - public static string GetNeovimPath() - { + public static string GetNeovimPath() + { #if UNITY_EDITOR_WIN string path = @"C:\Program Files\Neovim\bin\nvim.exe"; if (File.Exists(path)) return path; return "nvim"; #else - string[] possiblePaths = new[] - { + string[] possiblePaths = new[] + { "/usr/bin/nvim", "/usr/local/bin/nvim", // Usual in Intel macOS e Linux "/opt/homebrew/bin/nvim", // Apple Silicon (M1/M2) "/snap/bin/nvim" // Linux Snap }; - foreach (var p in possiblePaths) - { - if (File.Exists(p)) - return p; - } + foreach (var p in possiblePaths) + { + if (File.Exists(p)) + return p; + } - return "nvim"; // PATH fallback + return "nvim"; // PATH fallback #endif - } + } - public static string GetLauncherPath() - { - string launcherPath = Environment.GetEnvironmentVariable("NVIMUNITY_PATH"); + public static string GetLauncherPath() + { + string launcherPath = Environment.GetEnvironmentVariable("NVIMUNITY_PATH"); - if (string.IsNullOrEmpty(launcherPath)) - { + if (string.IsNullOrEmpty(launcherPath)) + { #if UNITY_EDITOR_WIN // Windows fallbacks string[] fallbackPaths = new[] @@ -94,9 +94,9 @@ public static string GetLauncherPath() return fallbackPaths.FirstOrDefault(File.Exists); #else - // Linux/macOS fallbacks - string[] fallbackPaths = new[] - { + // Linux/macOS fallbacks + string[] fallbackPaths = new[] + { "/usr/bin/nvimunity", "/usr/local/bin/nvimunity", "/opt/nvimunity/nvimunity.sh", @@ -105,60 +105,60 @@ public static string GetLauncherPath() "/Applications/NvimUnity.app/Contents/MacOS/nvimunity" }; - return fallbackPaths.FirstOrDefault(File.Exists); + return fallbackPaths.FirstOrDefault(File.Exists); #endif - } + } #if UNITY_EDITOR_WIN return Path.Combine(launcherPath, "NvimUnity.exe"); #else - return launcherPath; + return launcherPath; #endif - } + } - public static void EnsureLauncherExecutable() - { + public static void EnsureLauncherExecutable() + { #if !UNITY_EDITOR_WIN - try - { - string path = GetLauncherPath(); - if (File.Exists(path)) - { - Process.Start(new ProcessStartInfo - { - FileName = "/bin/chmod", - Arguments = $"+x \"{path}\"", - UseShellExecute = false, - CreateNoWindow = true - }); - } - } - catch (Exception e) - { - UnityEngine.Debug.LogWarning("[NvimUnity] Failed to chmod launcher: " + e.Message); - } -#endif + try + { + string path = GetLauncherPath(); + if (File.Exists(path)) + { + Process.Start(new ProcessStartInfo + { + FileName = "/bin/chmod", + Arguments = $"+x \"{path}\"", + UseShellExecute = false, + CreateNoWindow = true + }); } + } + catch (Exception e) + { + UnityEngine.Debug.LogWarning("[NvimUnity] Failed to chmod launcher: " + e.Message); + } +#endif + } - public static ProcessStartInfo BuildProcessStartInfo(string defaultApp, string path, int line) - { + public static ProcessStartInfo BuildProcessStartInfo(string defaultApp, string path, int line) + { #if !UNITY_EDITOR_WIN - string preferredTerminal = NeovimPreferences.GetPreferredTerminal(); + string preferredTerminal = NeovimPreferences.GetPreferredTerminal(); - string fileName = "/usr/bin/env"; - string args = "echo 'No terminal emulator found!'; sleep 1"; + string fileName = "/usr/bin/env"; + string args = "echo 'No terminal emulator found!'; sleep 1"; - Dictionary terminals = NeovimPreferences.GetAvailableTerminals(); + Dictionary terminals = NeovimPreferences.GetAvailableTerminals(); - if (terminals.TryGetValue(preferredTerminal, out var cmdFormat) && - IsTerminalAvailable(preferredTerminal)) + if (terminals.TryGetValue(preferredTerminal, out var cmdFormat) && + IsTerminalAvailable(preferredTerminal)) #if UNITY_EDITOR_LINUX { fileName = preferredTerminal; if (cmdFormat == "") { - args = $"{defaultApp} {path} {line}"; + args = $"-e {defaultApp} {path} {line}"; } else { @@ -173,78 +173,77 @@ public static ProcessStartInfo BuildProcessStartInfo(string defaultApp, string p if (IsTerminalAvailable(t.Key)) { fileName = t.Key; - args = $"{defaultApp} {path} {line}"; + args = $"-e {defaultApp} {path} {line}"; break; } } } #else - { - if (cmdFormat == "") - { - fileName = preferredTerminal; - args = $"-e {defaultApp} {path} {line}"; - } - else - { - args = string.Format(cmdFormat, $"{defaultApp} {path} {line}"); - } - } + { + if (cmdFormat == "") + { + fileName = preferredTerminal; + args = $"{defaultApp} {path} {line}"; + } + else + { + args = string.Format(cmdFormat, $"{defaultApp} {path} {line}"); + } + } - else - { - foreach (var t in terminals) - { - if (IsTerminalAvailable(t.Key)) - { - args = string.Format(t.Value, $"{defaultApp} {path} {line}"); - } - } - } + else + { + foreach (var t in terminals) + { + if (IsTerminalAvailable(t.Key)) + { + args = string.Format(t.Value, $"{defaultApp} {path} {line}"); + } + } + } #endif - return new ProcessStartInfo - { - FileName = fileName, - Arguments = args, - UseShellExecute = true, - CreateNoWindow = false - }; + return new ProcessStartInfo + { + FileName = fileName, + Arguments = args, + UseShellExecute = true, + CreateNoWindow = false + }; #else return null; #endif - } + } - public static bool IsTerminalAvailable(string terminalName) - { + public static bool IsTerminalAvailable(string terminalName) + { #if !UNITY_EDITOR_WIN - UnityEngine.Debug.Log($"[NvimUnity] Checking if terminal is available: {terminalName}"); - try - { - var psi = new ProcessStartInfo - { - FileName = "which", - Arguments = terminalName, - RedirectStandardOutput = true, - UseShellExecute = false, - CreateNoWindow = true - }; - - UnityEngine.Debug.Log($"[NvimUnity] Executing: {psi.FileName} {psi.Arguments}"); - using (var process = Process.Start(psi)) - { - process.WaitForExit(500); - return process.ExitCode == 0; - } - } - catch - { - return false; - } + try + { + var psi = new ProcessStartInfo + { + FileName = "which", + Arguments = terminalName, + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + UnityEngine.Debug.Log($"[NvimUnity] Executing: {psi.FileName} {psi.Arguments}"); + using (var process = Process.Start(psi)) + { + process.WaitForExit(500); + return process.ExitCode == 0; + } + } + catch + { + return false; + } #else return true; #endif - } } + } }