Skip to content

Commit 69f697d

Browse files
committed
fix(debounce): fix small buf debounce issue, add more docs
1 parent 6134070 commit 69f697d

1 file changed

Lines changed: 65 additions & 48 deletions

File tree

lua/fuzzy/picker.lua

Lines changed: 65 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,59 @@ function Picker:_compute_headers(headers, actions)
573573
return vim.list_extend(headers, action_headers)
574574
end
575575

576+
function Picker:_matching_worker(mode, list, match_query, total, __type)
577+
if __type == 1 then
578+
if self._matching_worker_debounced == nil then
579+
self._matching_worker_debounced = utils.debounce_callback(
580+
self._options.match_debounce, self._matching_worker
581+
)
582+
end
583+
self._matching_worker_debounced(
584+
self, mode, list,
585+
match_query, total
586+
)
587+
else
588+
-- since this call is sometimes debounced we want to minimize the chance of doing extra work, in case the query has
589+
-- changed in between the call of this callback and it actually execting, we can skip it, the finalization above
590+
-- will ensure that the check should_match will trigger the final matching if needed.
591+
local current = assert(self.select:query())
592+
if #current == 0 or current ~= match_query then
593+
return
594+
end
595+
-- start a new matcher with a non-empty query, against the target list that list will either be the entire stream
596+
-- accumulated so far, or just the latest chunk from the stream,
597+
self.match:match(list, match_query, function(matching)
598+
if matching == nil then
599+
if not self.match:isvalid() or self.match:isempty() then
600+
local matching_state = self._state.matching or {}
601+
local accum = matching_state and matching_state.accum
602+
if not accum or #accum[1] == 0 then
603+
self.select:list(
604+
utils.EMPTY_TABLE,
605+
utils.EMPTY_TABLE
606+
)
607+
self.select:status("0/0")
608+
self.select:list(nil, nil)
609+
end
610+
end
611+
else
612+
-- the running match will ensure to reset the state internally in case the query was any different than
613+
-- it is currently stored with the matching state, the matching argument here will either be all results
614+
-- or the current streaming buffer based on the call site of this callback
615+
if assert(mode) == "all" then self:_running_match() end
616+
matching = self:_running_match(matching, match_query)
617+
self.select:list(matching[1], matching[2])
618+
self.select:status(string.format(
619+
"%d/%d", #matching[1], total
620+
))
621+
end
622+
end, self._state.display)
623+
end
624+
end
625+
626+
-- match_state.workerd = utils.debounce_callback(
627+
-- self._options.match_debounce, match_state.worker)
628+
576629
function Picker:_running_match(matching, query)
577630
-- Maintain a running accumulator of match results while a stream is active; it is a
578631
-- {matches, positions, scores} triplet that grows per chunk, resets on query change,
@@ -630,10 +683,6 @@ function Picker:_running_match(matching, query)
630683
debounced = nil,
631684
}
632685
end
633-
if state.matching.query ~= query then
634-
self:_running_match()
635-
state.matching = {}
636-
end
637686
local match_state = state.matching
638687
match_state.query = assert(query)
639688

@@ -801,52 +850,20 @@ function Picker:_flush_direct()
801850
local query = self.select:query()
802851

803852
if #all > 0 and type(query) == "string" and #query > 0 then
804-
if not self._state.matching then
805-
self._state.matching = {}
806-
end
807-
local match_state = self._state.matching
808-
if match_state.debounced == nil then
809-
match_state.debounced = utils.debounce_callback(
810-
self._options.match_debounce,
811-
function(mode, list, match_query, total)
812-
local current = self.select:query()
813-
if current ~= match_query or current == "" then
814-
return
815-
end
816-
self.match:match(list, match_query, function(matching)
817-
if matching == nil then
818-
if not self.match:isvalid() or self.match:isempty() then
819-
local current_state = self._state.matching
820-
local accum = current_state and current_state.accum
821-
if not accum or #accum[1] == 0 then
822-
self.select:list(
823-
utils.EMPTY_TABLE,
824-
utils.EMPTY_TABLE
825-
)
826-
self.select:status("0/0")
827-
self.select:list(nil, nil)
828-
end
829-
end
830-
else
831-
if mode == "all" then self:_running_match() end
832-
matching = self:_running_match(matching, match_query)
833-
self.select:list(matching[1], matching[2])
834-
self.select:status(string.format(
835-
"%d/%d", #matching[1], total
836-
))
837-
end
838-
end, self._state.display)
839-
end
840-
)
841-
end
842-
843853
-- If the query changed we have to do a match on all stream entries thus far, to reflect the matching state of these entries
844-
-- while the stream is still emitting
845-
local query_changed = not match_state or match_state.query ~= query
846-
if query_changed and #all > 0 then
847-
match_state.debounced("all", all, query, #all)
854+
-- while the stream is still emitting. We have two options here the query changed and we have to match on the entire set of
855+
-- results, or keep matching the incoming chunks of the stream and merge with current running match results, since the query
856+
-- never changed
857+
local match_all = not self._state.matching
858+
or self._state.matching.query ~= query
859+
if match_all and #all > 0 then
860+
-- debounce call, match onto all stream results, it is safe to debounce this call because we always have access to the latest
861+
-- accumulated results
862+
self:_matching_worker("all", all, query, #all, 1)
848863
elseif buf and #buf > 0 then
849-
match_state.debounced("buf", buf, query, #all)
864+
-- directly call non-debounced accumulate match worker for the current buffer, it is not safe to debounce this call as
865+
-- the buffer we receive here will no longer be available and and we can not afford to skip it
866+
self:_matching_worker("buf", buf, query, #all, 0)
850867
end
851868
else
852869
-- No query, render all of the results as-is and reset any running accumulator, as it will have become

0 commit comments

Comments
 (0)