From d36ef1b6ac8012b160b0049c7ebcc241bbb57165 Mon Sep 17 00:00:00 2001 From: thuan do Date: Sun, 14 Jun 2026 12:22:59 +0700 Subject: [PATCH 1/7] feat: resume last picker in refactored picker_ui structure Add ability to resume the last closed picker with full state (query, results, cursor, mode) by adapting for the refactored module structure. In the coordinator (picker_ui.lua): - save_state_and_close() deep-copies picker state before closing - restore_from_state() recreates UI from saved snapshot - M.close() overrides the wired close to save state then call close_windows - M.resume(), M.resume_find_files(), M.resume_live_grep() public API - Per-mode saved state (find_files vs live_grep) In layout_manager.lua: - Extracted close_windows() as the low-level cleanup - close() now delegates to close_windows() In main.lua: - find_files and live_grep support opts.resume - M.resume() public API for :FFFResume command plugin/fff.lua: - :FFFResume user command --- lua/fff/main.lua | 43 +++++- lua/fff/picker_ui/layout_manager.lua | 78 ++++++++++- lua/fff/picker_ui/picker_ui.lua | 201 ++++++++++++++++++++++++++- plugin/fff.lua | 4 + 4 files changed, 315 insertions(+), 11 deletions(-) diff --git a/lua/fff/main.lua b/lua/fff/main.lua index 24241576..1b97382e 100644 --- a/lua/fff/main.lua +++ b/lua/fff/main.lua @@ -6,19 +6,29 @@ M.state = { initialized = false } --- @param config table Configuration options function M.setup(config) vim.g.fff = config end ---- Find files in current directory ---- @param opts? table Optional configuration {renderer = custom_renderer} +--- Find files in current directory. +--- When opts.resume is true, resumes the last find_files picker (or opens a new one if none saved). +--- @param opts? table Optional configuration {renderer = custom_renderer, resume = boolean} function M.find_files(opts) local picker_ok, picker_ui = pcall(require, 'fff.picker_ui.picker_ui') - if picker_ok then - picker_ui.open(opts) - else + if not picker_ok then vim.notify('Failed to load picker UI: ' .. picker_ui, vim.log.levels.ERROR) + return end + + if opts and opts.resume then + local cleaned = vim.deepcopy(opts) + cleaned.resume = nil + picker_ui.resume_find_files(cleaned) + return + end + + picker_ui.open(opts) end ---- Live grep: search file contents in the current directory ---- @param opts? {cwd?: string, title?: string, prompt?: string, layout?: table, grep?: {max_file_size?: number, smart_case?: boolean, max_matches_per_file?: number, modes?: string[]}, query?: string} Optional configuration overrides +--- Live grep: search file contents in the current directory. +--- When opts.resume is true, resumes the last live_grep picker (or opens a new one if none saved). +--- @param opts? {cwd?: string, title?: string, prompt?: string, layout?: table, grep?: {max_file_size?: number, smart_case?: boolean, max_matches_per_file?: number, modes?: string[]}, query?: string, resume?: boolean} Optional configuration overrides function M.live_grep(opts) local picker_ok, picker_ui = pcall(require, 'fff.picker_ui.picker_ui') if not picker_ok then @@ -26,6 +36,13 @@ function M.live_grep(opts) return end + if opts and opts.resume then + local cleaned = vim.deepcopy(opts) + cleaned.resume = nil + picker_ui.resume_live_grep(cleaned) + return + end + local config = require('fff.conf').get() local grep_renderer = require('fff.picker_ui.grep_renderer') @@ -427,6 +444,18 @@ end --- @return boolean `true` if successful, `false` otherwise function M.change_indexing_directory(new_path) return require('fff.core').change_indexing_directory(new_path) end +--- Resume the most recently closed picker (find_files or live_grep). +--- Similar to Telescope's `require('telescope.builtin').resume()`. +---@return boolean true if a picker was resumed, false if there is nothing to resume +function M.resume() + local picker_ok, picker_ui = pcall(require, 'fff.picker_ui.picker_ui') + if not picker_ok then + vim.notify('Failed to load picker UI: ' .. picker_ui, vim.log.levels.ERROR) + return false + end + return picker_ui.resume() +end + -- Strip wrapper punctuation that frequently surrounds paths in prose: leading -- markdown-link `[`, parens `(`, brackets `<`, quotes; trailing sentence -- punctuation. We additionally truncate at the first closing wrapper so a diff --git a/lua/fff/picker_ui/layout_manager.lua b/lua/fff/picker_ui/layout_manager.lua index f847f953..5ff8c9d4 100644 --- a/lua/fff/picker_ui/layout_manager.lua +++ b/lua/fff/picker_ui/layout_manager.lua @@ -19,6 +19,78 @@ local function restore_paste(should_restore) if should_restore then vim.o.paste = true end end +function M.close_windows() + if not P.state.active then return end + + vim.cmd('stopinsert') + P.state.active = false + + restore_paste(S.restore_paste) + + list_separator.cleanup() + scrollbar.cleanup() + + local ts_ok, ts_hl = pcall(require, 'fff.treesitter_hl') + if ts_ok then ts_hl.cleanup() end + + local windows = { S.input_win, S.list_win, S.preview_win } + if S.file_info_win then table.insert(windows, S.file_info_win) end + + for _, win in ipairs(windows) do + if win and vim.api.nvim_win_is_valid(win) then vim.api.nvim_win_close(win, true) end + end + + local buffers = { S.input_buf, S.list_buf, S.file_info_buf } + if S.preview_buf then buffers[#buffers + 1] = S.preview_buf end + + for _, buf in ipairs(buffers) do + if buf and vim.api.nvim_buf_is_valid(buf) then + vim.api.nvim_buf_clear_namespace(buf, -1, 0, -1) + if buf == S.preview_buf then preview.clear_buffer(buf) end + vim.api.nvim_buf_delete(buf, { force = true }) + end + end + + if S.preview_timer then + S.preview_timer:stop() + S.preview_timer:close() + S.preview_timer = nil + end + + S.input_win = nil + S.list_win = nil + S.file_info_win = nil + S.preview_win = nil + S.input_buf = nil + S.list_buf = nil + S.file_info_buf = nil + S.preview_buf = nil + S.preview_visible = false + S.items = {} + S.filtered_items = {} + S.cursor = 1 + S.query = '' + S.ns_id = nil + S.last_preview_file = nil + S.last_preview_location = nil + S.current_file_cache = nil + S.location = nil + S.selected_files = {} + S.selected_items = {} + S.mode = nil + S.grep_config = nil + S.grep_mode = 'plain' + S.grep_regex_fallback_error = nil + S.suggestion_items = nil + S.suggestion_source = nil + S.renderer = nil + S.restore_paste = false + S.combo_visible = true + S.combo_initial_cursor = nil + P.reset_history_state() + pcall(vim.api.nvim_del_augroup_by_name, 'fff_picker_focus') +end + function M.relayout() if not P.state.active then return end @@ -75,7 +147,7 @@ function M.relayout() P.update_status() end -function M.close() +function M.close_windows() if not P.state.active then return end vim.cmd('stopinsert') @@ -102,9 +174,7 @@ function M.close() for _, buf in ipairs(buffers) do if buf and vim.api.nvim_buf_is_valid(buf) then vim.api.nvim_buf_clear_namespace(buf, -1, 0, -1) - if buf == S.preview_buf then preview.clear_buffer(buf) end - vim.api.nvim_buf_delete(buf, { force = true }) end end @@ -148,4 +218,6 @@ function M.close() pcall(vim.api.nvim_del_augroup_by_name, 'fff_picker_focus') end +function M.close() M.close_windows() end + return M diff --git a/lua/fff/picker_ui/picker_ui.lua b/lua/fff/picker_ui/picker_ui.lua index fce04d8c..21e7de9b 100644 --- a/lua/fff/picker_ui/picker_ui.lua +++ b/lua/fff/picker_ui/picker_ui.lua @@ -83,7 +83,206 @@ M.scroll_to_bottom = renderer.scroll_to_bottom -- Wire layout_manager module (relayout, close) layout_manager.init(M) M.relayout = layout_manager.relayout -M.close = layout_manager.close +M.close_windows = layout_manager.close_windows + +-- Resume state: saved snapshots of closed pickers for the resume feature. +--- @type table|nil Saved state from last file picker (find_files) session +local last_file_picker_state = nil +--- @type table|nil Saved state from last grep session +local last_grep_picker_state = nil +--- @type string|nil 'files' or 'grep' — which mode was most recently closed +local last_closed_mode = nil + +--- Save the current picker state for later resume, then close. +local function save_state_and_close() + if M.state.query == '' then + -- Don't save empty state to avoid confusion on resume + M.close_windows() + return + end + if not M.state.active then return end + + -- Deep copy the full state to capture all data fields (future-proof). + -- Window/buffer handles are also copied but are ignored during restore since UI is recreated. + local snapshot = vim.deepcopy(M.state) + + -- Capture the base_path from the Rust file indexer (not part of M.state) + local fuzzy = require('fff.core').ensure_initialized() + local ok, base_path = pcall(fuzzy.get_base_path) + if ok and base_path then + snapshot.base_path = base_path + else + snapshot.base_path = M.state.config and M.state.config.base_path or nil + end + + -- Save to the mode-specific slot + if M.state.mode == 'grep' then + last_grep_picker_state = snapshot + last_closed_mode = 'grep' + else + last_file_picker_state = snapshot + last_closed_mode = 'files' + end + + M.close_windows() +end + +--- Internal: restore picker from a saved state snapshot. +---@param state table The saved state table +---@param source_label string Label for error messages +---@return boolean +local function restore_from_state(state, source_label) + -- Ensure the file picker is initialized + if not file_picker.is_initialized() then + if not file_picker.setup() then + vim.notify('Failed to initialize file picker', vim.log.levels.ERROR) + return false + end + end + + -- Restore the picker with the saved config and mode + M.state.renderer = state.renderer + M.state.mode = state.mode + M.state.grep_config = state.grep_config + M.state.grep_mode = state.grep_mode + M.state.selected_files = vim.deepcopy(state.selected_files or {}) + M.state.selected_items = vim.deepcopy(state.selected_items or {}) + + -- Restore the saved base_path for the indexer if it differs from the current CWD + if state.base_path then require('fff.core').change_indexing_directory(state.base_path) end + + -- Use the saved config directly to restore the exact picker state + M.state.config = state.config + + if not M.create_ui() then + vim.notify('FFF: failed to create picker UI for ' .. source_label, vim.log.levels.ERROR) + return false + end + + M.state.active = true + M.state.current_file_cache = state.current_file_cache + + -- Restore the full picker state + M.state.query = state.query + M.state.items = state.items or {} + M.state.filtered_items = state.filtered_items or {} + M.state.cursor = math.min(state.cursor or 1, #(state.filtered_items or {})) + M.state.cursor = math.max(M.state.cursor, 1) + M.state.location = state.location + M.state.pagination = vim.deepcopy(state.pagination or { + page_index = 0, + page_size = 20, + total_matched = 0, + prefetch_margin = 5, + grep_file_offsets = {}, + grep_next_file_offset = 0, + }) + M.state.combo_visible = state.combo_visible ~= false + M.state.combo_initial_cursor = state.combo_initial_cursor + M.state.suggestion_items = state.suggestion_items + M.state.suggestion_source = state.suggestion_source + + -- Set the query text in the input buffer + if state.query and state.query ~= '' then + vim.api.nvim_buf_set_lines(M.state.input_buf, 0, -1, false, { M.state.config.prompt .. state.query }) + end + + -- Render the restored state + M.render_list() + M.update_preview() + M.update_status() + + vim.api.nvim_set_current_win(M.state.input_win) + + -- Position cursor at end of query + vim.schedule(function() + if M.state.active and M.state.input_win and vim.api.nvim_win_is_valid(M.state.input_win) then + local prompt_len = #M.state.config.prompt + vim.api.nvim_win_set_cursor(M.state.input_win, { 1, prompt_len + #state.query }) + vim.cmd('startinsert!') + end + end) + + return true +end + +--- Close the picker, saving state for later resume. +--- When query is empty, the state is not saved. +---@param skip_save boolean|nil When true, closes without saving (for internal use) +function M.close(skip_save) + if skip_save then + M.close_windows() + return + end + save_state_and_close() +end + +---@return boolean true if a picker was resumed, false otherwise +function M.resume() + if M.state.active then + vim.notify('FFF: close the current picker before resuming', vim.log.levels.INFO) + return false + end + + -- Pick the most recently closed mode + if last_closed_mode == 'grep' then + return M.resume_live_grep() + elseif last_closed_mode == 'files' then + return M.resume_find_files() + end + + -- Fallback: try grep state, then file state, then open an empty find_files picker + if last_grep_picker_state then return restore_from_state(last_grep_picker_state, 'grep resume') end + if last_file_picker_state then return restore_from_state(last_file_picker_state, 'files resume') end + + -- Nothing saved: open an empty find_files picker + return M.open() +end + +--- Resume the last file picker (find_files mode). +--- Falls back to opening a new find_files picker if nothing to resume. +---@param opts? table Optional config overrides for fallback open +---@return boolean +function M.resume_find_files(opts) + if M.state.active then + vim.notify('FFF: close the current picker before resuming', vim.log.levels.INFO) + return false + end + + if not last_file_picker_state then + -- Nothing saved: open a new find_files picker + return M.open(opts) + end + + return restore_from_state(last_file_picker_state, 'find_files resume') +end + +--- Resume the last live_grep picker. +--- Falls back to opening a new live_grep picker if nothing to resume. +---@param opts? table Optional config overrides for fallback open +---@return boolean +function M.resume_live_grep(opts) + if M.state.active then + vim.notify('FFF: close the current picker before resuming', vim.log.levels.INFO) + return false + end + + if not last_grep_picker_state then + -- Nothing saved: open a new live_grep picker + local config = conf.get() + local grep_renderer = require('fff.picker_ui.grep_renderer') + local grep_config = vim.tbl_deep_extend('force', config.grep or {}, (opts and opts.grep) or {}) + M.open(vim.tbl_deep_extend('force', { + mode = 'grep', + renderer = grep_renderer, + grep_config = grep_config, + title = 'Live Grep', + }, opts or {})) + return true + end + + return restore_from_state(last_grep_picker_state, 'live_grep resume') +end function M.toggle_debug() local config_changed = conf.toggle_debug() diff --git a/plugin/fff.lua b/plugin/fff.lua index fb14fcce..428746c8 100644 --- a/plugin/fff.lua +++ b/plugin/fff.lua @@ -22,6 +22,10 @@ else }) end +vim.api.nvim_create_user_command('FFFResume', function() require('fff').resume() end, { + desc = 'Resume the last FFF picker (restores query, results, cursor, and mode)', +}) + vim.api.nvim_create_user_command('FFFFind', function(opts) local fff = require('fff') if opts.args and opts.args ~= '' then From 9e07ff2c46f3d97dd2142691881efe4346b493fc Mon Sep 17 00:00:00 2001 From: thuan do Date: Sun, 14 Jun 2026 12:50:42 +0700 Subject: [PATCH 2/7] chore: update --- lua/fff/picker_ui/layout_manager.lua | 72 ---------------------------- lua/fff/picker_ui/picker_ui.lua | 19 ++------ 2 files changed, 3 insertions(+), 88 deletions(-) diff --git a/lua/fff/picker_ui/layout_manager.lua b/lua/fff/picker_ui/layout_manager.lua index 5ff8c9d4..6b3114e7 100644 --- a/lua/fff/picker_ui/layout_manager.lua +++ b/lua/fff/picker_ui/layout_manager.lua @@ -19,78 +19,6 @@ local function restore_paste(should_restore) if should_restore then vim.o.paste = true end end -function M.close_windows() - if not P.state.active then return end - - vim.cmd('stopinsert') - P.state.active = false - - restore_paste(S.restore_paste) - - list_separator.cleanup() - scrollbar.cleanup() - - local ts_ok, ts_hl = pcall(require, 'fff.treesitter_hl') - if ts_ok then ts_hl.cleanup() end - - local windows = { S.input_win, S.list_win, S.preview_win } - if S.file_info_win then table.insert(windows, S.file_info_win) end - - for _, win in ipairs(windows) do - if win and vim.api.nvim_win_is_valid(win) then vim.api.nvim_win_close(win, true) end - end - - local buffers = { S.input_buf, S.list_buf, S.file_info_buf } - if S.preview_buf then buffers[#buffers + 1] = S.preview_buf end - - for _, buf in ipairs(buffers) do - if buf and vim.api.nvim_buf_is_valid(buf) then - vim.api.nvim_buf_clear_namespace(buf, -1, 0, -1) - if buf == S.preview_buf then preview.clear_buffer(buf) end - vim.api.nvim_buf_delete(buf, { force = true }) - end - end - - if S.preview_timer then - S.preview_timer:stop() - S.preview_timer:close() - S.preview_timer = nil - end - - S.input_win = nil - S.list_win = nil - S.file_info_win = nil - S.preview_win = nil - S.input_buf = nil - S.list_buf = nil - S.file_info_buf = nil - S.preview_buf = nil - S.preview_visible = false - S.items = {} - S.filtered_items = {} - S.cursor = 1 - S.query = '' - S.ns_id = nil - S.last_preview_file = nil - S.last_preview_location = nil - S.current_file_cache = nil - S.location = nil - S.selected_files = {} - S.selected_items = {} - S.mode = nil - S.grep_config = nil - S.grep_mode = 'plain' - S.grep_regex_fallback_error = nil - S.suggestion_items = nil - S.suggestion_source = nil - S.renderer = nil - S.restore_paste = false - S.combo_visible = true - S.combo_initial_cursor = nil - P.reset_history_state() - pcall(vim.api.nvim_del_augroup_by_name, 'fff_picker_focus') -end - function M.relayout() if not P.state.active then return end diff --git a/lua/fff/picker_ui/picker_ui.lua b/lua/fff/picker_ui/picker_ui.lua index 21e7de9b..7f2f11a8 100644 --- a/lua/fff/picker_ui/picker_ui.lua +++ b/lua/fff/picker_ui/picker_ui.lua @@ -83,7 +83,6 @@ M.scroll_to_bottom = renderer.scroll_to_bottom -- Wire layout_manager module (relayout, close) layout_manager.init(M) M.relayout = layout_manager.relayout -M.close_windows = layout_manager.close_windows -- Resume state: saved snapshots of closed pickers for the resume feature. --- @type table|nil Saved state from last file picker (find_files) session @@ -96,17 +95,13 @@ local last_closed_mode = nil --- Save the current picker state for later resume, then close. local function save_state_and_close() if M.state.query == '' then - -- Don't save empty state to avoid confusion on resume - M.close_windows() + layout_manager.close() return end if not M.state.active then return end - -- Deep copy the full state to capture all data fields (future-proof). - -- Window/buffer handles are also copied but are ignored during restore since UI is recreated. local snapshot = vim.deepcopy(M.state) - -- Capture the base_path from the Rust file indexer (not part of M.state) local fuzzy = require('fff.core').ensure_initialized() local ok, base_path = pcall(fuzzy.get_base_path) if ok and base_path then @@ -115,7 +110,6 @@ local function save_state_and_close() snapshot.base_path = M.state.config and M.state.config.base_path or nil end - -- Save to the mode-specific slot if M.state.mode == 'grep' then last_grep_picker_state = snapshot last_closed_mode = 'grep' @@ -124,7 +118,7 @@ local function save_state_and_close() last_closed_mode = 'files' end - M.close_windows() + layout_manager.close() end --- Internal: restore picker from a saved state snapshot. @@ -206,14 +200,7 @@ local function restore_from_state(state, source_label) return true end ---- Close the picker, saving state for later resume. ---- When query is empty, the state is not saved. ----@param skip_save boolean|nil When true, closes without saving (for internal use) -function M.close(skip_save) - if skip_save then - M.close_windows() - return - end +function M.close() save_state_and_close() end From d0742eda58e6a36d3fac19f5b061c7ab456ae74d Mon Sep 17 00:00:00 2001 From: thuan do Date: Sun, 14 Jun 2026 13:09:25 +0700 Subject: [PATCH 3/7] chore: fix lint --- lua/fff/main.lua | 8 ++------ lua/fff/picker_ui/layout_manager.lua | 4 +--- lua/fff/picker_ui/picker_ui.lua | 4 ++-- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/lua/fff/main.lua b/lua/fff/main.lua index 1b97382e..f8d48945 100644 --- a/lua/fff/main.lua +++ b/lua/fff/main.lua @@ -17,9 +17,7 @@ function M.find_files(opts) end if opts and opts.resume then - local cleaned = vim.deepcopy(opts) - cleaned.resume = nil - picker_ui.resume_find_files(cleaned) + picker_ui.resume_find_files(opts) return end @@ -37,9 +35,7 @@ function M.live_grep(opts) end if opts and opts.resume then - local cleaned = vim.deepcopy(opts) - cleaned.resume = nil - picker_ui.resume_live_grep(cleaned) + picker_ui.resume_live_grep(opts) return end diff --git a/lua/fff/picker_ui/layout_manager.lua b/lua/fff/picker_ui/layout_manager.lua index 6b3114e7..541dcc4a 100644 --- a/lua/fff/picker_ui/layout_manager.lua +++ b/lua/fff/picker_ui/layout_manager.lua @@ -75,7 +75,7 @@ function M.relayout() P.update_status() end -function M.close_windows() +function M.close() if not P.state.active then return end vim.cmd('stopinsert') @@ -146,6 +146,4 @@ function M.close_windows() pcall(vim.api.nvim_del_augroup_by_name, 'fff_picker_focus') end -function M.close() M.close_windows() end - return M diff --git a/lua/fff/picker_ui/picker_ui.lua b/lua/fff/picker_ui/picker_ui.lua index 7f2f11a8..382c113c 100644 --- a/lua/fff/picker_ui/picker_ui.lua +++ b/lua/fff/picker_ui/picker_ui.lua @@ -204,7 +204,7 @@ function M.close() save_state_and_close() end ----@return boolean true if a picker was resumed, false otherwise +---@return boolean|nil true if a picker was resumed, false otherwise function M.resume() if M.state.active then vim.notify('FFF: close the current picker before resuming', vim.log.levels.INFO) @@ -229,7 +229,7 @@ end --- Resume the last file picker (find_files mode). --- Falls back to opening a new find_files picker if nothing to resume. ---@param opts? table Optional config overrides for fallback open ----@return boolean +---@return boolean|nil function M.resume_find_files(opts) if M.state.active then vim.notify('FFF: close the current picker before resuming', vim.log.levels.INFO) From 248865ff41fb607415a51811d0fa40164f20e322 Mon Sep 17 00:00:00 2001 From: thuan do Date: Sun, 14 Jun 2026 13:21:05 +0700 Subject: [PATCH 4/7] chore: rename func --- lua/fff/picker_ui/picker_ui.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lua/fff/picker_ui/picker_ui.lua b/lua/fff/picker_ui/picker_ui.lua index 382c113c..f2659bc4 100644 --- a/lua/fff/picker_ui/picker_ui.lua +++ b/lua/fff/picker_ui/picker_ui.lua @@ -93,7 +93,7 @@ local last_grep_picker_state = nil local last_closed_mode = nil --- Save the current picker state for later resume, then close. -local function save_state_and_close() +function M.close() if M.state.query == '' then layout_manager.close() return @@ -200,10 +200,6 @@ local function restore_from_state(state, source_label) return true end -function M.close() - save_state_and_close() -end - ---@return boolean|nil true if a picker was resumed, false otherwise function M.resume() if M.state.active then From 83eed5e87e489df9cb7469055c77a433710933ef Mon Sep 17 00:00:00 2001 From: thuan do Date: Sun, 14 Jun 2026 21:13:04 +0700 Subject: [PATCH 5/7] feat: enhance cycle forward function --- lua/fff/picker_ui/search_manager.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lua/fff/picker_ui/search_manager.lua b/lua/fff/picker_ui/search_manager.lua index c75673c3..6bafa75d 100644 --- a/lua/fff/picker_ui/search_manager.lua +++ b/lua/fff/picker_ui/search_manager.lua @@ -315,11 +315,15 @@ function M.cycle_forward_query() if not P.state.active then return end if S.history_offset == nil then - S.history_offset = 0 - elseif S.history_offset > 0 then - S.history_offset = S.history_offset - 1 - else + -- Already at present state, nothing to cycle forward to return + elseif S.history_offset == 0 then + -- At the most recent history entry, go back to present + S.history_offset = nil + vim.api.nvim_buf_set_lines(S.input_buf, 0, -1, false, { S.config.prompt }) + return + else + S.history_offset = S.history_offset - 1 end local fuzzy = require('fff.core').ensure_initialized() From 1ea12636f6f723a0bd029b1b9672a46d4a2b07e8 Mon Sep 17 00:00:00 2001 From: thuan do Date: Sun, 14 Jun 2026 22:01:54 +0700 Subject: [PATCH 6/7] chore: clear input from resume --- lua/fff/picker_ui/search_manager.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lua/fff/picker_ui/search_manager.lua b/lua/fff/picker_ui/search_manager.lua index 6bafa75d..b6907c93 100644 --- a/lua/fff/picker_ui/search_manager.lua +++ b/lua/fff/picker_ui/search_manager.lua @@ -315,7 +315,10 @@ function M.cycle_forward_query() if not P.state.active then return end if S.history_offset == nil then - -- Already at present state, nothing to cycle forward to + -- At top of stack (fresh open or resume with pre-filled input). + -- Clear input to return to a clean slate. + S.history_offset = nil + vim.api.nvim_buf_set_lines(S.input_buf, 0, -1, false, { S.config.prompt }) return elseif S.history_offset == 0 then -- At the most recent history entry, go back to present From f761bf34c5573ddd5f161d9722463afa35ae9e3c Mon Sep 17 00:00:00 2001 From: thuan do Date: Tue, 16 Jun 2026 06:46:38 +0700 Subject: [PATCH 7/7] re-run pipeline