Skip to content

Commit

Permalink
make it possible to use flags like '-sdp' as verb (#874)
Browse files Browse the repository at this point in the history
* make it possible to use flags like '-sdp' as verb

* Modify verbinvocation syntax parser

* introduce the dash-flags way

* check verb names verify new naming constraints
  • Loading branch information
Canop authored May 4, 2024
1 parent f0db2f4 commit 3c3f4e6
Show file tree
Hide file tree
Showing 17 changed files with 235 additions and 58 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,11 @@ Add files to the [staging area](staging-area) then execute any command on all of

If you want to display *sizes*, *dates* and *permissions*, do `br -sdp` which gets you this:

![replace ls](website/docs/img/20230930-sdp.png)
![replace ls](website/docs/img/20240501-sdp.png)

You may also toggle options with a few keystrokes while inside broot. For example hitting a space, a <kbd>d</kbd> then <kbd>enter</kbd> shows you the dates. Or hit <kbd>alt</kbd><kbd>h</kbd> and you see hidden files.
You may also toggle options with a few keystrokes while inside broot.
For example you could have typed this `-sdp` while in broot.
Or hit <kbd>alt</kbd><kbd>h</kbd> and you see hidden files.

## Sort, see what takes space:

Expand Down
2 changes: 1 addition & 1 deletion src/app/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ impl App {
}
if let Some(shared_root) = &mut self.shared_root {
if let Ok(mut root) = shared_root.lock() {
*root = app_state.root.clone();
root.clone_from(&app_state.root);
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions src/app/panel_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,25 @@ pub trait PanelState {
.map(|inv| inv.bang)
.unwrap_or(internal_exec.bang);
Ok(match internal_exec.internal {
Internal::apply_flags => {
info!("applying flags input_invocation: {:#?}", input_invocation);
let flags = input_invocation.and_then(|inv| inv.args.as_ref());
if let Some(flags) = flags {
self.with_new_options(
screen,
&|o| {
match o.apply_flags(flags) {
Ok(()) => "*flags applied*",
Err(e) => e,
}
},
bang,
con,
)
} else {
CmdResult::error(":apply_flags needs flags as arguments")
}
}
Internal::back => CmdResult::PopState,
Internal::copy_line | Internal::copy_path => {
#[cfg(not(feature = "clipboard"))]
Expand Down Expand Up @@ -986,6 +1005,7 @@ pub trait PanelState {
)
} else {
let sel_info = self.sel_info(app_state);
info!("invocation: {:#?}", invocation);
match cc.app.con.verb_store.search_sel_info(
&invocation.name,
sel_info,
Expand Down
4 changes: 4 additions & 0 deletions src/cli/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ pub struct Args {
#[arg(short, long)]
pub whale_spotting: bool,

/// No sort, no show hidden, no show git ignored
#[arg(short='W', long)]
pub no_whale_spotting: bool,

/// Trim the root too and don't show a scrollbar
#[arg(short='t', long)]
pub trim_root: bool,
Expand Down
2 changes: 2 additions & 0 deletions src/command/panel_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,8 @@ impl PanelInput {
let raw = self.input_field.get_content();
let parts = CommandParts::from(raw.clone());

info!("parts: {:#?}", parts);

let verb = if self.is_key_allowed_for_verb(key, mode) {
self.find_key_verb(
key,
Expand Down
21 changes: 14 additions & 7 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ custom_error! {pub ProgramError
ZeroLenFile = "File seems empty",
}

custom_error!{pub ShellInstallError
custom_error! {pub ShellInstallError
Io {source: io::Error, when: String} = "IO Error {source} on {when}",
}
impl ShellInstallError {
Expand All @@ -43,19 +43,25 @@ impl ShellInstallError {
Self::Io { source, .. } => {
if source.kind() == io::ErrorKind::PermissionDenied {
true
} else { cfg!(windows) && source.raw_os_error().unwrap_or(0) == 1314 }
} else {
cfg!(windows) && source.raw_os_error().unwrap_or(0) == 1314
}
}
}
}
}
pub trait IoToShellInstallError<Ok> {
fn context(self, f: &dyn Fn() -> String) -> Result<Ok, ShellInstallError>;
fn context(
self,
f: &dyn Fn() -> String,
) -> Result<Ok, ShellInstallError>;
}
impl<Ok> IoToShellInstallError<Ok> for Result<Ok, io::Error> {
fn context(self, f: &dyn Fn() -> String) -> Result<Ok, ShellInstallError> {
self.map_err(|source| ShellInstallError::Io {
source, when: f()
})
fn context(
self,
f: &dyn Fn() -> String,
) -> Result<Ok, ShellInstallError> {
self.map_err(|source| ShellInstallError::Io { source, when: f() })
}
}

Expand Down Expand Up @@ -88,6 +94,7 @@ custom_error! {pub ConfError
InvalidDefaultFlags { flags: String } = "invalid default flags: {flags:?}",
InvalidSyntaxTheme { name: String } = "invalid syntax theme: {name:?}",
InvalidGlobPattern { pattern: String } = "invalid glob pattern: {pattern:?}",
InvalidVerbName { name: String } = "invalid verb name: {name:?} (must either not start with a special character or be only made of special characters)",
}

// error which can be raised when parsing a pattern the user typed
Expand Down
2 changes: 1 addition & 1 deletion src/filesystems/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use {
std::sync::Mutex,
};

pub static MOUNTS: Lazy<Mutex<MountList>> = Lazy::new(|| Mutex::new(MountList::new()));
pub static MOUNTS: Lazy<Mutex<MountList>> = Lazy::new(|| Mutex::new(MountList::default()));

pub fn clear_cache() {
let mut mount_list = MOUNTS.lock().unwrap();
Expand Down
4 changes: 1 addition & 3 deletions src/filesystems/mount_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@ use {
},
};

#[derive(Default)]
pub struct MountList {
mounts: Option<Vec<Mount>>,
}

impl MountList {
pub const fn new() -> Self {
Self { mounts: None }
}
pub fn clear_cache(&mut self) {
self.mounts = None;
}
Expand Down
1 change: 0 additions & 1 deletion src/git/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ impl LineStatusComputer {
}
}

///
#[derive(Debug, Clone)]
pub struct TreeGitStatus {
pub current_branch_name: Option<String>,
Expand Down
24 changes: 24 additions & 0 deletions src/tree/tree_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use {
errors::ConfError,
pattern::*,
},
clap::Parser,
lazy_regex::regex_is_match,
std::convert::TryFrom,
};

Expand Down Expand Up @@ -99,6 +101,21 @@ impl TreeOptions {
.unwrap_or(DEFAULT_COLS);
Ok(())
}
/// apply flags like "sdp"
pub fn apply_flags(&mut self, flags: &str) -> Result<(), &'static str> {
if !regex_is_match!("^[a-zA-Z]+$", flags) {
return Err("Flags must be a sequence of letters");
}
let prefixed = format!("-{flags}");
let tokens = vec!["broot", &prefixed];
let args = Args::try_parse_from(tokens)
.map_err(|_| {
warn!("invalid flags: {:?}", flags);
"invalid flag (valid flags are -dDfFgGhHiIpPsSwWtT)"
})?;
self.apply_launch_args(&args);
Ok(())
}
/// change tree options according to broot launch arguments
pub fn apply_launch_args(&mut self, cli_args: &Args) {
if cli_args.sizes {
Expand All @@ -114,6 +131,13 @@ impl TreeOptions {
self.show_sizes = true;
self.show_root_fs = true;
}
if cli_args.no_whale_spotting {
self.show_hidden = false;
self.respect_git_ignore = true;
self.sort = Sort::None;
self.show_sizes = false;
self.show_root_fs = false;
}
if cli_args.only_folders {
self.only_folders = true;
} else if cli_args.no_only_folders {
Expand Down
1 change: 0 additions & 1 deletion src/tree_build/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,6 @@ impl<'c> TreeBuilder<'c> {
})
}

///
pub fn build_paths<F>(
mut self,
total_search: bool,
Expand Down
5 changes: 4 additions & 1 deletion src/verb/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ macro_rules! Internals {
// internals:
// name: "description" needs_a_path
Internals! {
apply_flags: "apply flags (eg `-sd` to show sizes and dates)" false,
back: "revert to the previous state (mapped to *esc*)" false,
clear_output: "clear the --verb-output file" false,
clear_stage: "empty the staging area" false,
close_panel_cancel: "close the panel, not using the selected path" false,
close_panel_ok: "close the panel, validating the selected path" false,
Expand Down Expand Up @@ -150,13 +152,13 @@ Internals! {
unstage: "remove selection from staging area" true,
up_tree: "focus the parent of the current root" true,
write_output: "write the argument to the --verb-output file" false,
clear_output: "clear the --verb-output file" false,
//restore_pattern: "restore a pattern which was just removed" false,
}

impl Internal {
pub fn invocation_pattern(self) -> &'static str {
match self {
Internal::apply_flags => r"-(?P<flags>\w+)?",
Internal::focus => r"focus (?P<path>.*)?",
Internal::select => r"select (?P<path>.*)?",
Internal::line_down => r"line_down (?P<count>\d*)?",
Expand All @@ -170,6 +172,7 @@ impl Internal {
}
pub fn exec_pattern(self) -> &'static str {
match self {
Internal::apply_flags => r"apply_flags {flags}",
Internal::focus => r"focus {path}",
Internal::line_down => r"line_down {count}",
Internal::line_up => r"line_up {count}",
Expand Down
17 changes: 16 additions & 1 deletion src/verb/verb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ impl Verb {
let invocation_parser = invocation_str.map(InvocationParser::new).transpose()?;
let mut names = Vec::new();
if let Some(ref invocation_parser) = invocation_parser {
names.push(invocation_parser.name().to_string());
let name = invocation_parser.name().to_string();
check_verb_name(&name)?;
names.push(name);
}
let (
needs_selection,
Expand Down Expand Up @@ -143,6 +145,11 @@ impl Verb {
self.show_in_doc = false;
self
}
pub fn with_name(&mut self, name: &str) -> Result<&mut Self, ConfError> {
check_verb_name(name)?;
self.names.insert(0, name.to_string());
Ok(self)
}
pub fn with_description(&mut self, description: &str) -> &mut Self {
self.description = VerbDescription::from_text(description.to_string());
self
Expand Down Expand Up @@ -300,3 +307,11 @@ impl Verb {
}
}
}

pub fn check_verb_name(name: &str) -> Result<(), ConfError> {
if regex_is_match!(r"^([@,#~&'%$\dù_-]+|[\w][\w_@,#~&'%$\dù_-]*)+$", name) {
Ok(())
} else {
Err(ConfError::InvalidVerbName{ name: name.to_string() })
}
}
Loading

0 comments on commit 3c3f4e6

Please sign in to comment.