diff options
author | Canop <cano.petrole@gmail.com> | 2022-05-04 20:40:46 +0200 |
---|---|---|
committer | Canop <cano.petrole@gmail.com> | 2022-05-04 20:40:46 +0200 |
commit | c76be097397df82ed62582546ef76bbbf5203819 (patch) | |
tree | 6cd7b03037e30b649a4e3be41d5ca5fe6935ca1f /src | |
parent | e737f692ffaa4808813b37c6c51b06e6195cb875 (diff) |
add the :stage_all_files internal
Default mapping: ctrl-a
Diffstat (limited to 'src')
-rw-r--r-- | src/app/app.rs | 33 | ||||
-rw-r--r-- | src/app/app_context.rs | 7 | ||||
-rw-r--r-- | src/app/cmd_result.rs | 6 | ||||
-rw-r--r-- | src/app/panel.rs | 7 | ||||
-rw-r--r-- | src/app/panel_state.rs | 4 | ||||
-rw-r--r-- | src/browser/browser_state.rs | 157 | ||||
-rw-r--r-- | src/conf/conf.rs | 3 | ||||
-rw-r--r-- | src/display/areas.rs | 2 | ||||
-rw-r--r-- | src/errors.rs | 2 | ||||
-rw-r--r-- | src/git/ignore.rs | 2 | ||||
-rw-r--r-- | src/pattern/input_pattern.rs | 7 | ||||
-rw-r--r-- | src/preview/preview_state.rs | 6 | ||||
-rw-r--r-- | src/stage/stage_state.rs | 7 | ||||
-rw-r--r-- | src/tree/tree.rs | 2 | ||||
-rw-r--r-- | src/tree_build/builder.rs | 55 | ||||
-rw-r--r-- | src/verb/builtin.rs | 11 | ||||
-rw-r--r-- | src/verb/internal.rs | 1 | ||||
-rw-r--r-- | src/verb/internal_focus.rs | 3 |
18 files changed, 209 insertions, 106 deletions
diff --git a/src/app/app.rs b/src/app/app.rs index bd5cf68..73bbc6d 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -86,7 +86,6 @@ impl App { con, &Dam::unlimited(), )? - .expect("Failed to create BrowserState"), ), Areas::create(&mut Vec::new(), 0, screen, false), con, @@ -384,7 +383,7 @@ impl App { } if let Some(selected_path) = self.state().selected_path() { let dir = closest_dir(selected_path); - if let Ok(Some(new_state)) = BrowserState::new( + if let Ok(new_state) = BrowserState::new( dir, self.state().tree_options().without_pattern(), self.screen, @@ -594,8 +593,11 @@ impl App { con: &AppContext, ) -> Result<(), ProgramError> { while self.has_pending_task() && !dam.has_event() { - if self.do_pending_task(app_state, con, dam) { - self.update_preview(con); // the selection may have changed + let error = self.do_pending_task(app_state, con, dam).err(); + self.update_preview(con); // the selection may have changed + if let Some(error) = &error { + self.mut_panel().set_error(error.to_string()); + } else { let app_cmd_context = AppCmdContext { panel_skin: &skin.focused, preview_panel: self.preview_panel, @@ -604,39 +606,38 @@ impl App { con, }; self.mut_panel().refresh_input_status(app_state, &app_cmd_context); - self.display_panels(w, skin, app_state, con)?; - } else { - warn!("unexpected lack of update on do_pending_task"); - return Ok(()); + } + self.display_panels(w, skin, app_state, con)?; + if error.is_some() { + return Ok(()); // breaking pending tasks chain on first error/interruption } } Ok(()) } - /// do the next pending task + /// Do the next pending task fn do_pending_task( &mut self, - app_state: &AppState, + app_state: &mut AppState, con: &AppContext, dam: &mut Dam, - ) -> bool { + ) -> Result<(), ProgramError> { let screen = self.screen; // we start with the focused panel if self.panel().has_pending_task() { - self.mut_panel().do_pending_task(&app_state.stage, screen, con, dam); - return true; + return self.mut_panel().do_pending_task(app_state, screen, con, dam); } // then the other ones for idx in 0..self.panels.len().get() { if idx != self.active_panel_idx { let panel = &mut self.panels[idx]; if panel.has_pending_task() { - panel.do_pending_task(&app_state.stage, screen, con, dam); - return true; + return panel.do_pending_task(app_state, screen, con, dam); } } } - false + warn!("unexpected lack of pending task"); + Ok(()) } fn has_pending_task(&mut self) -> bool { diff --git a/src/app/app_context.rs b/src/app/app_context.rs index 542b422..518bafa 100644 --- a/src/app/app_context.rs +++ b/src/app/app_context.rs @@ -75,6 +75,9 @@ pub struct AppContext { /// number of threads used by file_sum (count, size, date) /// computation pub file_sum_threads_count: usize, + + /// number of files which may be staged in one staging operation + pub max_staged_count: usize, } impl AppContext { @@ -116,6 +119,9 @@ impl AppContext { (_, Some(b)) => !b, _ => true, }; + let max_staged_count = config.max_staged_count + .unwrap_or(10_000) + .clamp(10, 100_000); Ok(Self { config_paths, launch_args, @@ -133,6 +139,7 @@ impl AppContext { max_panels_count, quit_on_last_cancel: config.quit_on_last_cancel.unwrap_or(false), file_sum_threads_count, + max_staged_count, }) } } diff --git a/src/app/cmd_result.rs b/src/app/cmd_result.rs index 74a5dde..465feff 100644 --- a/src/app/cmd_result.rs +++ b/src/app/cmd_result.rs @@ -62,11 +62,11 @@ impl CmdResult { CmdResult::DisplayError(format!("verb not found: {:?}", &text)) } pub fn from_optional_state( - os: Result<Option<BrowserState>, TreeBuildError>, + os: Result<BrowserState, TreeBuildError>, in_new_panel: bool, ) -> CmdResult { match os { - Ok(Some(os)) => { + Ok(os) => { if in_new_panel { CmdResult::NewPanel { state: Box::new(os), @@ -77,7 +77,7 @@ impl CmdResult { CmdResult::NewState(Box::new(os)) } } - Ok(None) => CmdResult::Keep, + Err(TreeBuildError::Interrupted) => CmdResult::Keep, Err(e) => CmdResult::error(e.to_string()), } } diff --git a/src/app/panel.rs b/src/app/panel.rs index 0411e49..9a2757d 100644 --- a/src/app/panel.rs +++ b/src/app/panel.rs @@ -13,7 +13,6 @@ use { errors::ProgramError, keys::KEY_FORMAT, skin::PanelSkin, - stage::*, task_sync::Dam, verb::*, }, @@ -109,12 +108,12 @@ impl Panel { /// in the dam pub fn do_pending_task( &mut self, - stage: &Stage, + app_state: &mut AppState, screen: Screen, con: &AppContext, dam: &mut Dam, - ) { - self.mut_state().do_pending_task(stage, screen, con, dam) + ) -> Result<(), ProgramError> { + self.mut_state().do_pending_task(app_state, screen, con, dam) } pub fn has_pending_task(&self) -> bool { diff --git a/src/app/panel_state.rs b/src/app/panel_state.rs index 01b2129..743579b 100644 --- a/src/app/panel_state.rs +++ b/src/app/panel_state.rs @@ -699,11 +699,11 @@ pub trait PanelState { fn do_pending_task( &mut self, - _stage: &Stage, + _app_state: &mut AppState, _screen: Screen, _con: &AppContext, _dam: &mut Dam, - ) { + ) -> Result<(), ProgramError> { // no pending task in default impl unreachable!(); } diff --git a/src/browser/browser_state.rs b/src/browser/browser_state.rs index 1264be0..8f23cac 100644 --- a/src/browser/browser_state.rs +++ b/src/browser/browser_state.rs @@ -24,38 +24,52 @@ use { pub struct BrowserState { pub tree: Tree, pub filtered_tree: Option<Tree>, - pub pending_pattern: InputPattern, // a pattern (or not) which has not yet be applied - pub total_search_required: bool, // whether the pending pattern should be in total search mode - mode: Mode, + // pub pending_pattern: InputPattern, // a pattern (or not) which has not yet be applied + // pub total_search_required: bool, // whether the pending pattern should be in total search mode + mode: Mode, // whether we're in 'input' or 'normal' mode + pending_task: Option<BrowserTask>, // note: there are some other pending task, see +} + +/// A task that can be computed in background +enum BrowserTask { + Search { + pattern: InputPattern, + total: bool, + }, + StageAll(InputPattern), } impl BrowserState { /// build a new tree state if there's no error and there's no cancellation. - /// - /// In case of cancellation return `Ok(None)`. If dam is `unlimited()` then - /// this can't be returned. pub fn new( path: PathBuf, mut options: TreeOptions, screen: Screen, con: &AppContext, dam: &Dam, - ) -> Result<Option<BrowserState>, TreeBuildError> { - let pending_pattern = options.pattern.take(); + ) -> Result<BrowserState, TreeBuildError> { + let pending_task = options.pattern + .take() + .as_option() + .map(|pattern| BrowserTask::Search { pattern, total: false }); let builder = TreeBuilder::from( path, options, BrowserState::page_height(screen) as usize, con, )?; - Ok(builder.build(false, dam).map(move |tree| BrowserState { + let tree = builder.build_tree(false, dam)?; + Ok(BrowserState { tree, filtered_tree: None, - pending_pattern, - total_search_required: false, mode: initial_mode(con), - })) + pending_task, + }) + } + + fn search(&mut self, pattern: InputPattern, total: bool) { + self.pending_task = Some(BrowserTask::Search { pattern, total }); } /// build a cmdResult asking for the addition of a new state @@ -71,7 +85,7 @@ impl BrowserState { ) -> CmdResult { let tree = self.displayed_tree(); let mut new_state = BrowserState::new(root, options, screen, con, &Dam::unlimited()); - if let Ok(Some(bs)) = &mut new_state { + if let Ok(bs) = &mut new_state { if tree.selection != 0 { bs.displayed_tree_mut().try_select_path(&tree.selected_line().path); } @@ -188,14 +202,16 @@ impl PanelState for BrowserState { } fn get_pending_task(&self) -> Option<&'static str> { - if self.pending_pattern.is_some() { - Some("searching") - } else if self.displayed_tree().has_dir_missing_sum() { + if self.displayed_tree().has_dir_missing_sum() { Some("computing stats") } else if self.displayed_tree().is_missing_git_status_computation() { Some("computing git status") } else { - None + self + .pending_task.as_ref().map(|task| match task { + BrowserTask::Search{ .. } => "searching", + BrowserTask::StageAll(_) => "staging", + }) } } @@ -233,7 +249,7 @@ impl PanelState for BrowserState { } fn clear_pending(&mut self) { - self.pending_pattern = InputPattern::none(); + self.pending_task = None; } fn on_click( @@ -275,10 +291,10 @@ impl PanelState for BrowserState { } if let Some(filtered_tree) = &self.filtered_tree { if pat != filtered_tree.options.pattern { - self.pending_pattern = pat; + self.search(pat, false); } } else { - self.pending_pattern = pat; + self.search(pat, false); } Ok(CmdResult::Keep) } @@ -472,6 +488,20 @@ impl PanelState for BrowserState { CmdResult::error("No selected line") } } + Internal::stage_all_files => { + let pattern = self.displayed_tree().options.pattern.clone(); + self.pending_task = Some(BrowserTask::StageAll(pattern)); + if cc.app.stage_panel.is_none() { + let stage_options = self.tree.options.without_pattern(); + CmdResult::NewPanel { + state: Box::new(StageState::new(app_state, stage_options, con)), + purpose: PanelPurpose::None, + direction: HDir::Right, + } + } else { + CmdResult::Keep + } + } Internal::select_first => { self.displayed_tree_mut().try_select_first(); CmdResult::Keep @@ -518,16 +548,17 @@ impl PanelState for BrowserState { } } Internal::total_search => { - if let Some(tree) = &self.filtered_tree { - if tree.total_search { + match self.filtered_tree.as_ref().map(|t| t.total_search) { + None => { + CmdResult::error("this verb can be used only after a search") + } + Some(true) => { CmdResult::error("search was already total: all possible matches have been ranked") - } else { - self.pending_pattern = tree.options.pattern.clone(); - self.total_search_required = true; + } + Some(false) => { + self.search(self.displayed_tree().options.pattern.clone(), true); CmdResult::Keep } - } else { - CmdResult::error("this verb can be used only after a search") } } Internal::quit => CmdResult::Quit, @@ -562,35 +593,52 @@ impl PanelState for BrowserState { /// Stop as soon as the dam asks for interruption fn do_pending_task( &mut self, - _stage: &Stage, + app_state: &mut AppState, screen: Screen, con: &AppContext, dam: &mut Dam, - ) { - if self.pending_pattern.is_some() { - let pattern_str = self.pending_pattern.raw.clone(); - let mut options = self.tree.options.clone(); - options.pattern = self.pending_pattern.take(); - let root = self.tree.root().clone(); - let page_height = BrowserState::page_height(screen) as usize; - let builder = match TreeBuilder::from(root, options, page_height, con) { - Ok(builder) => builder, - Err(e) => { - warn!("Error while preparing tree builder: {:?}", e); - return; + ) -> Result<(), ProgramError> { + if let Some(pending_task) = self.pending_task.take() { + match pending_task { + BrowserTask::Search { pattern, total } => { + let pattern_str = pattern.raw.clone(); + let mut options = self.tree.options.clone(); + options.pattern = pattern; + let root = self.tree.root().clone(); + let page_height = BrowserState::page_height(screen) as usize; + let builder = TreeBuilder::from(root, options, page_height, con)?; + let filtered_tree = time!( + Info, + "tree filtering", + &pattern_str, + builder.build_tree(total, dam), + ); + if let Ok(mut ft) = filtered_tree { + ft.try_select_best_match(); + ft.make_selection_visible(BrowserState::page_height(screen)); + self.filtered_tree = Some(ft); + } + } + BrowserTask::StageAll(pattern) => { + let tree = self.displayed_tree(); + let root = tree.root().clone(); + let mut options = tree.options.clone(); + let total_search = true; + options.pattern = pattern; // should be the same + let builder = TreeBuilder::from(root, options, con.max_staged_count, con); + let mut paths = builder + .and_then(|mut builder| { + builder.matches_max = Some(con.max_staged_count); + time!(builder.build_paths( + total_search, + dam, + |line| line.file_type.is_file(), + )) + })?; + for path in paths.drain(..) { + app_state.stage.add(path); + } } - }; - let mut filtered_tree = time!( - Info, - "tree filtering", - &pattern_str, - builder.build(self.total_search_required, dam), - ); // can be None if a cancellation was required - self.total_search_required = false; - if let Some(ref mut ft) = filtered_tree { - ft.try_select_best_match(); - ft.make_selection_visible(BrowserState::page_height(screen)); - self.filtered_tree = filtered_tree; } } else if self.displayed_tree().is_missing_git_status_computation() { let root_path = self.displayed_tree().root(); @@ -599,6 +647,7 @@ impl PanelState for BrowserState { } else { self.displayed_tree_mut().fetch_some_missing_dir_sum(dam, con); } + Ok(()) } fn display( @@ -650,8 +699,8 @@ impl PanelState for BrowserState { } fn get_starting_input(&self) -> String { - if self.pending_pattern.is_some() { - self.pending_pattern.raw.clone() + if let Some(BrowserTask::Search { pattern, .. }) = self.pending_task.as_ref() { + pattern.raw.clone() } else { self.displayed_tree().options.pattern.raw.clone() } diff --git a/src/conf/conf.rs b/src/conf/conf.rs index f94db83..b8c8404 100644 --- a/src/conf/conf.rs +++ b/src/conf/conf.rs @@ -93,6 +93,8 @@ pub struct Conf { pub file_sum_threads_count: Option<usize>, + #[serde(alias="max_staged_count")] + pub max_staged_count: Option<usize>, } impl Conf { @@ -161,6 +163,7 @@ impl Conf { overwrite!(self, modal, conf); overwrite!(self, quit_on_last_cancel, conf); overwrite!(self, file_sum_threads_count, conf); + overwrite!(self, max_staged_count, conf); self.verbs.append(&mut conf.verbs); // the following maps are "additive": we can add entries from several // config files and they still make sense diff --git a/src/display/areas.rs b/src/display/areas.rs index d05131e..0e6161e 100644 --- a/src/display/areas.rs +++ b/src/display/areas.rs @@ -77,7 +77,7 @@ impl Areas { fn compute_areas( panels: &mut [Panel], - slots: &mut Vec<Slot>, + slots: &mut [Slot], screen: Screen, with_preview: bool, // slightly larger last panel ) { diff --git a/src/errors.rs b/src/errors.rs index 4845b1c..2342f72 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -35,6 +35,8 @@ custom_error! {pub ProgramError custom_error! {pub TreeBuildError NotADirectory { path: String } = "Not a directory: {}", FileNotFound { path: String } = "File not found: {}", + Interrupted = "Task Interrupted", + TooManyMatches { max: usize } = "Too many matches (max allowed: {max})", } custom_error! {pub ConfError diff --git a/src/git/ignore.rs b/src/git/ignore.rs index d0f8623..fa1aa53 100644 --- a/src/git/ignore.rs +++ b/src/git/ignore.rs @@ -164,7 +164,7 @@ impl GitIgnorer { } } if let Ok(gif) = GitIgnoreFile::new(&ignore_file, dir) { - debug!("pushing GIF {:#?}", &gif); + //debug!("pushing GIF {:#?}", &gif); chain.push(self.files.alloc(gif)); } if is_repo { diff --git a/src/pattern/input_pattern.rs b/src/pattern/input_pattern.rs index d5e6c39..d8729e4 100644 --- a/src/pattern/input_pattern.rs +++ b/src/pattern/input_pattern.rs @@ -50,6 +50,13 @@ impl InputPattern { pub fn take(&mut self) -> Self { std::mem::replace(self, Self::none()) } + pub fn as_option(self) -> Option<Self> { + if self.is_some() { + Some(self) + } else { + None + } + } /// from a pattern used to filter a tree, build a pattern /// which would make sense to filter a previewed file pub fn tree_to_preview(&self) -> Self { diff --git a/src/preview/preview_state.rs b/src/preview/preview_state.rs index 2c3041b..b09951a 100644 --- a/src/preview/preview_state.rs +++ b/src/preview/preview_state.rs @@ -7,7 +7,6 @@ use { errors::ProgramError, flag::Flag, pattern::InputPattern, - stage::*, task_sync::Dam, tree::TreeOptions, verb::*, @@ -147,11 +146,11 @@ impl PanelState for PreviewState { /// do the preview filtering if required and not yet done fn do_pending_task( &mut self, - _stage: &Stage, + _app_state: &mut AppState, _screen: Screen, con: &AppContext, dam: &mut Dam, - ) { + ) -> Result<(), ProgramError> { if self.pending_pattern.is_some() { let old_selection = self .filtered_preview @@ -170,6 +169,7 @@ impl PanelState for PreviewState { } } } + Ok(()) } fn selected_path(&self) -> Option<&Path> { diff --git a/src/stage/stage_state.rs b/src/stage/stage_state.rs index 90551ce..90694c3 100644 --- a/src/stage/stage_state.rs +++ b/src/stage/stage_state.rs @@ -194,15 +194,16 @@ impl PanelState for StageState { } fn do_pending_task( &mut self, - stage: &Stage, + app_state: &mut AppState, _screen: Screen, con: &AppContext, dam: &mut Dam, // need the stage here - ) { + ) -> Result<(), ProgramError> { if self.need_sum_computation() { - self.stage_sum.compute(stage, dam, con); + self.stage_sum.compute(&app_state.stage, dam, con); } + Ok(()) } fn get_pending_task(&self) -> Option<&'static str> { if self.need_sum_computation() { diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 441c197..ac32d9e 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -47,7 +47,7 @@ impl Tree { con, )?; let mut tree = builder - .build( + .build_tree( false, // on refresh we always do a non total search &Dam::unlimited(), ) diff --git a/src/tree_build/builder.rs b/src/tree_build/builder.rs index 2465630..f5b6209 100644 --- a/src/tree_build/builder.rs +++ b/src/tree_build/builder.rs @@ -63,6 +63,7 @@ pub struct TreeBuilder<'c> { git_ignorer: GitIgnorer, line_status_computer: Option<LineStatusComputer>, con: &'c AppContext, + pub matches_max: Option<usize>, // optional hard limit trim_root: bool, } impl<'c> TreeBuilder<'c> { @@ -108,6 +109,7 @@ impl<'c> TreeBuilder<'c> { line_status_computer, con, trim_root, + matches_max: None, }) } @@ -267,7 +269,7 @@ impl<'c> TreeBuilder<'c> { /// first step of the build: we explore the directories and gather lines. /// If there's no search pattern we stop when we have enough lines to fill the screen. /// If there's a pattern, we try to gather more lines that will be sorted afterwards. - fn gather_lines(&mut self, total_search: bool, dam: &Dam) -> Option<Vec<BId>> { + fn gather_lines(&mut self, total_search: bool, dam: &Dam) -> Result<Vec<BId>, TreeBuildError> { let start = Instant::now(); let mut out_blines: Vec<BId> = Vec::new(); // the blines we want to display let optimal_size = if self.options.pattern.pattern.has_real_scores() { @@ -289,6 +291,11 @@ impl<'c> TreeBuilder<'c> { self.total_search = false; break; } + if let Some(max) = self.matches_max { + if nb_lines_ok > max { + return Err(TreeBuildError::TooManyMatches{max}); + } + } if let Some(open_dir_id) = open_dirs.pop_front() { if let Some(child_id) = self.next_child(open_dir_id) { open_dirs.push_back(open_dir_id); @@ -314,7 +321,7 @@ impl<'c> TreeBuilder<'c> { for next_level_dir_id in &next_level_dirs { if dam.has_event() { info!("task expired (core build - inner loop)"); - return None; + return Err(TreeBuildError::Interrupted); } let has_child_match = self.load_children(*next_level_dir_id); if has_child_match { @@ -338,6 +345,11 @@ impl<'c> TreeBuilder<'c> { next_level_dirs.clear(); } } + if let Some(max) = self.matches_max { + if nb_lines_ok > max { + return Err(TreeBuildError::TooManyMatches{max}); + } + } if !self.trim_root { // if the root directory isn't totally read, we finished it even // it it goes past the bottom of the screen @@ -345,7 +357,7 @@ impl<'c> TreeBuilder<'c> { out_blines.push(child_id); } } - Some(out_blines) + Ok(out_blines) } /// Post search trimming @@ -398,7 +410,7 @@ impl<'c> TreeBuilder<'c> { } /// make a tree from the builder's specific structure - fn take(mut self, out_blines: &[BId]) -> Tree { + fn take_as_tree(mut self, out_blines: &[BId]) -> Tree { let mut lines: Vec<TreeLine> = Vec::new(); for id in out_blines.iter() { if self.blines[*id].has_match { @@ -444,16 +456,33 @@ impl<'c> TreeBuilder<'c> { /// /// Return None if the lifetime expires before end of computation /// (usually because the user hit a key) - pub fn build(mut self, total_search: bool, dam: &Dam) -> Option<Tree> { - match self.gather_lines(total_search, dam) { - Some(out_blines) => { - debug!("blines before trimming: {}", out_blines.len()); + pub fn build_tree(mut self, total_search: bool, dam: &Dam) -> Result<Tree, TreeBuildError> { + self.gather_lines(total_search, dam) + .map(|blines_ids| { + debug!("blines before trimming: {}", blines_ids.len()); if !self.total_search { - self.trim_excess(&out_blines); + self.trim_excess(&blines_ids); } - Some(self.take(&out_blines)) - } - None => None, // interrupted - } + self.take_as_tree(&blines_ids) + }) + } + + /// + pub fn build_paths<F>( + mut self, + total_search: bool, + dam: &Dam, + filter: F + ) -> Result<Vec<PathBuf>, TreeBuildError> + where F: Fn(&BLine) -> bool + { + self.gather_lines(total_search, dam) + .map(|mut blines_ids| { + blines_ids + .drain(..) + .filter(|&bid| filter(&self.blines[bid])) + .map(|id| self.blines[id].path.clone()) + .collect() + }) } } diff --git a/src/verb/builtin.rs b/src/verb/builtin.rs index 69fc310..fbc9edf 100644 --- a/src/verb/builtin.rs +++ b/src/verb/builtin.rs @@ -91,9 +91,12 @@ pub fn builtin_verbs() -> Vec<Verb> { internal(open_preview), internal(close_preview), internal(toggle_preview), - internal(preview_image), - internal(preview_text), - internal(preview_binary), + internal(preview_image) + .with_shortcut("img"), + internal(preview_text) + .with_shortcut("txt"), + internal(preview_binary) + .with_shortcut("hex"), internal(close_panel_ok), internal(close_panel_cancel) .with_key(key!(ctrl-w)), @@ -206,6 +209,8 @@ pub fn builtin_verbs() -> Vec<Verb> { .with_key(key!('+')), internal(unstage) .with_key(key!('-')), + internal(stage_all_files) + .with_key(key!(ctrl-a)), internal(toggle_stage) .with_key(key!(ctrl-g)), internal(open_staging_area).with_shortcut("osa"), diff --git a/src/verb/internal.rs b/src/verb/internal.rs index a43591a..cfbfdf6 100644 --- a/src/verb/internal.rs +++ b/src/verb/internal.rs @@ -125,6 +125,7 @@ Internals! { open_staging_area: "open the staging area" false, close_staging_area: "close the staging area panel" false, toggle_staging_area: "open/close the staging area panel" false, + stage_all_files: "stage all matching files" true, toggle_stage: "add or remove selection to staging area" true, toggle_counts: "toggle showing number of files in directories" false, toggle_dates: "toggle showing last modified dates" false, diff --git a/src/verb/internal_focus.rs b/src/verb/internal_focus.rs index 9fbee13..c1e2028 100644 --- a/src/verb/internal_focus.rs +++ b/src/verb/internal_focus.rs @@ -67,12 +67,11 @@ pub fn new_panel_on_path( } else { let path = path::closest_dir(&path); match BrowserState::new(path, tree_options, screen, con, &Dam::unlimited()) { - Ok(Some(os)) => CmdResult::NewPanel { + Ok(os) => CmdResult::NewPanel { state: Box::new(os), purpose, direction, }, - Ok(None) => CmdResult::Keep, // this isn't supposed to happen Err(e) => CmdResult::DisplayError(e.to_string()), } } |