diff --git a/nvim-core/src/lib.rs b/nvim-core/src/lib.rs index 6ee0edd..b25ac51 100644 --- a/nvim-core/src/lib.rs +++ b/nvim-core/src/lib.rs @@ -59,6 +59,28 @@ pub async fn send_error(id: Value, code: i32, message: &str) { .await; } +async fn is_interactive(socket_path: &str) -> bool { + // If it's our explicitly named headless fallback instance, skip the shell-out and reject it immediately + if socket_path.contains("agy-headless-nvim") { + return false; + } + + if let Ok(output) = tokio::process::Command::new("nvim") + .arg("--server") + .arg(socket_path) + .arg("--headless") + .arg("--remote-expr") + .arg("empty(nvim_list_uis())") + .output() + .await + { + let stdout = String::from_utf8_lossy(&output.stdout); + // empty(nvim_list_uis()) returns 1 if it IS headless/empty, and 0 if it has a UI + return stdout.trim() == "0"; + } + false +} + #[cfg(windows)] async fn get_socket_path() -> Result { let profile = @@ -83,7 +105,10 @@ async fn get_socket_path() -> Result { let name = entry.file_name(); let name_str = name.to_string_lossy(); if name_str.starts_with("nvim.") { - return Ok(format!(r"\\.\pipe\{name_str}")); + let candidate = format!(r"\\.\pipe\{name_str}"); + if is_interactive(&candidate).await { + return Ok(candidate); + } } } } @@ -108,7 +133,10 @@ async fn get_socket_path() -> Result { if name.starts_with("nvim.") { let path = entry.path().join("0"); if tokio::fs::try_exists(&path).await.unwrap_or(false) { - return Ok(path.to_string_lossy().to_string()); + let candidate = path.to_string_lossy().to_string(); + if is_interactive(&candidate).await { + return Ok(candidate); + } } } }