1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 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
 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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use {
    crate::{
        app_context::AppContext,
        app_state::AppStateCmdResult,
        browser_states::BrowserState,
        commands::Command,
        errors::ProgramError,
        external,
        flat_tree::Tree,
        help_states::HelpState,
        screens::Screen,
        task_sync::TaskLifetime,
        tree_options::{OptionBool, TreeOptions},
        verb_invocation::VerbInvocation,
        verbs::{Verb, VerbExecutor},
    },
    directories::UserDirs,
    std::path::PathBuf,
};

fn focus_path(path: PathBuf, screen: &mut Screen, tree: &Tree) -> AppStateCmdResult {
    AppStateCmdResult::from_optional_state(
        BrowserState::new(
            path,
            tree.options.clone(),
            screen,
            &TaskLifetime::unlimited(),
        ),
        Command::from(&tree.options.pattern),
    )
}

impl VerbExecutor for BrowserState {
    fn execute_verb(
        &mut self,
        verb: &Verb,
        invocation: &VerbInvocation,
        screen: &mut Screen,
        con: &AppContext,
    ) -> Result<AppStateCmdResult, ProgramError> {
        if let Some(err) = verb.match_error(invocation) {
            return Ok(AppStateCmdResult::DisplayError(err));
        }
        let page_height = BrowserState::page_height(screen);
        Ok(match verb.execution.as_ref() {
            ":back" => AppStateCmdResult::PopState,
            ":focus" => {
                let tree = self.displayed_tree_mut();
                let line = &tree.selected_line();
                let mut path = line.target();
                if !path.is_dir() {
                    path = path.parent().unwrap().to_path_buf();
                }
                focus_path(path, screen, tree)
            }
            ":focus_root" => focus_path(PathBuf::from("/"), screen, self.displayed_tree()),
            ":up_tree" => match self.displayed_tree().root().parent() {
                Some(path) => focus_path(path.to_path_buf(), screen, self.displayed_tree()),
                None => AppStateCmdResult::DisplayError("no parent found".to_string()),
            },
            ":focus_user_home" => match UserDirs::new() {
                Some(ud) => focus_path(ud.home_dir().to_path_buf(), screen, self.displayed_tree()),
                None => AppStateCmdResult::DisplayError("no user home directory found".to_string()),
            },
            ":help" => {
                AppStateCmdResult::NewState(Box::new(HelpState::new(screen, con)), Command::new())
            }
            ":open_stay" => self.open_selection_stay_in_broot(screen, con)?,
            ":open_leave" => self.open_selection_quit_broot(screen, con)?,
            ":line_down" => {
                self.displayed_tree_mut().move_selection(1, page_height);
                AppStateCmdResult::Keep
            }
            ":line_up" => {
                self.displayed_tree_mut().move_selection(-1, page_height);
                AppStateCmdResult::Keep
            }
            ":page_down" => {
                let tree = self.displayed_tree_mut();
                if page_height < tree.lines.len() as i32 {
                    tree.try_scroll(page_height, page_height);
                }
                AppStateCmdResult::Keep
            }
            ":page_up" => {
                let tree = self.displayed_tree_mut();
                if page_height < tree.lines.len() as i32 {
                    tree.try_scroll(-page_height, page_height);
                }
                AppStateCmdResult::Keep
            }
            ":parent" => match &self.displayed_tree().selected_line().path.parent() {
                Some(path) => AppStateCmdResult::from_optional_state(
                    BrowserState::new(
                        path.to_path_buf(),
                        self.displayed_tree().options.without_pattern(),
                        screen,
                        &TaskLifetime::unlimited(),
                    ),
                    Command::new(),
                ),
                None => AppStateCmdResult::DisplayError("no parent found".to_string()),
            },
            ":print_path" => {
                external::print_path(&self.displayed_tree().selected_line().target(), con)?
            }
            ":print_tree" => external::print_tree(&self.displayed_tree(), screen, con)?,
            ":refresh" => AppStateCmdResult::RefreshState { clear_cache: true },
            ":select_first" => {
                self.displayed_tree_mut().try_select_first();
                AppStateCmdResult::Keep
            }
            ":select_last" => {
                self.displayed_tree_mut().try_select_last();
                AppStateCmdResult::Keep
            }
            ":toggle_dates" => self.with_new_options(screen, &|o| o.show_dates ^= true),
            ":toggle_files" => {
                self.with_new_options(screen, &|o: &mut TreeOptions| o.only_folders ^= true)
            }
            ":toggle_hidden" => self.with_new_options(screen, &|o| o.show_hidden ^= true),
            ":toggle_git_ignore" => self.with_new_options(screen, &|options| {
                options.respect_git_ignore = match options.respect_git_ignore {
                    OptionBool::Auto => {
                        if self.displayed_tree().nb_gitignored > 0 {
                            OptionBool::No
                        } else {
                            OptionBool::Yes
                        }
                    }
                    OptionBool::Yes => OptionBool::No,
                    OptionBool::No => OptionBool::Yes,
                };
            }),
            ":toggle_perm" => self.with_new_options(screen, &|o| o.show_permissions ^= true),
            ":toggle_sizes" => self.with_new_options(screen, &|o| o.show_sizes ^= true),
            ":toggle_trim_root" => self.with_new_options(screen, &|o| o.trim_root ^= true),
            ":total_search" => {
                if let Some(tree) = &self.filtered_tree {
                    if tree.total_search {
                        AppStateCmdResult::DisplayError(
                            "search was already total - all children have been rated".to_owned(),
                        )
                    } else {
                        self.pending_pattern = tree.options.pattern.clone();
                        self.total_search_required = true;
                        AppStateCmdResult::Keep
                    }
                } else {
                    AppStateCmdResult::DisplayError(
                        "this verb can be used only after a search".to_owned(),
                    )
                }
            }
            ":quit" => AppStateCmdResult::Quit,
            _ => verb.to_cmd_result(
                &self.displayed_tree().selected_line().path.clone(),
                &invocation.args,
                screen,
                con,
            )?,
        })
    }
}