diff --git a/local/recipes/tui/tlc/source/src/filemanager/dispatch.rs b/local/recipes/tui/tlc/source/src/filemanager/dispatch.rs index 20d1499ff8..8ad49e6728 100644 --- a/local/recipes/tui/tlc/source/src/filemanager/dispatch.rs +++ b/local/recipes/tui/tlc/source/src/filemanager/dispatch.rs @@ -439,6 +439,42 @@ impl FileManager { self.cmdline.insert_text(&name); Ok(true) } + Cmd::InsertOtherFile => { + let name = self + .other_panel() + .cursor_path() + .file_name() + .map(|n| n.to_string_lossy().to_string()) + .unwrap_or_default(); + self.cmdline.insert_text(&name); + Ok(true) + } + Cmd::InsertCurTagged => { + let names: Vec = self + .active_panel() + .marked_names() + .into_iter() + .collect(); + if names.is_empty() { + self.status.set_message("No tagged files".to_string()); + } else { + self.cmdline.insert_text(&names.join(" ")); + } + Ok(true) + } + Cmd::InsertOtherTagged => { + let names: Vec = self + .other_panel() + .marked_names() + .into_iter() + .collect(); + if names.is_empty() { + self.status.set_message("No tagged files".to_string()); + } else { + self.cmdline.insert_text(&names.join(" ")); + } + Ok(true) + } Cmd::SplitLess => { self.runtime.equal_split = Some(false); self.split_ratio = self.split_ratio.saturating_sub(5).max(10); diff --git a/local/recipes/tui/tlc/source/src/filemanager/mod.rs b/local/recipes/tui/tlc/source/src/filemanager/mod.rs index 31f05fdf44..6654960294 100644 --- a/local/recipes/tui/tlc/source/src/filemanager/mod.rs +++ b/local/recipes/tui/tlc/source/src/filemanager/mod.rs @@ -1546,4 +1546,53 @@ mod tests { assert_eq!(fm.runtime.equal_split, Some(true)); let _ = fs::remove_dir_all(&dir); } + + #[test] + fn insert_cur_file_inserts_cursor_filename() { + let dir = std::env::temp_dir().join("tlc-fm-insert-cur-file"); + let _ = fs::remove_dir_all(&dir); + fs::create_dir_all(&dir).unwrap(); + fs::write(dir.join("hello.txt"), b"x").unwrap(); + let mut fm = FileManager::new(&dir, &empty_cfg()).unwrap(); + fm.active_panel_mut().refresh().unwrap(); + fm.move_cursor(1); + fm.dispatch(Cmd::InsertCurFile).unwrap(); + assert_eq!(fm.cmdline.value(), "hello.txt"); + let _ = fs::remove_dir_all(&dir); + } + + #[test] + fn insert_other_file_inserts_other_panel_filename() { + let src = std::env::temp_dir().join("tlc-fm-insert-other-file-src"); + let dst = std::env::temp_dir().join("tlc-fm-insert-other-file-dst"); + let _ = fs::remove_dir_all(&src); + let _ = fs::remove_dir_all(&dst); + fs::create_dir_all(&src).unwrap(); + fs::create_dir_all(&dst).unwrap(); + fs::write(dst.join("other.txt"), b"x").unwrap(); + let mut fm = FileManager::new(&src, &empty_cfg()).unwrap(); + fm.active_panel_mut().refresh().unwrap(); + let _ = fm.other_panel_mut().set_path(&dst); + fm.other_panel_mut().refresh().unwrap(); + fm.dispatch(Cmd::InsertOtherFile).unwrap(); + assert_eq!(fm.cmdline.value(), "other.txt"); + let _ = fs::remove_dir_all(&src); + let _ = fs::remove_dir_all(&dst); + } + + #[test] + fn insert_cur_tagged_inserts_marked_names() { + let dir = std::env::temp_dir().join("tlc-fm-insert-tagged"); + let _ = fs::remove_dir_all(&dir); + fs::create_dir_all(&dir).unwrap(); + fs::write(dir.join("a.txt"), b"x").unwrap(); + fs::write(dir.join("b.txt"), b"x").unwrap(); + let mut fm = FileManager::new(&dir, &empty_cfg()).unwrap(); + fm.active_panel_mut().refresh().unwrap(); + fm.active_panel_mut().mark_at(1); + fm.dispatch(Cmd::InsertCurTagged).unwrap(); + let text = fm.cmdline.value(); + assert!(text.contains("a.txt"), "expected 'a.txt' in '{text}'"); + let _ = fs::remove_dir_all(&dir); + } } diff --git a/local/recipes/tui/tlc/source/src/keymap/mod.rs b/local/recipes/tui/tlc/source/src/keymap/mod.rs index b41a77dad1..e6486eb11d 100644 --- a/local/recipes/tui/tlc/source/src/keymap/mod.rs +++ b/local/recipes/tui/tlc/source/src/keymap/mod.rs @@ -151,6 +151,14 @@ pub enum Cmd { /// (MC binds Alt-Enter / Ctrl-Enter; tlc binds Alt-Enter to /// [`Cmd::Cmdline`], so this is bound to `Alt-;` to avoid the clash.) InsertCurFile, + /// C-x C-r — insert the other panel's cursor filename into the + /// command line. + InsertOtherFile, + /// C-x t — insert all tagged (selected) filenames of the active + /// panel into the command line, space-separated. + InsertCurTagged, + /// C-x C-t — insert all tagged filenames of the inactive panel. + InsertOtherTagged, /// Shift-F10 (a.k.a. F20) — quit immediately without the confirm dialog. QuitQuiet, /// C-z — suspend tlc (send SIGTSTP to self, return to the parent shell). @@ -275,6 +283,9 @@ impl Cmd { Cmd::InsertCurPath => "Insert current path", Cmd::InsertOtherPath => "Insert other path", Cmd::InsertCurFile => "Insert filename", + Cmd::InsertOtherFile => "Insert other filename", + Cmd::InsertCurTagged => "Insert tagged filenames", + Cmd::InsertOtherTagged => "Insert other tagged", Cmd::QuitQuiet => "Quit (no confirm)", Cmd::Suspend => "Suspend", Cmd::EditHistory => "View/edit history", @@ -502,6 +513,11 @@ pub fn default_keymap() -> Keymap { km.bind(Key::alt('a'), Cmd::InsertCurPath); km.bind(Key::alt('A'), Cmd::InsertOtherPath); km.bind(Key::alt(';'), Cmd::InsertCurFile); + km.bind(Key::alt('\''), Cmd::InsertOtherFile); + km.bind(Key::alt('t'), Cmd::InsertCurTagged); + let mut ctrl_alt_g = Key::alt('g'); + ctrl_alt_g.mods |= Modifiers::CTRL; + km.bind(ctrl_alt_g, Cmd::InsertOtherTagged); km.bind(Key::f(20), Cmd::QuitQuiet); km.bind(Key::ctrl('z'), Cmd::Suspend); km.bind(Key::ctrl('d'), Cmd::CompareFiles); @@ -609,6 +625,15 @@ mod tests { }), Some(Cmd::SortReverse) ); + assert_eq!(km.lookup(Key::alt('\'')), Some(Cmd::InsertOtherFile)); + assert_eq!(km.lookup(Key::alt('t')), Some(Cmd::InsertCurTagged)); + assert_eq!( + km.lookup(Key { + code: b'g' as u32, + mods: crate::key::Modifiers::CTRL | crate::key::Modifiers::ALT, + }), + Some(Cmd::InsertOtherTagged) + ); } #[test]