Cancel in-flight picker fetches when pickers close #721

Closed
opened 2026-05-12 15:09:26 +00:00 by barrettruth · 0 comments
Owner

Status: WIP, but the broad direction is clear. The implementation still needs the usual small design pass before code.

Problem

Long-running picker fetches cannot currently be interrupted by closing/aborting the picker. On large repositories, opening a streamed picker such as PRs can leave the user stuck waiting for the backing CLI request to finish even after pressing <c-c>.

The current picker lifecycle only marks stale results as ignorable. It does not stop the underlying process:

  • lua/forge/picker/session.lua starts picker requests with vim.system(...) and discards the returned process handle.
  • picker_session.invalidate(key) increments an epoch and clears inflight, but it cannot kill the running gh/glab/tea process.
  • fzf-lua picker abort/close exits the picker UI, but forge.nvim owns the forge CLI process and must cancel it explicitly.

User impact

  • <c-c> does not reliably feel like cancel from the user point of view.
  • Large repo fetches can block the workflow until the forge CLI exits.
  • Refresh/invalidate can leave obsolete work running in the background.

Affected surfaces

This should cover every picker request path that goes through forge.picker.session, including:

  • streamed PR, issue, release, and CI picker list fetches
  • prefetch requests
  • entity/detail request paths that use request_json/pick_json

Proposed direction

  • Store the vim.system process handle in picker session state alongside the epoch/inflight metadata.
  • Make invalidation cancel the active process for that key before bumping/staling state.
  • Wire picker close/abort into request cancellation, likely by attaching/composing fzf-lua winopts.on_close for forge pickers that stream async content.
  • Treat user cancellation as a silent stale result, not a user-facing error.
  • Keep existing stale callback protection so killed or late callbacks cannot mutate the picker.

Acceptance criteria

  • Closing/aborting a picker with an active fetch kills the backing forge CLI process.
  • Refreshing a picker cancels the previous fetch before starting a new one.
  • Stale callbacks remain ignored and do not emit user-facing failure messages.
  • Existing fzf-lua no_hide/no_resume behavior remains intact.
  • Tests cover process-handle storage and cancellation on invalidate/close.
  • Tests cover that cancellation is not surfaced as a command failure to the user.

Non-goals

  • Do not change persistent caching semantics here.
  • Do not redesign picker rendering or pagination here.
  • Do not introduce backend-specific cancellation behavior unless a backend requires it.
Status: WIP, but the broad direction is clear. The implementation still needs the usual small design pass before code. ## Problem Long-running picker fetches cannot currently be interrupted by closing/aborting the picker. On large repositories, opening a streamed picker such as PRs can leave the user stuck waiting for the backing CLI request to finish even after pressing `<c-c>`. The current picker lifecycle only marks stale results as ignorable. It does not stop the underlying process: - `lua/forge/picker/session.lua` starts picker requests with `vim.system(...)` and discards the returned process handle. - `picker_session.invalidate(key)` increments an epoch and clears `inflight`, but it cannot kill the running `gh`/`glab`/`tea` process. - `fzf-lua` picker abort/close exits the picker UI, but forge.nvim owns the forge CLI process and must cancel it explicitly. ## User impact - `<c-c>` does not reliably feel like cancel from the user point of view. - Large repo fetches can block the workflow until the forge CLI exits. - Refresh/invalidate can leave obsolete work running in the background. ## Affected surfaces This should cover every picker request path that goes through `forge.picker.session`, including: - streamed PR, issue, release, and CI picker list fetches - prefetch requests - entity/detail request paths that use `request_json`/`pick_json` ## Proposed direction - Store the `vim.system` process handle in picker session state alongside the epoch/inflight metadata. - Make invalidation cancel the active process for that key before bumping/staling state. - Wire picker close/abort into request cancellation, likely by attaching/composing `fzf-lua` `winopts.on_close` for forge pickers that stream async content. - Treat user cancellation as a silent stale result, not a user-facing error. - Keep existing stale callback protection so killed or late callbacks cannot mutate the picker. ## Acceptance criteria - Closing/aborting a picker with an active fetch kills the backing forge CLI process. - Refreshing a picker cancels the previous fetch before starting a new one. - Stale callbacks remain ignored and do not emit user-facing failure messages. - Existing fzf-lua `no_hide`/`no_resume` behavior remains intact. - Tests cover process-handle storage and cancellation on invalidate/close. - Tests cover that cancellation is not surfaced as a command failure to the user. ## Non-goals - Do not change persistent caching semantics here. - Do not redesign picker rendering or pagination here. - Do not introduce backend-specific cancellation behavior unless a backend requires it.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
barrettruth/forge.nvim#721
No description provided.