Struct broot::verb::VerbStore

source ·
pub struct VerbStore {
    pub verbs: Vec<Verb>,
}
Expand description

Provide access to the verbs:

  • the built-in ones
  • the user defined ones A user defined verb can replace a built-in. When the user types some keys, we select a verb
  • if the input exactly matches a shortcut or the name
  • if only one verb name starts with the input

Fields§

§verbs: Vec<Verb>

Implementations§

Examples found in repository?
src/cli/mod.rs (line 96)
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
pub fn run() -> Result<Option<Launchable>, ProgramError> {

    // parse the launch arguments we got from cli
    let args = Args::parse();
    let mut must_quit = false;

    if let Some(dir) = &args.write_default_conf {
        write_default_conf_in(dir)?;
        must_quit = true;
    }

    // read the install related arguments
    let install_args = InstallLaunchArgs::from(&args)?;

    // execute installation things required by launch args
    if let Some(state) = install_args.set_install_state {
        write_state(state)?;
        must_quit = true;
    }
    if let Some(shell) = &install_args.print_shell_function {
        ShellInstall::print(shell)?;
        must_quit = true;
    }
    if must_quit {
        return Ok(None);
    }

    // read the list of specific config files
    let specific_conf: Option<Vec<PathBuf>> = args.conf
        .as_ref()
        .map(|s| s.split(';').map(PathBuf::from).collect());

    // if we don't run on a specific config file, we check the
    // configuration
    if specific_conf.is_none() && install_args.install != Some(false) {
        let mut shell_install = ShellInstall::new(install_args.install == Some(true));
        shell_install.check()?;
        if shell_install.should_quit {
            return Ok(None);
        }
    }

    // read the configuration file(s): either the standard one
    // or the ones required by the launch args
    let mut config = match &specific_conf {
        Some(conf_paths) => {
            let mut conf = Conf::default();
            for path in conf_paths {
                conf.read_file(path.to_path_buf())?;
            }
            conf
        }
        _ => time!(Conf::from_default_location())?,
    };
    debug!("config: {:#?}", &config);

    // verb store is completed from the config file(s)
    let verb_store = VerbStore::new(&mut config)?;

    let mut context = AppContext::from(args, verb_store, &config)?;

    #[cfg(unix)]
    if let Some(server_name) = &context.launch_args.send {
        use crate::{
            command::Sequence,
            net::{Client, Message},
        };
        let client = Client::new(server_name);
        if let Some(seq) = &context.launch_args.cmd {
            let message = Message::Sequence(Sequence::new_local(seq.to_string()));
            client.send(&message)?;
        } else if !context.launch_args.get_root {
            let message = Message::Command(
                format!(":focus {}", context.initial_root.to_string_lossy())
            );
            client.send(&message)?;
        };
        if context.launch_args.get_root {
            client.send(&Message::GetRoot)?;
        }
        return Ok(None);
    }

    let mut w = display::writer();
    let app = App::new(&context)?;
    w.queue(EnterAlternateScreen)?;
    w.queue(cursor::Hide)?;
    if context.capture_mouse {
        w.queue(EnableMouseCapture)?;
    }
    let r = app.run(&mut w, &mut context, &config);
    if context.capture_mouse {
        w.queue(DisableMouseCapture)?;
    }
    w.queue(cursor::Show)?;
    w.queue(LeaveAlternateScreen)?;
    w.flush()?;
    r
}
Examples found in repository?
src/verb/verb_store.rs (line 68)
63
64
65
66
67
68
69
70
71
72
    pub fn search_sel_info_unique <'v>(
        &'v self,
        prefix: &str,
        sel_info: SelInfo<'_>,
    ) -> Option<&'v Verb> {
        match self.search_sel_info(prefix, sel_info) {
            PrefixSearchResult::Match(_, verb) => Some(verb),
            _ => None,
        }
    }
More examples
Hide additional examples
src/command/completion.rs (line 92)
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
    fn for_verb(
        start: &str,
        con: &AppContext,
        sel_info: SelInfo<'_>,
    ) -> Self {
        match con.verb_store.search_sel_info(start, sel_info) {
            PrefixSearchResult::NoMatch => Self::None,
            PrefixSearchResult::Match(name, _) => {
                if start.len() >= name.len() {
                    debug_assert!(name == start);
                    Self::None
                } else {
                    Self::Common(name[start.len()..].to_string())
                }
            }
            PrefixSearchResult::Matches(completions) => Self::for_wholes(
                start,
                completions,
            ),
        }
    }

    fn list_for_path(
        verb_name: &str,
        arg: &str,
        path: &Path,
        sel_info: SelInfo<'_>,
        con: &AppContext,
    ) -> io::Result<Vec<String>> {
        let anchor = match con.verb_store.search_sel_info(verb_name, sel_info) {
            PrefixSearchResult::Match(_, verb) => verb.get_unique_arg_anchor(),
            _ => PathAnchor::Unspecified,
        };
        let (_, parent_part, child_part) = regex_captures!(r"^(.*?)([^/]*)$", arg).unwrap();
        let parent = path::path_from(path, anchor, parent_part);
        let mut children = Vec::new();
        if !parent.exists() {
            debug!("no path completion possible because {:?} doesn't exist", &parent);
        } else {
            for entry in parent.read_dir()? {
                let entry = entry?;
                let mut name = entry.file_name().to_string_lossy().to_string();
                if !child_part.is_empty() {
                    if !name.starts_with(child_part) {
                        continue;
                    }
                    if name == child_part && entry.file_type()?.is_dir() {
                        name = "/".to_string();
                    } else {
                        name.drain(0..child_part.len());
                    }
                }
                children.push(name);
            }
        }
        Ok(children)
    }
src/app/panel_state.rs (lines 775-778)
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
    fn on_command(
        &mut self,
        w: &mut W,
        app_state: &mut AppState,
        cc: &CmdContext,
    ) -> Result<CmdResult, ProgramError> {
        self.clear_pending();
        let con = &cc.app.con;
        let screen = cc.app.screen;
        match &cc.cmd {
            Command::Click(x, y) => self.on_click(*x, *y, screen, con),
            Command::DoubleClick(x, y) => self.on_double_click(*x, *y, screen, con),
            Command::PatternEdit { raw, expr } => {
                match InputPattern::new(raw.clone(), expr, con) {
                    Ok(pattern) => self.on_pattern(pattern, app_state, con),
                    Err(e) => Ok(CmdResult::DisplayError(format!("{}", e))),
                }
            }
            Command::VerbTrigger {
                index,
                input_invocation,
            } => self.execute_verb(
                w,
                &con.verb_store.verbs[*index],
                input_invocation.as_ref(),
                TriggerType::Other,
                app_state,
                cc,
            ),
            Command::Internal {
                internal,
                input_invocation,
            } => self.on_internal(
                w,
                &InternalExecution::from_internal(*internal),
                input_invocation.as_ref(),
                TriggerType::Other,
                app_state,
                cc,
            ),
            Command::VerbInvocate(invocation) => {
                let sel_info = self.sel_info(app_state);
                match con.verb_store.search_sel_info(
                    &invocation.name,
                    sel_info,
                ) {
                    PrefixSearchResult::Match(_, verb) => {
                        self.execute_verb(
                            w,
                            verb,
                            Some(invocation),
                            TriggerType::Input(verb),
                            app_state,
                            cc,
                        )
                    }
                    _ => Ok(CmdResult::verb_not_found(&invocation.name)),
                }
            }
            Command::None | Command::VerbEdit(_) => {
                // we do nothing here, the real job is done in get_status
                Ok(CmdResult::Keep)
            }
        }
    }

    /// return a cmdresult asking for the opening of a preview
    fn open_preview(
        &mut self,
        prefered_mode: Option<PreviewMode>,
        close_if_open: bool,
        cc: &CmdContext,
    ) -> CmdResult {
        if let Some(id) = cc.app.preview_panel {
            if close_if_open {
                CmdResult::ClosePanel {
                    validate_purpose: false,
                    panel_ref: PanelReference::Id(id),
                }
            } else {
                if prefered_mode.is_some() {
                    // we'll make the preview mode change be
                    // applied on the preview panel
                    CmdResult::ApplyOnPanel { id }
                } else {
                    CmdResult::Keep
                }
            }
        } else {
            if let Some(path) = self.selected_path() {
                if path.is_file() {
                    CmdResult::NewPanel {
                        state: Box::new(PreviewState::new(
                            path.to_path_buf(),
                            InputPattern::none(),
                            prefered_mode,
                            self.tree_options(),
                            cc.app.con,
                        )),
                        purpose: PanelPurpose::Preview,
                        direction: HDir::Right,
                    }
                } else {
                    CmdResult::error("only regular files can be previewed")
                }
            } else {
                CmdResult::error("no selected file")
            }
        }
    }

    /// must return None if the state doesn't display a file tree
    fn tree_root(&self) -> Option<&Path> {
        None
    }

    fn selected_path(&self) -> Option<&Path>;

    fn selection(&self) -> Option<Selection<'_>>;

    fn sel_info<'c>(&'c self, _app_state: &'c AppState) -> SelInfo<'c> {
        // overloaded in stage_state
        match self.selection() {
            None => SelInfo::None,
            Some(selection) => SelInfo::One(selection),
        }
    }

    fn has_at_least_one_selection(&self, _app_state: &AppState) -> bool {
        true // overloaded in stage_state
    }

    fn refresh(&mut self, screen: Screen, con: &AppContext) -> Command;

    fn tree_options(&self) -> TreeOptions;

    /// Build a cmdResult in response to a command being a change of
    /// tree options. This may or not be a new state.
    ///
    /// The provided `change_options` function returns a status message
    /// explaining the change
    fn with_new_options(
        &mut self,
        screen: Screen,
        change_options: &dyn Fn(&mut TreeOptions) -> &'static str,
        in_new_panel: bool,
        con: &AppContext,
    ) -> CmdResult;

    fn do_pending_task(
        &mut self,
        _app_state: &mut AppState,
        _screen: Screen,
        _con: &AppContext,
        _dam: &mut Dam,
    ) -> Result<(), ProgramError> {
        // no pending task in default impl
        unreachable!();
    }

    fn get_pending_task(
        &self,
    ) -> Option<&'static str> {
        None
    }

    fn display(
        &mut self,
        w: &mut W,
        disc: &DisplayContext,
    ) -> Result<(), ProgramError>;

    /// return the flags to display
    fn get_flags(&self) -> Vec<Flag> {
        vec![]
    }

    fn get_starting_input(&self) -> String {
        String::new()
    }

    fn set_selected_path(&mut self, _path: PathBuf, _con: &AppContext) {
        // this function is useful for preview states
    }

    /// return the status which should be used when there's no verb edited
    fn no_verb_status(
        &self,
        _has_previous_state: bool,
        _con: &AppContext,
    ) -> Status {
        Status::from_message(
            "Hit *esc* to get back, or a space to start a verb"
        )
    }

    fn get_status(
        &self,
        app_state: &AppState,
        cc: &CmdContext,
        has_previous_state: bool,
    ) -> Status {
        match &cc.cmd {
            Command::PatternEdit { .. } => self.no_verb_status(has_previous_state, cc.app.con),
            Command::VerbEdit(invocation) => {
                if invocation.name.is_empty() {
                    Status::new(
                        "Type a verb then *enter* to execute it (*?* for the list of verbs)",
                        false,
                    )
                } else {
                    let sel_info = self.sel_info(app_state);
                    match cc.app.con.verb_store.search_sel_info(
                        &invocation.name,
                        sel_info,
                    ) {
                        PrefixSearchResult::NoMatch => {
                            Status::new("No matching verb (*?* for the list of verbs)", true)
                        }
                        PrefixSearchResult::Match(_, verb) => {
                            self.get_verb_status(verb, invocation, sel_info, cc, app_state)
                        }
                        PrefixSearchResult::Matches(completions) => Status::new(
                            format!(
                                "Possible verbs: {}",
                                completions
                                    .iter()
                                    .map(|c| format!("*{}*", c))
                                    .collect::<Vec<String>>()
                                    .join(", "),
                            ),
                            false,
                        ),
                    }
                }
            }
            _ => self.no_verb_status(has_previous_state, cc.app.con),
        }
    }
Examples found in repository?
src/command/sequence.rs (line 91)
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
fn add_commands(
    input: &str,
    commands: &mut Vec<(String, Command)>,
    con: &AppContext,
) -> Result<(), ProgramError> {
    let raw_parts = CommandParts::from(input.to_string());
    let (pattern, verb_invocation) = raw_parts.split();
    if let Some(pattern) = pattern {
        commands.push((input.to_string(), Command::from_parts(pattern, false)));
    }
    if let Some(verb_invocation) = verb_invocation {
        let command = Command::from_parts(verb_invocation, true);
        if let Command::VerbInvocate(invocation) = &command {
            // we check that the verb exists to avoid running a sequence
            // of actions with some missing
            match con.verb_store.search_prefix(&invocation.name) {
                PrefixSearchResult::NoMatch => {
                    return Err(ProgramError::UnknownVerb {
                        name: invocation.name.to_string(),
                    });
                }
                PrefixSearchResult::Matches(_) => {
                    return Err(ProgramError::AmbiguousVerbName {
                        name: invocation.name.to_string(),
                    });
                }
                _ => {}
            }
            commands.push((input.to_string(), command));
        }
    }
    Ok(())
}

Return either the only match, or None if there’s not exactly one match

Examples found in repository?
src/command/completion.rs (line 159)
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
    fn for_arg(
        verb_name: &str,
        arg: &str,
        con: &AppContext,
        sel_info: SelInfo<'_>,
    ) -> Self {
        if arg.contains(' ') {
            return Self::None;
        }
        // we try to get the type of argument
        let arg_def = con
            .verb_store
            .search_sel_info_unique(verb_name, sel_info)
            .and_then(|verb| verb.invocation_parser.as_ref())
            .and_then(|invocation_parser| invocation_parser.get_unique_arg_def());
        if matches!(arg_def, Some(ArgDef::Theme)) {
            Self::for_theme_arg(arg)
        } else {
            Self::for_path_arg(verb_name, arg, con, sel_info)
        }
    }
Examples found in repository?
src/verb/verb_store.rs (line 51)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
    pub fn search_sel_info<'v>(
        &'v self,
        prefix: &str,
        sel_info: SelInfo<'_>,
    ) -> PrefixSearchResult<'v, &Verb> {
        let stype = sel_info.common_stype();
        let count = sel_info.count_paths();
        self.search(prefix, stype, Some(count), sel_info.extension())
    }

    pub fn search_prefix<'v>(
        &'v self,
        prefix: &str,
    ) -> PrefixSearchResult<'v, &Verb> {
        self.search(prefix, None, None, None)
    }
Examples found in repository?
src/app/standard_status.rs (line 35)
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
    pub fn new(verb_store: &VerbStore) -> Self {
        let tree_top_focus = "*enter* to go up".to_string(); // enter is hardcoded on focus
        let tree_dir_focus = "*enter* to focus".to_string();
        let tree_dir_cd = verb_store
            .key_desc_of_internal_stype(Internal::open_leave, SelectionType::Directory)
            .map(|k| format!("*{}* to cd", k));
        let tree_file_open_stay = verb_store
            .key_desc_of_internal_stype(Internal::open_stay, SelectionType::File)
            .map(|k| format!("*{}* to open the file", k));
        let tree_file_open_leave = verb_store
            .key_desc_of_internal_stype(Internal::open_leave, SelectionType::File)
            .map(|k| format!("*{}* to open and quit", k));
        //let tree_file_enter = None; // TODO (for when enter is customized)
        let tree_unfiltered = "a few letters to search".to_string();
        let tree_filtered = "*esc* to clear the filter".to_string();
        let preview_unfiltered = "a pattern to filter".to_string();
        let preview_filtered = verb_store
            .key_desc_of_internal(Internal::panel_right)
            .map(|k| format!("*{}* to reveal the text", k));
        let preview_restorable_filter = verb_store
            .key_desc_of_internal(Internal::panel_left_no_open)
            .map(|k| format!("*{}* to restore the filter", k));
        let not_first_state = "*esc* to go back".to_string();
        let help = "*?* for help".to_string();
        let no_verb = "a space then a verb".to_string();
        let all_files_hidden = verb_store
            .key_desc_of_internal(Internal::toggle_hidden)
            .map(|k| format!("Some files are hidden, use *{}* to display them", k));
        let all_files_git_ignored = verb_store
            .key_desc_of_internal(Internal::toggle_git_ignore)
            .map(|k| format!("Some files are git-ignored, use *{}* to display them", k));
        Self {
            tree_top_focus,
            tree_dir_focus,
            tree_dir_cd,
            tree_file_open_stay,
            tree_file_open_leave,
            //tree_file_enter,
            tree_unfiltered,
            tree_filtered,
            preview_unfiltered,
            preview_filtered,
            preview_restorable_filter,
            not_first_state,
            help,
            no_verb,
            all_files_hidden,
            all_files_git_ignored,
        }
    }
Examples found in repository?
src/app/standard_status.rs (line 48)
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
    pub fn new(verb_store: &VerbStore) -> Self {
        let tree_top_focus = "*enter* to go up".to_string(); // enter is hardcoded on focus
        let tree_dir_focus = "*enter* to focus".to_string();
        let tree_dir_cd = verb_store
            .key_desc_of_internal_stype(Internal::open_leave, SelectionType::Directory)
            .map(|k| format!("*{}* to cd", k));
        let tree_file_open_stay = verb_store
            .key_desc_of_internal_stype(Internal::open_stay, SelectionType::File)
            .map(|k| format!("*{}* to open the file", k));
        let tree_file_open_leave = verb_store
            .key_desc_of_internal_stype(Internal::open_leave, SelectionType::File)
            .map(|k| format!("*{}* to open and quit", k));
        //let tree_file_enter = None; // TODO (for when enter is customized)
        let tree_unfiltered = "a few letters to search".to_string();
        let tree_filtered = "*esc* to clear the filter".to_string();
        let preview_unfiltered = "a pattern to filter".to_string();
        let preview_filtered = verb_store
            .key_desc_of_internal(Internal::panel_right)
            .map(|k| format!("*{}* to reveal the text", k));
        let preview_restorable_filter = verb_store
            .key_desc_of_internal(Internal::panel_left_no_open)
            .map(|k| format!("*{}* to restore the filter", k));
        let not_first_state = "*esc* to go back".to_string();
        let help = "*?* for help".to_string();
        let no_verb = "a space then a verb".to_string();
        let all_files_hidden = verb_store
            .key_desc_of_internal(Internal::toggle_hidden)
            .map(|k| format!("Some files are hidden, use *{}* to display them", k));
        let all_files_git_ignored = verb_store
            .key_desc_of_internal(Internal::toggle_git_ignore)
            .map(|k| format!("Some files are git-ignored, use *{}* to display them", k));
        Self {
            tree_top_focus,
            tree_dir_focus,
            tree_dir_cd,
            tree_file_open_stay,
            tree_file_open_leave,
            //tree_file_enter,
            tree_unfiltered,
            tree_filtered,
            preview_unfiltered,
            preview_filtered,
            preview_restorable_filter,
            not_first_state,
            help,
            no_verb,
            all_files_hidden,
            all_files_git_ignored,
        }
    }

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The alignment of pointer.
The type for initializers.
Initializes a with the given initializer. Read more
Dereferences the given pointer. Read more
Mutably dereferences the given pointer. Read more
Drops the object pointed to by the given pointer. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.