Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions bin/nvim-socket.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,41 @@ find_nvim_socket() {
local live_sockets=()
local socket pid

# 2a. Pidfile discovery — each running nvim that called code-preview.setup()
# registers itself at ${XDG_STATE_HOME:-$HOME/.local/state}/code-preview/sockets/<pid>.
# File contents: line 1 = socket path, line 2 = cwd. Cheaper and OS-portable
# vs the glob-based discovery below, which we keep as a fallback for nvim
# instances that haven't been restarted since the upgrade.
local _state_home="${XDG_STATE_HOME:-$HOME/.local/state}"
local _pidfile_dir="$_state_home/code-preview/sockets"
if [[ -d "$_pidfile_dir" ]]; then
local pidfile pf_socket pf_cwd
for pidfile in "$_pidfile_dir"/*; do
[[ -f "$pidfile" ]] || continue
pid="$(basename "$pidfile")"
[[ "$pid" =~ ^[0-9]+$ ]] || continue
{ IFS= read -r pf_socket; IFS= read -r pf_cwd; } < "$pidfile" || continue
[[ -n "$pf_socket" && -S "$pf_socket" ]] || continue

# Validate socket is responsive — self-heals stale pidfiles from crashed
# nvim where the PID may even have been recycled.
if ! nvim --server "$pf_socket" --remote-expr "1" >/dev/null 2>&1; then
continue
fi

# If project_cwd given, check match-or-parent rule using the cwd the
# nvim itself reported (more accurate than lsof, which gives startup cwd).
if [[ -n "$project_cwd" && -n "$pf_cwd" ]]; then
if [[ "$project_cwd" == "$pf_cwd" || "$project_cwd" == "$pf_cwd/"* ]]; then
eval "$_oldopts"
echo "$pf_socket"
return 0
fi
fi
live_sockets+=("$pid:$pf_socket")
done
fi

# 2. Scan macOS /var/folders paths
local _glob_out
_glob_out="$(compgen -G '/var/folders/*/*/T/nvim.*/*/nvim.*.0' 2>/dev/null)" || true
Expand Down
17 changes: 17 additions & 0 deletions lua/code-preview/health.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,23 @@ function M.check()
local layout = (cfg.diff and cfg.diff.layout) or "unknown"
ok("Diff layout: " .. layout)

-- Pidfile registration — used by hook scripts to find this nvim's socket
-- without OS-specific socket-glob discovery.
local pidfile = require("code-preview.pidfile").path()
local pf = io.open(pidfile, "r")
if not pf then
warn("Pidfile not found at " .. pidfile .. " (hook scripts will fall back to socket discovery)")
else
local pf_socket = pf:read("*l") or ""
local pf_cwd = pf:read("*l") or ""
pf:close()
if pf_socket == "" or pf_cwd == "" then
warn("Pidfile " .. pidfile .. " is malformed (expected socket+cwd on two lines)")
else
ok("Pidfile registered: " .. pidfile)
end
end

-- ── Claude Code backend ───────────────────────────────────────

start("Claude Code backend")
Expand Down
11 changes: 11 additions & 0 deletions lua/code-preview/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ function M.setup(user_config)
-- Initialise logging
require("code-preview.log").init({ debug = M.config.debug })

-- Self-register socket + cwd for hook-script discovery
require("code-preview.pidfile").setup()

-- ── New commands ──────────────────────────────────────────────

vim.api.nvim_create_user_command("CodePreviewInstallClaudeCodeHooks", function()
Expand Down Expand Up @@ -226,6 +229,14 @@ function M.status()
table.insert(lines, "Neovim socket : not found")
end

-- Pidfile (used by hook scripts for socket discovery)
local pidfile = require("code-preview.pidfile").path()
if vim.fn.filereadable(pidfile) == 1 then
table.insert(lines, "Pidfile : " .. pidfile)
else
table.insert(lines, "Pidfile : not registered")
end

-- jq dependency
local jq_ok = vim.fn.executable("jq") == 1
table.insert(lines, "jq : " .. (jq_ok and "found" or "MISSING"))
Expand Down
69 changes: 69 additions & 0 deletions lua/code-preview/pidfile.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
-- pidfile.lua — Self-registers this nvim's RPC socket + cwd so the hook
-- scripts can find it without OS-specific socket-glob discovery.
--
-- File format (two lines):
-- <socket path>
-- <cwd>
--
-- Location: ${XDG_STATE_HOME:-$HOME/.local/state}/code-preview/sockets/<pid>
-- The same path is computed by bin/nvim-socket.sh.

local M = {}

function M.dir()
local state = vim.env.XDG_STATE_HOME
if not state or state == "" then
state = (vim.env.HOME or "") .. "/.local/state"
end
return state .. "/code-preview/sockets"
end

function M.path()
return M.dir() .. "/" .. tostring(vim.fn.getpid())
end

local function write()
local socket = vim.v.servername or ""
if socket == "" then return end

vim.fn.mkdir(M.dir(), "p")

local f, err = io.open(M.path(), "w")
if not f then
require("code-preview.log").warn("pidfile: open failed: " .. tostring(err))
return
end
f:write(socket, "\n", vim.fn.getcwd(), "\n")
f:close()
end

local function remove()
pcall(os.remove, M.path())
end

function M.setup()
-- Initial write
pcall(write)

local group = vim.api.nvim_create_augroup("CodePreviewPidfile", { clear = true })

-- Refresh cwd line when the user :cd's so socket discovery stays accurate.
vim.api.nvim_create_autocmd("DirChanged", {
group = group,
callback = function() pcall(write) end,
})

-- Re-write if servername changes (rare, but possible via :let v:servername).
vim.api.nvim_create_autocmd("VimEnter", {
group = group,
callback = function() pcall(write) end,
})

-- Cleanup on exit
vim.api.nvim_create_autocmd("VimLeavePre", {
group = group,
callback = remove,
})
end

return M
Loading