Skip to content

Commit df27387

Browse files
committed
refactor(libuv.spawn): simplify finish conditions
1 parent e169bf1 commit df27387

1 file changed

Lines changed: 41 additions & 38 deletions

File tree

lua/fzf-lua/libuv.lua

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ M.spawn = function(opts, fn_transform, fn_done)
5757
local EOL = opts.EOL or "\n"
5858
local output_pipe = assert(uv.new_pipe(false))
5959
local error_pipe = assert(uv.new_pipe(false))
60-
local write_cb_count, read_cb_count, on_exit_called = 0, 0, nil
60+
local write_cb_count, read_cb_count = 0, 0
6161
local prev_line_content = nil
6262
local handle, pid
6363
local co = coroutine.running()
@@ -71,6 +71,15 @@ M.spawn = function(opts, fn_transform, fn_done)
7171
-- cb_write_lines trumps cb_write
7272
if opts.cb_write_lines then opts.cb_write = opts.cb_write_lines end
7373

74+
local can_finish = function()
75+
if not output_pipe:is_active() -- EOF signalled or process is aborting
76+
and read_cb_count == 0 -- no outstanding read_cb data processing
77+
and write_cb_count == 0 -- no outstanding write callbacks
78+
then
79+
return true
80+
end
81+
end
82+
7483
---@diagnostic disable-next-line: redefined-local
7584
local finish = function(code, sig, from, pid)
7685
-- Uncomment to debug pipe closure timing issues (#1521)
@@ -130,19 +139,14 @@ M.spawn = function(opts, fn_transform, fn_done)
130139
end)(),
131140
verbatim = _is_win,
132141
}, function(code, signal)
133-
on_exit_called = true
134-
if write_cb_count == 0
135-
and read_cb_count == 0
136-
and not output_pipe:is_active()
137-
-- on_exit is called before queue processing of the last line (#2205)
138-
and (not opts.use_queue or queue:empty())
139-
then
142+
if can_finish() or code ~= 0 then
140143
-- Do not call `:read_stop` or `:close` here as we may have data
141144
-- reads outstanding on slower Windows machines (#1521), only call
142145
-- `finish` if all our `uv.write` calls are completed and the pipe
143146
-- is no longer active (i.e. no more read cb's expected)
144-
finish(code, signal, 1, pid)
147+
finish(code, signal, "[on_exit]", pid)
145148
end
149+
handle:close()
146150
end)
147151

148152
-- save current process pid
@@ -155,24 +159,20 @@ M.spawn = function(opts, fn_transform, fn_done)
155159
if err then
156160
-- can fail with premature process kill
157161
-- assert(not err)
158-
finish(130, 0, 2, pid)
159-
elseif write_cb_count == 0
160-
and read_cb_count == 0
161-
and on_exit_called
162-
and not output_pipe:is_active()
163-
then
164-
-- spawn callback already called and did not close the pipe
165-
-- due to write_cb_count>0, since this is the last call
166-
-- we can close the fzf pipe
167-
finish(0, 0, 3, pid)
162+
finish(130, 0, "[write_cb: err]", pid)
163+
elseif can_finish() then
164+
-- on_exit callback already called and did not close the
165+
-- pipe due to write_cb_count>0, since this is the last
166+
-- call we can close the fzf pipe
167+
finish(0, 0, "[write_cb: finish]", pid)
168168
end
169169
end)
170170
end
171171

172172
---@param data string data stream
173173
---@param prev string? rest of line from previous call
174174
---@param trans function? line transformation function
175-
---@return table, string line array, partial last line (no EOL)
175+
---@return table, string? line array, partial last line (no EOL)
176176
local function split_lines(data, prev, trans)
177177
local ret = {}
178178
local start_idx = 1
@@ -208,13 +208,11 @@ M.spawn = function(opts, fn_transform, fn_done)
208208
local process_data = function(data)
209209
data = data or prev_line_content and (prev_line_content .. EOL) or nil
210210
if not data then
211-
-- https://github.com/LazyVim/LazyVim/discussions/5264
212-
-- The pipe can remain active *after* on_exit was called
213-
if write_cb_count == 0
214-
and read_cb_count == 0
215-
and on_exit_called
216-
then
217-
finish(0, 0, 5, pid)
211+
-- NOTE: this isn't called when prev_line_content is not nil but that's
212+
-- not a problem as the write_cb will call finish once the callback is done
213+
-- since the output_pipe is already in "closing" state
214+
if can_finish() then
215+
finish(0, 0, "[EOF]", pid)
218216
end
219217
return
220218
end
@@ -267,33 +265,34 @@ M.spawn = function(opts, fn_transform, fn_done)
267265

268266
local read_cb = function(err, data)
269267
if err then
270-
finish(130, 0, 4, pid)
268+
finish(130, 0, "[read_cb: err]", pid)
271269
return
272270
end
271+
if not data then
272+
-- EOF signalled, we can close the pipe
273+
output_pipe:close()
274+
end
273275
if opts.use_queue then
274-
if data then
275-
queue:push(data)
276-
elseif prev_line_content then
277-
queue:push(prev_line_content .. EOL)
278-
prev_line_content = nil
279-
end
276+
if data then queue:push(data) end
277+
-- Either we have outstanding data enqueued or the pipe is closing
278+
-- due to the above `output_pipe:close`, in both cases we need to
279+
-- resume the dequeue loop
280280
coroutine.resume(co)
281281
else
282-
-- Schedule data processing, will call finish if data is nil
283-
-- and no leftover data is present
284282
read_cb_count = read_cb_count + 1
285283
local process = function()
286284
read_cb_count = read_cb_count - 1
287285
process_data(data)
288286
end
289-
-- Avoid "attempt to yield across C-call boundary" by using vim.schedule
287+
-- Schedule data processing if we're in fast event
288+
-- avoids "attempt to yield across C-call boundary" by using vim.schedule
290289
if vim.in_fast_event() then vim.schedule(process) else process() end
291290
end
292291
end
293292

294293
local err_cb = function(err, data)
295294
if err then
296-
finish(130, 0, 9, pid)
295+
finish(130, 0, "[err_cb]", pid)
297296
end
298297
if not data then
299298
return
@@ -324,6 +323,10 @@ M.spawn = function(opts, fn_transform, fn_done)
324323
process_data(queue:pop())
325324
end
326325
end
326+
-- process the leftover line from `processs_data`
327+
-- will call `finish` immediately if there's no last line
328+
-- otherwise, finish is called in the write callback
329+
process_data(nil)
327330
end
328331

329332
return handle, pid

0 commit comments

Comments
 (0)