refactor(peek): replace adapter registry with push-model peek sources#16
Merged
Conversation
The peek subsystem dispatched through a name-keyed adapter table
(default_adapters[name] / Config.adapters[name]) resolved at call time.
That registry was stringly-typed, invisible to lua-ls, and existed mainly
to serve a custom-adapter-by-name extension surface that isn't a goal
(pre-1.0, internal cleanup). The built-in dispatch never needed it -- the
names are fixed at author time and the module IS the handle.
New shape ("push"): a peek source is just a function that builds
OverlookPopupOptions and hands them to require("overlook.window").open_popup.
There is no dispatcher, no contract object, no .async flag, no registry, no
name lookup. Sync sources call open_popup directly; async sources (LSP) call
it later from their own callback -- the sync/async distinction disappears.
- Delete lua/overlook/peek.lua (the metatable dispatcher).
- lua/overlook/adapter/ -> lua/overlook/peek/ : cursor, marks, definition are
now plain functions. require("overlook.peek.cursor")() reads as the action.
- window.lua: add module-level M.open_popup(opts) = M.current():open_popup(opts)
so a source never has to know the Window/Stack/Popup layering. The seam
lives where popups already live rather than in a separate peek layer.
- api.lua: peek_* wrappers call the source modules directly; drop the Peek
require.
- config.lua: remove the `adapters` option (and the leaked your_custom_adapter
stub) -- custom sources are now "write a function that calls
Window.open_popup and bind it to a key", no registration.
- types.lua: drop the OverlookAdapter contract; document a peek source as a
plain function (OverlookPeekSource alias).
Folds in the bug fixes from the earlier cleanup pass (unpushed, superseded):
- peek_mark no longer fires the stray pre-prompt marks() call (double error).
- definition guards empty/nil LSP results + missing user_data (was a crash).
- cursor drops the dead file_path field.
- a source that has nothing to peek notifies and simply does not open; no
generic "returned nil options" error.
Tests: per-source specs (cursor/marks/definition) assert the source calls
Window.open_popup with the right options (or notifies and opens nothing);
api_spec covers peek_mark's prompt flow; the dispatcher spec is removed.
99 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the peek subsystem's name-keyed adapter registry with a "push" model: a peek source is just a function that builds
OverlookPopupOptionsand hands them torequire("overlook.window").open_popup(opts). No dispatcher, no contract object, no.asyncflag, no string-keyed registry, no name lookup.The old design dispatched through a table keyed by adapter name (
default_adapters[name]/Config.adapters[name]), resolved at call time. That was stringly-typed, invisible to lua-ls, and existed mainly to support custom-adapters-by-name — an extension surface that isn't a goal pre-1.0. The built-in dispatch never needed it: the names are fixed at author time and the module is the handle.Pre-1.0, internal cleanup, adapter contract free to break.
What changed
lua/overlook/peek.lua(the metatable__indexdispatcher).adapter/→peek/:cursor,marks,definitionare now plain functions.require("overlook.peek.cursor")()reads as the action and mirrors thepeek_cursorAPI.open_popupdirectly; async sources (LSP) call it later from their own callback. There's no.asyncflag or two-shape contract anymore.window.lua: added module-levelM.open_popup(opts) = M.current():open_popup(opts)— the single seam a source uses, so a source never has to know the Window/Stack/Popup layering. It lives where popups already live rather than in a separate peek layer.api.lua:peek_*wrappers call the source modules directly; dropped thePeekrequire.config.lua: removed theadaptersoption (and theyour_custom_adapterstub that deep-merged into every user's config as a callable no-op). A custom source is now "write a function that callsWindow.open_popupand bind it to a key" — no registration.types.lua: dropped theOverlookAdaptercontract; a peek source is documented as a plain function (OverlookPeekSource).Bug fixes folded in
peek_markno longer fires the stray pre-promptmarks()call that produced a double error on every invocation.definitionguards empty/nil LSP results and missinguser_data(was anattempt to index a nil valuecrash on the common no-definition case).cursordrops the deadfile_pathfield (set but never read, not inOverlookPopupOptions).Test plan
cursor_spec,marks_spec,definition_spec) assert each source callsWindow.open_popupwith the right options, or notifies and opens nothing.api_speccoverspeek_mark's prompt flow (no pre-prompt fire; one fire on a valid char; invalid/cancelled handling).Breaking changes
Config.adaptersis removed. Any custom adapter registered via config no longer resolves — register a custom source by binding a function that callsrequire("overlook.window").open_popup(opts)to a key instead. Acceptable pre-1.0.🤖 Generated with Claude Code