summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2022-05-04 20:40:46 +0200
committerCanop <cano.petrole@gmail.com>2022-05-04 20:40:46 +0200
commitc76be097397df82ed62582546ef76bbbf5203819 (patch)
tree6cd7b03037e30b649a4e3be41d5ca5fe6935ca1f /src
parente737f692ffaa4808813b37c6c51b06e6195cb875 (diff)
add the :stage_all_files internal
Default mapping: ctrl-a
Diffstat (limited to 'src')
-rw-r--r--src/app/app.rs33
-rw-r--r--src/app/app_context.rs7
-rw-r--r--src/app/cmd_result.rs6
-rw-r--r--src/app/panel.rs7
-rw-r--r--src/app/panel_state.rs4
-rw-r--r--src/browser/browser_state.rs157
-rw-r--r--src/conf/conf.rs3
-rw-r--r--src/display/areas.rs2
-rw-r--r--src/errors.rs2
-rw-r--r--src/git/ignore.rs2
-rw-r--r--src/pattern/input_pattern.rs7
-rw-r--r--src/preview/preview_state.rs6
-rw-r--r--src/stage/stage_state.rs7
-rw-r--r--src/tree/tree.rs2
-rw-r--r--src/tree_build/builder.rs55
-rw-r--r--src/verb/builtin.rs11
-rw-r--r--src/verb/internal.rs1
-rw-r--r--src/verb/internal_focus.rs3
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()),
}
}