pub struct DirPickSource {
pub spec: PickerSourceSpec,
}Expand description
PC.9: dir-pick — FilePickSource’s directory peer. Browse to a
directory and supply its path as a value.
§Incremental, not a walk
The candidates are the children of the directory the query names, filtered
by the basename it ends with — gen:directories’ model, which is what
emacs’s read-directory-name does. It shares the implementation with that
generator ([lattice_completion::builtins::generators::path_entries])
rather than copying it, so <Tab> on the : line and this picker cannot
disagree about what listing a path means.
walk_files_for_picker with directories instead of files was the obvious
alternative and is the wrong one here: it has no depth cap and a flat
FILE_PICKER_MAX_ENTRIES ceiling, so pointed anywhere near a home
directory it stops somewhere arbitrary and the directory you wanted may
simply not be in the list. Incremental has no ceiling, reaches any depth,
and opens in one read_dir.
§It starts at HOME, where file-pick starts at the workspace root
Not an inconsistency. Picking a file is nearly always picking one in the project you are in, so the workspace root is the useful default. Picking a directory is nearly always about going somewhere you are not — the motivating case is choosing a project you have never opened — and rooting that at the project you are already in would make the common case start in the one place it does not want.
An explicit start argument still wins, and :picker dir-pick . is the
spelling for “here”.
§Tilde survives into the rows on purpose
A row’s text keeps whatever spelling the query used (~/src/…), because
that is what the user is reading and typing against, and descending
re-lists from it unchanged. The value handed back on accept is the
expanded absolute path off CandidateData::File, because a consumer
resolving it has no obligation to know about ~.
Fields§
§spec: PickerSourceSpecImplementations§
Source§impl DirPickSource
impl DirPickSource
Trait Implementations§
Source§impl Default for DirPickSource
impl Default for DirPickSource
Source§impl PickerSourceGenerator for DirPickSource
impl PickerSourceGenerator for DirPickSource
Source§fn on_query_changed(
&self,
_ctx: &PickerContext<'_>,
query: &str,
) -> Option<SourceResult<PickerInitResult>>
fn on_query_changed( &self, _ctx: &PickerContext<'_>, query: &str, ) -> Option<SourceResult<PickerInitResult>>
Re-list on every keystroke. An unreadable query yields an EMPTY list, not an error: half a typed path names nothing yet, and that is the state the user is in for most of the keystrokes — erroring on it would mean the picker spends its life reporting failure.
Source§fn descend(
&self,
_ctx: &PickerContext<'_>,
candidate: &RawCandidate,
) -> Option<String>
fn descend( &self, _ctx: &PickerContext<'_>, candidate: &RawCandidate, ) -> Option<String>
<C-l>: the selected row’s own text becomes the query, so the next
listing is of its children. It already ends in / — path_entries
puts one on every directory — which is exactly the prefix that lists a
directory’s contents rather than its siblings.
PP.3: <CR> on ../ GOES UP. It does not choose the parent.
PP.1 shipped the other reading — ../ is an ordinary row, so <CR>
supplies its path like every other row does — and it was wrong in the
way that only shows up in use. ../ reads as a verb, every file
browser there is (netrw, oil, ranger, lf, telescope-file-browser)
treats <CR> on .. as “go up”, and the UX-convention rule says
muscle memory wins on a surface like this one.
What it looked like in practice: <CR> on ../ at ~/ supplied
/Users, which the project flow then refused — an error message where
the user had asked to go up a level.
Only this row. Every other row in this picker is a directory you might
be choosing, and <CR> still chooses it.
Source§fn ascend(&self, query: &str) -> Option<String>
fn ascend(&self, query: &str) -> Option<String>
<C-w>: drop the last path component.
parent_of does the work, shared with the ../
row so the key and the row cannot land in different places. / stays
a fixed point — parent_of answers None there, and this returns the
query unchanged so the host recognises it and spends no re-query.
Source§fn initial_query(&self, args: &[String]) -> Option<String>
fn initial_query(&self, args: &[String]) -> Option<String>
PP.1: open on the start directory rather than on an empty query.
The query IS the directory being listed here, so an empty one leaves the prompt unable to say where you are — every row carries a path and the one line meant to orient you carries nothing. It also left ascend with no last component to drop, so the first press did nothing and the second worked.
The trailing / is what makes it a LISTING rather than a filter:
path_entries("~/src") lists ~’s children whose names start with
src, where path_entries("~/src/") lists what is inside. Seeding
the un-slashed form is the bug this normalisation exists to prevent,
and :picker dir-pick /tmp walked straight into it.
Source§fn spec(&self) -> &PickerSourceSpec
fn spec(&self) -> &PickerSourceSpec
:describe-picker /
:picker <Tab> listings without cloning.Source§fn init(
&self,
_ctx: &PickerContext<'_>,
args: &[String],
) -> SourceResult<PickerInitResult>
fn init( &self, _ctx: &PickerContext<'_>, args: &[String], ) -> SourceResult<PickerInitResult>
ctx, clone into async captures if
necessary, return the appropriate PickerInitResult
variant. Synchronous errors (no active buffer when
one was required, args validation failure) return
Err; the host echoes the error and leaves the
picker closed. Read moreSource§fn accept(
&self,
_ctx: &PickerContext<'_>,
routing: &RoutingPayload,
) -> SourceResult<PickerAcceptOutcome>
fn accept( &self, _ctx: &PickerContext<'_>, routing: &RoutingPayload, ) -> SourceResult<PickerAcceptOutcome>
PickerAcceptOutcome the host applies. The
generator owns the mapping from its emitted
routing-payload variant(s) to outcome(s); mismatch
returns Err, which the host echoes.Source§fn accept_async(
&self,
_ctx: &PickerContext<'_>,
_routing: &RoutingPayload,
) -> Option<AcceptFuture>
fn accept_async( &self, _ctx: &PickerContext<'_>, _routing: &RoutingPayload, ) -> Option<AcceptFuture>
None means “this source resolves accept
synchronously” — every native source takes this path and is unchanged. Read more