diff --git a/local/recipes/tui/tlc/source/src/filemanager/dialog_ops.rs b/local/recipes/tui/tlc/source/src/filemanager/dialog_ops.rs index db969f7680..4aa0a4f4c0 100644 --- a/local/recipes/tui/tlc/source/src/filemanager/dialog_ops.rs +++ b/local/recipes/tui/tlc/source/src/filemanager/dialog_ops.rs @@ -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(()) } diff --git a/local/recipes/tui/tlc/source/src/filemanager/find.rs b/local/recipes/tui/tlc/source/src/filemanager/find.rs index 7882ba2be5..d73044d973 100644 --- a/local/recipes/tui/tlc/source/src/filemanager/find.rs +++ b/local/recipes/tui/tlc/source/src/filemanager/find.rs @@ -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(), ""); + } } diff --git a/local/recipes/tui/tlc/source/src/filemanager/panel.rs b/local/recipes/tui/tlc/source/src/filemanager/panel.rs index 91ef06f6f7..caaff20f0f 100644 --- a/local/recipes/tui/tlc/source/src/filemanager/panel.rs +++ b/local/recipes/tui/tlc/source/src/filemanager/panel.rs @@ -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> { diff --git a/local/recipes/tui/tlc/source/src/widget/input.rs b/local/recipes/tui/tlc/source/src/widget/input.rs index 0195dfe8ad..461d1bc332 100644 --- a/local/recipes/tui/tlc/source/src/widget/input.rs +++ b/local/recipes/tui/tlc/source/src/widget/input.rs @@ -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) { + self.text = s.into(); + self.cursor = self.text.chars().count(); + } + /// Mark the input as focused. #[must_use] pub fn focused(mut self) -> Self {