tlc: Sprint 3 C10 — Find dialog pre-fills from cursor entry

Previously Find dialog always opened with the default placeholder
('*.rs'). Now open_find_dialog() inspects the active panel's
cursor entry and uses its name as the initial pattern — so
pressing M-? with cursor on 'main.rs' opens the dialog with
'main.rs' already in the input, ready for refinement (e.g.
'main.rs~' for backups, 'main*' for variants).

Implementation:
  - find::FindDialog::new_with_pattern(start_dir, initial_pattern)
  - panel::Panel::cursor_entry() -> Option<&Entry>
  - widget::input::Input::set_text(s) mutable setter (the
    existing .text() builder consumes self)
  - dialog_ops::open_find_dialog() uses cursor entry name as
    the initial pattern; skips '..' and falls back to default
    when the panel is empty

Tests (2 new in find::tests):
  - new_with_pattern_prefills_pattern_input
  - new_with_pattern_empty_falls_back_to_default

Total: 1278 passing (was 1276; +2 new).
This commit is contained in:
kellito
2026-07-05 18:55:04 +03:00
parent 790e476d8e
commit d14d4ad72d
4 changed files with 59 additions and 3 deletions
@@ -354,10 +354,24 @@ impl FileManager {
Ok(())
}
/// Open the M-? Find dialog.
/// Open the M-? Find dialog. The pattern input is pre-filled with
/// the cursor entry's name (so searching for the selected file's
/// pattern is the default); falls back to the default placeholder
/// if the cursor is on `..` or the panel is empty.
pub fn open_find_dialog(&mut self) -> Result<()> {
let start = self.active_panel().path().to_path_buf();
self.dialog = Some(DialogState::Find(Box::new(find::FindDialog::new(start))));
let pattern = self
.active_panel()
.cursor_entry()
.map(|e| e.name.clone())
.filter(|n| n != "..")
.unwrap_or_default();
let dialog = if pattern.is_empty() {
find::FindDialog::new(start)
} else {
find::FindDialog::new_with_pattern(start, &pattern)
};
self.dialog = Some(DialogState::Find(Box::new(dialog)));
Ok(())
}
@@ -129,7 +129,8 @@ pub struct FindDialog {
}
impl FindDialog {
/// Create a new Find dialog rooted at `start_dir`.
/// Create a new Find dialog rooted at `start_dir` with the default
/// placeholder pattern ("*.rs").
#[must_use]
pub fn new(start_dir: PathBuf) -> Self {
let pattern = Input::new().label("Find").placeholder("*.rs");
@@ -147,6 +148,19 @@ impl FindDialog {
}
}
/// Create a new Find dialog rooted at `start_dir` with the
/// pattern input pre-filled from the user's current selection
/// (e.g. cursor filename). Empty `initial_pattern` falls back to
/// the default placeholder.
#[must_use]
pub fn new_with_pattern(start_dir: PathBuf, initial_pattern: &str) -> Self {
let mut dlg = Self::new(start_dir);
if !initial_pattern.is_empty() {
dlg.pattern.set_text(initial_pattern.to_string());
}
dlg
}
/// Re-run the search against the current pattern and mode.
/// Errors are stored in `last_error` rather than propagated, so
/// the dialog stays interactive even when the user types an
@@ -779,4 +793,18 @@ mod tests {
.expect("render must not panic");
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn new_with_pattern_prefills_pattern_input() {
let d = FindDialog::new_with_pattern(PathBuf::from("/tmp"), "foo.rs");
assert_eq!(d.pattern.value(), "foo.rs");
}
#[test]
fn new_with_pattern_empty_falls_back_to_default() {
let d = FindDialog::new_with_pattern(PathBuf::from("/tmp"), "");
// Empty pattern leaves the input at the default placeholder
// (".value()" returns empty when no text was set).
assert_eq!(d.pattern.value(), "");
}
}
@@ -260,6 +260,13 @@ impl Panel {
&self.entries
}
/// Entry under the cursor, or `None` if the cursor is past the
/// end of the entry list.
#[must_use]
pub fn cursor_entry(&self) -> Option<&Entry> {
self.entries.get(self.cursor)
}
/// Current status message.
#[must_use]
pub fn message(&self) -> Option<&str> {
@@ -99,6 +99,13 @@ impl Input {
self
}
/// Set the current text (mutable setter, used to pre-fill the
/// input after construction). Cursor moves to the end.
pub fn set_text(&mut self, s: impl Into<String>) {
self.text = s.into();
self.cursor = self.text.chars().count();
}
/// Mark the input as focused.
#[must_use]
pub fn focused(mut self) -> Self {