refactor completions into a backend based architecture - #5
Merged
Revantark merged 15 commits intoAug 28, 2026
Conversation
- Add a CompletionBackend trait: one `complete(request) -> result` method, so each backend decides for itself whether a request is its own. - Add matcher.rs, a pure ranking function shared by every backend. Ranking now follows the typed pattern rather than the order the scan happened to produce. - Fold the file-path scanner behind the trait: one flat workspace index in place of per-directory scans, dropping DirEntry, refilter and dir_part.
- Backends declare a trigger character and the controller keys them in a map, so the trait is dispatched rather than only declared - Token parsing moves into token.rs, shared by every backend; fixes a panic when the cursor sat inside a multi-byte character - Only one scan runs at a time, so the generation counter, cancel epoch and delivery channel are gone - Paths::new takes its root and no longer scans from the constructor - Scan limits collapse to MAX_INDEXED_PATHS and MAX_PATH_DEPTH, plus a cap on suggestions built per keystroke - @crates/main.rs matched nothing; pattern parts may now skip directories - Accepting a directory replaced it with itself forever, and left no separator so the next keystroke reopened the popup - One unreadable directory failed the entire scan
- Brightness now tracks what Enter will take; the trailing `/` already says an entry is a directory - Pull the row styling out of the render loop into item_line
- matcher moves inside completion/, where its only caller lives, and stops being crate-public - Re-export CompletionItem from core alongside the other completion types - Drop CompletionItem::description: nothing sets it, so it can come back with the slash-command backend that will - replace_range bails instead of panicking if a range outlives its line
- CompletionRequest carries `pattern` and `range` rather than the line and a token span, so no backend slices and the same span has one name - Fix Event::Paste syncing completion before inserting, which left the popup shut after pasting a mention
`@src/s` ranked every `.rs` file above `skill.rs`, because the name check looked for the whole pattern inside the name. `src` never appears in a filename, so nothing ever matched on its name and the trailing `s` of `.rs` satisfied the rest. The last part is the name being typed; the parts before it only locate it.
`@src/s` put skill.rs third behind sse.rs and selection.rs. Every candidate matched on its name, so only the tie-break separated them, and the first field compared was the offset of the pattern's first part — effectively "how few directories precede src", which says nothing about match quality. Comparing length first is what fzf does by default.
- Add an Activity the controller derives, so the footer stops assembling the status line out of booleans: thinking, suggesting, or idle - The status line becomes a table keyed on that, with plan mode and cost as badges layered on top - Anchor the completion popup above the prompt rather than the cursor, which had it painting over the status line describing it - Drop Controller::is_busy, which activity folds in
The first session on a machine failed with "No such file or directory", because `create` chmodded the root before `create_dir_all` had made it. Every test pre-created the root, so the first-run path was never exercised.
Revantark
approved these changes
Aug 28, 2026
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.
Completion moves behind a
CompletionBackendtrait so slash commands can slot in beside@paths without the editor learning a second syntax.CompletionControllerkeys them ontrigger(), and one shared tokenizer means no backend parses the line or handles byte offsets itself.matcher.rsranks every backend the same way. A pattern's/-separated parts may skip directories, so@crates/main.rsfindscrates/alan/src/main.rs, and the last part ranks against the file name rather than anywhere in the path.DirEntry,refilteranddir_part.JoinHandleis also where the result arrives.Activitythe controller derives, so "Enter send" no longer shows while Enter would accept a completion.MAX_INDEXED_PATHSis 10,000 where the oldCANDIDATE_LIMITwas 5,000. That one truncated after sorting by depth, so on a repo the size of zed (5,117 entries) the deepest files were silently uncompletable.MAX_PATH_DEPTHwent 32 to 10, and suggestions built per keystroke are capped at 100.The last commit touches
crates/agent: the first session on a machine failed with "No such file or directory", because the root was chmodded beforecreate_dir_allmade it and every test pre-created it. Happy to split that out if you'd rather keep this to completion.Pathsis the only backend so far; aCommandsbackend on/is the intended second one.