@ 261105.2

261020.22 · c8198e52b0c261dd1fe788962037c4910237a2c5

fix(win): stop the mediator's default pipe (serverlist pollution)

Every Windows nvim listens by default (`\\.\pipe\nvim.<pid>.<n>`), so the headless `win_spawn.lua` mediator - alive for the whole picker - was listed as a server by the `serverlist` picker (the discovery globs the pipe namespace, `providers/nvim.lua`). Its sibling scripts (`spawn.lua`, `rpc.lua`, `scripts/cli.lua`) already stop their default listener at startup; `win_spawn.lua` (added in 25d9e551) missed that guard.

- stop the default listener in `win_spawn.lua` (same `serverstop` guard)
- update the `providers/nvim.lua` discovery comment: our headless spawns
never register a pipe; third-party UI-less instances (`--embed`) are
still deliberately listed (filtering them emptied the picker for
embed-based workflows, see the note there)
- new `win_spawn_spec` case spawns the real mediator (kept alive via a
`ping` spec) and asserts no `\\.\pipe\nvim*` appears while it runs;
verified failing before the fix ("mediator left a listener:
//./pipe/nvim.8476.0") and passing after

The transient `previewer/fzf.lua` `--remote-ui` screenshot client is left as is: it lives only for a preview dump and a `--cmd` guard proved fragile (argument splitting); not observed in the picker.

Verification: full nightly 315/0 fails/19 notes; full stable 315/0/19; focused {win_spawn} 6/0, {win} 65/0, {serverlist,headless,fzf} 33/0; emmylua_check clean.

reading declarations

lua/fzf-lua/providers/nvim.lua

@@ -807,7 +807,10 @@ end ---no peer candidates; every address it reports is instead excluded below in ---all slash forms, so none of our own sockets/pipes can be listed. Entries ---failing an RPC probe, ourselves and our own `fzf-lua*` infra are skipped----everywhere; every other responsive listener is listed, including+---everywhere; our headless spawns (`scripts/cli.lua`, `spawn.lua`,+---`rpc.lua`, `win_spawn.lua`) additionally stop their default listener+---(`serverstop`), so they never register a pipe at all (every Windows nvim+---listens by default); every other responsive listener is listed, including ---UI-less `--embed` backends (indistinguishable from detached user ---sessions). Addresses stay in native ---form; `utils.serverlist_parse_entry` repairs the single backslash

lua/fzf-lua/win_spawn.lua

@@ -8,6 +8,15 @@ -- the terminal job's exit code (see `fzf.raw_fzf`). assert(#vim.api.nvim_list_uis() == 0) +-- every Windows nvim listens by default (`\\.\pipe\nvim.<pid>.<n>`); the+-- mediator doesn't need the server and its pipe would be listed as a+-- server in the `serverlist` picker (`providers/nvim.lua` discovery globs+-- that namespace), stopping the server removes the pipe (same as+-- `spawn.lua`/`rpc.lua`/`scripts/cli.lua`)+if vim.v.servername and #vim.v.servername > 0 then+  pcall(vim.fn.serverstop, vim.v.servername)+end+ -- path to this file local _info = debug.getinfo(1, "S") ---@cast _info {source: string}

tests/win_spawn_spec.lua

@@ -200,4 +200,51 @@ describe("Testing win_spawn (Windows one route)", function()     eq(out, "")     vim.fn.delete(infile)   end)++  it("mediator does not leave a default listener (serverlist pollution)", function()+    helpers.SKIP_IF_NOT_WIN()+    ---@diagnostic disable-next-line: deprecated+    local _info = debug.getinfo(1, "S")+    ---@cast _info {source: string}+    local root = vim.fn.fnamemodify(_info.source:gsub("^@", ""), ":h:h")+    local script = (root .. "/lua/fzf-lua/win_spawn.lua"):gsub("\\", "/")++    local outfile = utils.tempname()+    local spec_path = utils.tempname()+    -- `ping` keeps the mediator alive for a few seconds (fzf exits too fast+    -- to sample the pipe namespace reliably)+    write_file(spec_path, libuv.serialize({+      bin = vim.fn.exepath("ping"),+      args = { "-n", "6", "127.0.0.1" },+      outfile = outfile,+      env = { SHELL = "cmd.exe" },+    }, true))++    local seen = utils.list_to_map(vim.fn.glob([[\\.\pipe\nvim*]], false, true))+    local rc ---@type integer?+    local handle = uv.spawn((vim.v.progpath):gsub("\\", "/"), {+      args = { "-u", "NONE", "-l", script, (spec_path:gsub("\\", "/")) },+      stdio = { nil, nil, nil },+    }, function(code, _)+      rc = code+    end)+    assert.is.True(handle ~= nil)+    -- sample while the mediator runs: every Windows nvim listens by default+    -- on `\\.\pipe\nvim.<pid>.<n>`, so without a `serverstop` guard the+    -- mediator registers a listener and the `serverlist` picker lists it+    -- (`providers/nvim.lua` discovery globs this namespace)+    local leaked = {}+    for _ = 1, 3 do+      vim.wait(900)+      for _, p in ipairs(vim.fn.glob([[\\.\pipe\nvim*]], false, true)) do+        if not seen[p] then leaked[#leaked + 1] = p end+      end+    end+    vim.wait(30000, function() return rc ~= nil end)+    vim.fn.delete(outfile)+    eq(rc, 0)+    if #leaked > 0 then+      error("mediator left a listener: " .. table.concat(leaked, ", "))+    end+  end) end)