Skip to content

feat: keep the GitHub and GitLab items in a disk cache, and add :CmpGitClearCache - #87

Merged
petertriho merged 6 commits into
mainfrom
persistent-cache
Oct 22, 2026
Merged

petertriho merged 6 commits into
mainfrom
persistent-cache

Conversation

@petertriho

Copy link
Copy Markdown
Owner

Keeps the GitHub and GitLab items (issues, pull requests, merge requests, and mentions) in a cache on disk, keyed by the remote. Each git commit from a shell starts a new Neovim, and the old cache was in memory and keyed by buffer number, so each commit message waited for the network again. Now the first #, !, or @ in a new Neovim shows the items from the cache file and starts no gh or glab process.

Behavior

  • The cache is in stdpath("cache")/cmp-git/<host>/<owner>/<repo>/. Each list has one JSON file. The file name has a hash of the query fields of its config (state, filter, limit, fields), so another config has its own file.
  • All buffers and all source instances of one remote share the entries. A cache hit needs no gh repo set-default lookup.
  • The cache keeps the raw items, and each request formats them with its own format config and trigger character.
  • When the items are older than the new cache.ttl option (300 seconds by default), a request still gets them at once. One refresh starts in the background, with the default repository from gh, and the next request gets the new items. A refresh of the mentions gets all pages.
  • A failed command or HTTP error is still not cached. A failed refresh keeps the old items and waits one more TTL.
  • Only the user can read the cache: the directories are 0700, and the files are 0600. A write goes to a temporary file and then a rename, so that another Neovim never reads a part of a file.
  • Commits and trailers stay in memory for each buffer. They are local, and a cold request takes 4 to 12 ms.

Command

  • :CmpGitClearCache clears the items of the remote of the current buffer, and the cached commits and trailers of the buffer.
  • :CmpGitClearCache! clears the items of all remotes, deletes the cache directory, and drops the commits and trailers of all buffers.

The new plugin/cmp_git.lua defines the command, so blink.cmp users get it without setup. The command loads its module only when it runs. It finds the remote with the config of each live core, because ssh_aliases or a GitLab path prefix can change the remote of a URL.

Results

A manual check in this repository with the real gh, three separate Neovim processes, and the cache in a temporary directory:

Request Time to the items gh processes
Cold 736 ms 3
New Neovim with the cache files 10.5 ms none
New Neovim with files older than the TTL 11.3 ms 3, in the background after the answer

make bench-compare REF=main, best of 2 runs:

Case main This branch
e2e/github_issues_and_prs/disk (new) - 0.74 ms, no processes
e2e/github_mentions/disk (new) - 0.11 ms, no processes
e2e/github_issues_and_prs/warm 0.15 ms 0.42 ms
e2e/github_mentions/warm 0.06 ms 0.12 ms
startup/setup 0.80 ms, 171 KB 0.78 ms, 174 KB
startup/require_all 1.92 ms, 392 KB 2.15 ms, 441 KB
  • The disk cases are a new Neovim with the files of an earlier session. On main, this request is cold (about 8 to 10 ms with the fake gh, plus the network).
  • The warm cases are slower because each cache hit now formats the raw items, as the commits capability already does.
  • The comparison reports one regression, startup/require_all. This case loads every module, and the branch adds two: cmp_git.cache and cmp_git.commands. setup and a declined request still load no new module.

Changes

  • 9be94b9: the test hooks and the benchmarks set XDG_CACHE_HOME to a temporary directory.
  • 0f875b2: the new cmp_git.cache module, with the entries, the file format, the atomic write, the TTL check, the refresh state, and the clear functions.
  • 6e72ad8: the GitHub and GitLab sources and their capabilities use the cache. Adds the cache.ttl option.
  • 438b445: :CmpGitClearCache[!].
  • fc192a7: the cold benchmark cases also clear the cache. The new disk cases measure a new session.
  • effd1f2: a "Cache" section in docs/configuration.md, and the cache.ttl default in the README.

Tests

  • make ci passes: format check, lint, type check, and tests.
  • make test passes in the nvim-0_12_0 dev shell.
  • New tests cover the file format and the modes, the key encoding, misses for invalid files, the background refresh and its failure, a clear during a fetch, a new session that reads the files without gh, and both forms of the command.

…rary directory

The GitHub and GitLab items will go to a disk cache under
`stdpath("cache")`, which follows `$XDG_CACHE_HOME`. The test cases
and the benchmarks now set this variable to a temporary directory, so
that they never read or write the cache of the user.
A new module `cmp_git.cache` keeps the raw items of one list of one
remote, for example the open issues of github.com/owner/repo. The
first request for an entry in a session reads its file, and a fetch
writes the file. The files are under `stdpath("cache")/cmp-git`, in a
directory for each host, owner, and repository. The name of a file has
the kind of the list and a hash of the query fields of its config, so
that another state, filter, limit, or fields value has its own entry.

Only the user can read the files and the directories, because they can
hold the issues of private repositories. A write goes to a temporary
file and then a rename, so that another Neovim never reads a part of a
file. A file with another version or shape is a miss.

An entry can refresh its items in the background when they are older
than a TTL. One refresh runs at a time, and a failed refresh keeps the
items and waits one more TTL. A clear removes the entries and the files
of one remote or of all remotes. An entry that a clear removed keeps no
more items, so that a fetch that ends after the clear does not bring
them back.

The sources do not use the module yet.
The issues, pull requests, merge requests, and mentions were cached for
each buffer and only in memory. Each commit message from `git commit`
starts a new Neovim, and each new buffer has a new number, so each of
them waited for the network again.

The GitHub and GitLab sources now keep the items in the disk cache,
keyed by the remote that discovery finds. All buffers and all source
instances of one remote share the entries. A cache hit in a new session
reads the file and starts no process, also not `gh repo set-default`.

The capabilities cache the raw items and format them for each request,
so that the format of the request and the current time give the labels
and the sort text. A failure is still not cached.

When the items are older than the new `cache.ttl` option (300 seconds
by default), a request still gets them at once, and one refresh starts
in the background. The refresh asks gh for the default repository
again, and its items are for the next request. A refresh of the
mentions gets all pages. A failed refresh keeps the items, and the next
refresh waits one more TTL.
…remotes

`:CmpGitClearCache` clears the GitHub and GitLab items of the remote of
the current buffer, and the cached commits and trailers of the buffer.
`:CmpGitClearCache!` clears the items of all remotes, deletes the cache
directory, and drops the commits and trailers of all buffers. The next
request fetches again. A message tells what the command cleared.

The command finds the remote with the config of each core that is
alive, because `ssh_aliases` or a host with a path prefix can change
the remote of a URL. Without a core, it uses the default config. The
cores and the git sources are in weak tables, so the command reaches
them without keeping them alive.

The new `plugin/cmp_git.lua` defines the command, so that blink.cmp
users get it without `setup`. It loads the command module only when
the command runs.
…ssion

The cache of the GitHub and GitLab items is now shared by all sources
and kept on disk, so a new source alone no longer makes a request cold.
The `cold` cases now also clear the cache. Revisions without the cache
module have nothing to clear.

The new `disk` cases for the GitHub issues and pull requests and for
the mentions measure a new source with an empty memory and the files of
the warm-up run, as in a new Neovim. CONTRIBUTING.md describes these
cases and the `XDG_CACHE_HOME` of the test hooks.
A new "Cache" section in docs/configuration.md tells where the cache
is, what it holds, how `cache.ttl` refreshes it in the background, and
how `:CmpGitClearCache` and `:CmpGitClearCache!` clear it. The section
also says that the cache holds the text of private repositories, and
that only the user can read it.

The troubleshooting section no longer tells users to wipe the buffer
to get new items. The README shows the `cache.ttl` default and links to
the new section.
@petertriho
petertriho merged commit 229ec58 into main Oct 22, 2026
5 of 6 checks passed
@petertriho
petertriho deleted the persistent-cache branch October 22, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant