261020.15 · ee1d77b5e7d5ac3b2661ffddb6243a7b477370fa
feat(win): spawn fzf via uv.spawn on a single route (CLI + TUI)
Windows fzf launches ran through `cmd.exe /c` with a single assembled command line, so shell escaping had to survive two layers (nvim -> cmd), user fragments needed caret escaping, and command lines over cmd.exe's 8191-char limit fell back to a PowerShell `ProcessStartInfo` script with its own quoting rules. The fallback also owned the producer pump and the result-file redirect, so quoting and lifecycle bugs only reproduced on one of the two paths.
Replace both with one managed route: `libuv.win_spawn` runs fzf with the raw argv (`_cli_segments`; no shell, no escaping, no command line limit), redirects stdout through the result file descriptor and pumps an optional producer (`cmd.exe /c <cmd>`) into fzf's stdin -- every chunk forwarded as-is, immediately, with backpressure (4MB/1MB watermarks). When fzf exits, the producer's process tree is reaped via `taskkill /PID /T /F`, which also covers producers blocked writing into a dead stdin.
TUI hosts (`utils.termopen`) need fzf to own the terminal buffer's pty: `win_spawn.lua`, a mediator nvim attached to that pty (`-u NONE -l`, like the existing `spawn.lua` producers), reads the serialized spec from a temp file (deleted right away), runs the same spawn route and exits with fzf's code. The CLI host (`scripts/cli.lua`) calls `libuv.win_spawn` in-process with a scheduled `on_exit`, keeping stdio like a bare `fzf`.
User fragments are cmd-ready strings (`shellescape` caret style), so `libuv.cmd_argv` reproduces what `cmd.exe /c` plus the Win32 argv parser would deliver (caret consumption, then quoting rules) and replaces `shellparse` on Windows -- existing configs keep working, including `--bind=load:...` payloads built by `stringify_data` (regression tests).
Removed: the PowerShell fallback and its BOM strip, `libuv.ps_escape`, the `escape_cli` "powershell" variant and the fzf_spec tests for the fallback. Note: fragments are now data, exactly like on posix -- `cmd` operators in user options are no longer interpreted by an outer shell (caret-escaped fragments are unaffected; values are still expanded by any shell fzf itself invokes).
Tests: `win_spawn_spec` (byte-exact pump/argv quoting, exit codes, 4.8MB backpressure, mediator round-trip, producer tree-kill on early exit), `libuv_spec` (`cmd_argv` round-trips, callback payload), `fzf_spec` (route wiring for both hosts), `api_spec` repro case.
reading declarations
Changes to scripts/cli.lua. All files
@@ -54,12 +54,26 @@ end _G.fzf_jobstart = function(cmd, opts) if fn.has("win32") == 1 or fn.has("win64") == 1 then- -- Inherit stdio like bare `fzf` (works on any terminal: conhost,- -- Windows Terminal, tmux pane). Join argv verbatim for the `cmd.exe`- -- launcher: the caret-escaped args from `fzf.raw_fzf` are built for- -- raw `cmd /c` parsing and libuv quoting corrupts them ("...syntax- -- is incorrect"). The PowerShell fallback (>8000 chars) keeps- -- default quoting.+ -- One managed Windows route: `fzf.raw_fzf` hands us a `win_spawn` spec+ -- (raw argv, result file, optional item command); `libuv.win_spawn`+ -- runs fzf directly via `uv.spawn` -- no shell, no escaping, no+ -- PowerShell fallback, results via file descriptor and the item+ -- command piped into fzf's stdin (see `libuv.win_spawn`).+ if cmd.__win_spawn then+ FzfLua.libuv.win_spawn(cmd.__win_spawn, {+ on_exit = vim.schedule_wrap(function(rc)+ opts.on_exit(nil, rc, nil)+ if not opts.no_quit and #api.nvim_get_proc_children(fn.getpid()) == 0 then+ vim.cmd.cquit { count = rc, bang = true }+ end+ end),+ })+ return+ end+ -- Legacy `cmd.exe` launcher for callers without raw segments (direct+ -- `raw_fzf` invocations). Join argv verbatim for `cmd.exe`: the+ -- caret-escaped args from `fzf.raw_fzf` are built for raw `cmd /c`+ -- parsing and libuv quoting corrupts them ("...syntax is incorrect"). -- stderr: fzf's light renderer (`--height`) requires a console on -- stderr (it draws the UI there and falls back to fullscreen without -- one, see `IsLightRendererSupported` in src/tui/light_windows.go).