refactor: switch plenary.log and plenary.async requires to neoplen (PR2 of plenary removal) - #343
refactor: switch plenary.log and plenary.async requires to neoplen (PR2 of plenary removal)#343delphinus wants to merge 4 commits into
Conversation
…R2 of plenary removal, draft)
Renames every `require "plenary.log"` / `require "plenary.async"` (including
`lazy_require` and inline `require("...")` forms) to the telescope-vendored
neoplen equivalent. The neoplen modules ship in nvim-telescope/telescope.nvim
on the `feat/neoplen` branch (PR nvim-telescope/telescope.nvim#3647) and will
land in master once that PR merges; the `neoplen` namespace itself is
explicitly temporary per clason.
Two upstream gaps were uncovered while migrating and are worked around inline
so the test suite stays green when run against feat/neoplen telescope. Both
are flagged for clason to address before this PR can leave draft:
1. neoplen.async dropped its `uv` submodule (telescope commit b9b203f,
"feat(plenary): remove unused async"). Re-attach plenary's `uv_async` to
the cached neoplen.async table from `lua/frecency/lazy_require.lua`.
2. neoplen.log dropped the parent-directory creation step that plenary.log
used to do (Path:mkdir { parents = true } at log.new time), so the first
log line throws ENOENT on a fresh XDG_STATE_HOME. Create `stdpath("log")`
once at `lua/frecency/log.lua` load time.
`async.tests.add_to_env()` is also gone from neoplen, so the two specs that
relied on it (`database_spec`, `file_lock_spec`) now call a tiny
`util.add_async_to_env()` helper that injects `a.describe / a.it /
a.before_each / a.after_each / a.pending` using `neoplen.async.util.will_block`
(the same primitive plenary's add_to_env used).
CI will fail until telescope#3647 merges — the cloned telescope on `master`
does not provide `neoplen.*`. Verified locally with feat/neoplen pinned in
~/.cache/telescope-frecency/info.json: 33/33 tests pass on macOS / Apple
Silicon / Neovim stable.
See #340 for the overall plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per #3647 (comment): bring back `neoplen.async.uv` with only the wrappers extensions actually depend on. Surveyed against telescope-frecency (draft PR nvim-telescope/telescope-frecency.nvim#343), which uses these 7: fs_stat, fs_open, fs_close, fs_read, fs_write, fs_unlink, fs_realpath. `init.lua` gets a `uv = "neoplen.async.uv_async"` entry so call sites that did `require("neoplen.async").uv.fs_*` keep working unchanged. Adding more wrappers later is a single `add(name, argc)` call. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per #3647 (comment): bring back `neoplen.async.uv` with only the wrappers extensions actually depend on. Surveyed against telescope-frecency (draft PR nvim-telescope/telescope-frecency.nvim#343), which uses these 7: fs_stat, fs_open, fs_close, fs_read, fs_write, fs_unlink, fs_realpath. `init.lua` gets a `uv = "neoplen.async.uv_async"` entry so call sites that did `require("neoplen.async").uv.fs_*` keep working unchanged. Adding more wrappers later is a single `add(name, argc)` call. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| @@ -1,3 +1,16 @@ | |||
| -- Compat: neoplen.async dropped its `uv` submodule in telescope.nvim commit | |||
There was a problem hiding this comment.
It's back now :) So this can be removed.
| if vim.fn.isdirectory(log_dir) == 0 then | ||
| vim.fn.mkdir(log_dir, "p") | ||
| end | ||
| end |
There was a problem hiding this comment.
also moved that back in neoplen.log
feat/neoplen now ships the bits these draft-only workarounds stood in for, so they can go: - gap 1: async.uv was restored on feat/neoplen (nvim-telescope/telescope.nvim#3669, cherry-picked 2026-05-22), so neoplen.async.uv resolves via the module's __index lookups. Drop the plenary.async.uv_async re-attach block in lazy_require.lua (this also removes the last production-code reference to plenary in this PR). - gap 2: neoplen.log's log.new() now mkdir's stdpath("log"), so drop the load-time mkdir block in log.lua. gap 3 (tests/util.lua add_async_to_env) stays: it replaces the permanently removed async.tests submodule. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Hmm, that's interesting -- Startup cost improvements in Telescope are very much needed but probably require a larger refactor than I feel comfortable with. Definitely welcome follow-up work once the neoplen branch is merged! (P.S.: Current thinking is to rename |
Before the neoplen switch, frecency's startup DB warm-up (the `bootstrap` option) and launch-buffer registration were cheap, telescope-free work (frecency + plenary only), so running them during startup -- to warm the DB before the first `:Telescope frecency` -- was worthwhile. Now that the async I/O goes through `neoplen.async` (vendored inside telescope.nvim), any such call makes a lazy-loading plugin manager load telescope.nvim. There is no longer any reason to do this before VimEnter: the async DB warm-up still finishes well before the first picker either way, while running it mid-startup would drag telescope's load onto Neovim's startup critical path. VimEnter is the natural spot -- off the startup path, still before the first picker. - wait.lua: require neoplen.async via lazy_require like every other module, so merely requiring the DB modules no longer loads telescope. - init.lua: add async_call_or_defer() and use it for the register autocmd; defer the bootstrap DB init to a VimEnter autocmd. Both run immediately once vim_did_enter == 1, so only the startup phase is deferred. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Since async I/O now lives in telescope.nvim's vendored neoplen module, the startup DB warm-up can no longer be a minimal frecency + plenary load -- it pulls in telescope.nvim, so it is deferred to VimEnter (off the startup path). Loading frecency non-lazily therefore also loads telescope at VimEnter; keeping telescope fully lazy while eager-loading frecency is no longer possible until vim.async lands. - README: bullet in "Upgrading to 2.0.0". - :help: correct the setup() "load telescope lazily (recommended way)" wording, the inline cmd comment, and the bootstrap option note. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
c30a812 to
e8d9232
Compare
|
Right — at the Lua level Why frecency does this at startup: it warms its DB asynchronously so the first So on the frecency side I no longer run that work before (My earlier comment/PR wording was unclear about the actual rationale, so I've reworded the code comments and docs.) It's a breaking change — you can no longer keep telescope fully lazy while eager-loading frecency — but it affects a very niche setup (probably just me), so it shouldn't block the branch. Once And 👍 on |
|
Yeah, I would counsel against hyper-optimizing "lazy" (out-of-the box deferred as opposed to on-demand) loading -- "prewarming" may not even be desirable if you don't use frecency at all in a session! In general, a plugin should strive to do as little "up front" as possible and avoid eagerly pulling in "expensive" submodules if they're only needed in a few functions. The rest is best left to users to set up keybinds and autocommands to only load the plugin when it's actually needed. (This is how I do it with telescope including frecency, which works just fine -- for me.) (Of course, you know what you are doing, so I don't want to lecture you.) The story would change significantly if Neovim ever becomes truly multithreaded, in which case background threads are "free real estate" for precomputation and warm-up. |
Per #3647 (comment): bring back `neoplen.async.uv` with only the wrappers extensions actually depend on. Surveyed against telescope-frecency (draft PR nvim-telescope/telescope-frecency.nvim#343), which uses these 7: fs_stat, fs_open, fs_close, fs_read, fs_write, fs_unlink, fs_realpath. `init.lua` gets a `uv = "neoplen.async.uv_async"` entry so call sites that did `require("neoplen.async").uv.fs_*` keep working unchanged. Adding more wrappers later is a single `add(name, argc)` call. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Important
Draft, blocked on nvim-telescope/telescope.nvim#3647.
The
neoplen.*modules this PRrequires ship only on telescope'sfeat/neoplenbranch. CI on this PR will fail because the test runner clones telescope'smaster(which does not have those modules). Verified locally againstfeat/neopleninstead — see "Verification" below.The
neoplennamespace itself is explicitly temporary per clason in the upstream PR body. This branch will need a final rename (mechanicalsed) once upstream settles on the final module names.Summary
PR 2 of the plenary.nvim removal effort. Renames every
require "plenary.log"/require "plenary.async"(andlazy_require/ inlinerequire("...")variants) in production code and tests to the telescope-vendored equivalent. No call-site rewrites — just the module name on therequireline.Upstream gaps surfaced while migrating
Three gaps vs
plenary.*were uncovered. Gaps 1 and 2 have since been fixed onfeat/neoplen, so the inline workarounds added earlier in this draft have been removed (commit "drop neoplen.async.uv / neoplen.log compat shims"). Gap 3 is a permanent reimplementation that stays.1.
neoplen.asyncno longer had theuvsubmodule — ✅ resolved upstreamRemoved in telescope commit
b9b203f("feat(plenary): remove unused async"). frecency callsasync.uv.fs_{stat,open,close,read,write,unlink,realpath}from 17 production sites (file_lock, fs, klass, watcher, v2/database) and 6 test sites — the async-aware libuv wrappers are core to how we do non-blocking I/O.Resolution: restored upstream via nvim-telescope/telescope.nvim#3669 (cherry-picked into
feat/neoplenon 2026-05-22).lua/neoplen/async/uv_async.luanow exports exactly those sevenfs_*wrappers, andneoplen/async/init.luaresolvesneoplen.async.uvthrough its__indexlookups. Theplenary.async.uv_asyncre-attach block inlua/frecency/lazy_require.luais therefore deleted — this also removes the last production-code reference toplenaryin this PR.2.
neoplen.logdid not create the outfile parent directory — ✅ resolved upstreamplenary.logdidPath:new(outfile):parent():mkdir { parents = true }atlog.new()time;neoplen.loghad dropped that step when it shedplenary.path. On a freshXDG_STATE_HOME(e.g. our test sandbox),stdpath("log")does not exist, and the first log line threw ENOENT onio.open(outfile, "a").Resolution:
feat/neoplen'slog.new()nowvim.fn.mkdir(stdpath("log"), "p")s the log dir itself. The load-timemkdirblock inlua/frecency/log.luais therefore deleted.3.
neoplen.async.tests.add_to_env()is gone — reimplemented in-tree (stays)Removed in commit
18572e9("feat(plenary): remove unused async.tests"). frecency'sdatabase_specandfile_lock_specused it to injecta.describe / a.it / a.before_each / a.after_each / a.pending.Replacement in this PR: small inline
util.add_async_to_env()helper inlua/frecency/tests/util.lua— sets_G.awith five functions wrappingneoplen.async.util.will_block. Same primitive plenary'sadd_to_envused. Not a workaround, just an inline reimplementation — kept regardless of what upstream decides (async.testsis gone for good).Changes
plenary.async→neoplen.asyncandplenary.log→neoplen.logacross 14 files (production + tests).lua/frecency/tests/util.lua: addadd_async_to_env()helper and export it (replacement for gap 3). Add a-- luacheck: globals describe it ...pragma because util.lua is not a*_spec.luafile and so doesn't auto-pick up busted globals.lua/frecency/tests/{database,file_lock}_spec.lua: replaceasync.tests.add_to_env()calls withutil.add_async_to_env().The gap 1 / gap 2 workaround blocks that earlier commits added to
lazy_require.luaandlog.luahave been removed now thatfeat/neoplencovers both;lazy_require.luais back to identical withmasterandlog.lua's only diff vsmasteris the one-linerequire "neoplen.log".FrecencyPlenaryAsync*andFrecencyPlenaryLogtype aliases are intentionally not renamed — keeping them minimises diff noise until the upstream name finalises; they'll be renamed in the samesedthat flipsneoplento its final name.Startup load-order change (behavior change in this PR)
neoplen.*lives inside telescope.nvim, so a lazy-loading plugin manager loads telescope.nvim when frecency requiresneoplen.async. frecency'ssetup()does async I/O during startup — thebootstrapDB warm-up and theBufWinEnter/BufWritePostregister autocmd for command-line-opened files. Pre-neoplen this was cheap, telescope-free work (frecency + plenary only), so doing it during startup to warm the DB before the first:Telescope frecencymade sense. Post-neoplen it unavoidably loads telescope, so there is no longer any reason to run it beforeVimEnter: the async DB warm-up still finishes well before the first picker, while running it mid-startup would drag telescope's load onto Neovim's startup critical path.Fix:
wait.lua: requireneoplen.asyncvialazy_requirelike every other module, so merely requiring the DB modules no longer loads telescope.init.lua: addasync_call_or_defer()— runs immediately oncevim_did_enter == 1, else defers to a one-shotVimEnterautocmd. Used for the register autocmd; thebootstrapblock is likewise deferred toVimEnter. So during startup frecency touches telescope only atVimEnter(off the startup path, still before the first picker).VimEnter, so telescope can no longer stay fully lazy while frecency eager-loads to register startup buffers (returns oncevim.asynclets frecency drop theneoplen.asyncdependency). Documented inREADME"Upgrading to 2.0.0" and the:helpsetup()/bootstrapsections.Verification
Run against a
feat/neoplenclone (with the #3669 cherry-pick),~/.cache/telescope-frecency/info.jsonpointingnvim-telescope/telescope.nvimat it:Full suite passes against
feat/neoplen: sorter 3 / database 1 / file_lock 11 / frecency_validate_database 4 / frecency 14 → 33 passed, 0 failed (on a current Neovim nightly; an earlier nightly had an unrelated empty-picker-result issue in the register flow that also reproduced on plainmaster, and is gone now)..luafiles.Reproduction recipe:
git clone --branch feat/neoplen --depth 1 https://github.com/nvim-telescope/telescope.nvim /tmp/telescope-neoplen # Edit ~/.cache/telescope-frecency/info.json so nvim-telescope/telescope.nvim.path points at /tmp/telescope-neoplen bin/run-testsCI on this PR is expected to fail at the require-resolution step until telescope#3647 lands.
Follow-up
neoplen→ final name (including theFrecencyPlenary*type aliases).tests/util.luastill does, viaplenary.path+plenary.job).🤖 Generated with Claude Code