diff options
Diffstat (limited to 'default-plugins/strider/src/state.rs')
-rw-r--r-- | default-plugins/strider/src/state.rs | 243 |
1 files changed, 163 insertions, 80 deletions
diff --git a/default-plugins/strider/src/state.rs b/default-plugins/strider/src/state.rs index 230e92ea8..1ff583862 100644 --- a/default-plugins/strider/src/state.rs +++ b/default-plugins/strider/src/state.rs @@ -1,9 +1,9 @@ -use pretty_bytes::converter as pb; +use crate::file_list_view::{FileListView, FsEntry}; +use crate::search_view::SearchView; +use crate::shared::calculate_list_bounds; use std::{ - collections::{HashMap, VecDeque}, - fs::read_dir, + collections::BTreeMap, path::{Path, PathBuf}, - time::Instant, }; use zellij_tile::prelude::*; @@ -11,106 +11,189 @@ pub const ROOT: &str = "/host"; #[derive(Default)] pub struct State { - pub path: PathBuf, - pub files: Vec<FsEntry>, - pub cursor_hist: HashMap<PathBuf, (usize, usize)>, + pub file_list_view: FileListView, + pub search_view: SearchView, pub hide_hidden_files: bool, - pub ev_history: VecDeque<(Event, Instant)>, // stores last event, can be expanded in future pub loading: bool, pub loading_animation_offset: u8, pub should_open_floating: bool, pub current_rows: Option<usize>, + pub handling_filepick_request_from: Option<(PipeSource, BTreeMap<String, String>)>, + pub initial_cwd: PathBuf, // TODO: get this from zellij + pub is_searching: bool, + pub search_term: String, + pub close_on_selection: bool, } impl State { - pub fn selected_mut(&mut self) -> &mut usize { - &mut self.cursor_hist.entry(self.path.clone()).or_default().0 + pub fn update_search_term(&mut self, character: char) { + self.search_term.push(character); + if &self.search_term == ".." { + self.descend_to_previous_path(); + } else if &self.search_term == "/" { + self.descend_to_root_path(); + } else { + self.is_searching = true; + self.search_view + .update_search_results(&self.search_term, &self.file_list_view.files); + } } - pub fn selected(&self) -> usize { - self.cursor_hist.get(&self.path).unwrap_or(&(0, 0)).0 + pub fn handle_backspace(&mut self) { + if self.search_term.is_empty() { + self.descend_to_previous_path(); + } else { + self.search_term.pop(); + if self.search_term.is_empty() { + self.is_searching = false; + } + self.search_view + .update_search_results(&self.search_term, &self.file_list_view.files); + } } - pub fn scroll_mut(&mut self) -> &mut usize { - &mut self.cursor_hist.entry(self.path.clone()).or_default().1 + pub fn clear_search_term_or_descend(&mut self) { + if self.search_term.is_empty() { + self.descend_to_previous_path(); + } else { + self.search_term.clear(); + self.search_view + .update_search_results(&self.search_term, &self.file_list_view.files); + self.is_searching = false; + } } - pub fn scroll(&self) -> usize { - self.cursor_hist.get(&self.path).unwrap_or(&(0, 0)).1 + pub fn move_selection_up(&mut self) { + if self.is_searching { + self.search_view.move_selection_up(); + } else { + self.file_list_view.move_selection_up(); + } + } + pub fn move_selection_down(&mut self) { + if self.is_searching { + self.search_view.move_selection_down(); + } else { + self.file_list_view.move_selection_down(); + } + } + pub fn handle_left_click(&mut self, line: isize) { + if let Some(current_rows) = self.current_rows { + let rows_for_list = current_rows.saturating_sub(5); + if self.is_searching { + let (start_index, _selected_index_in_range, _end_index) = calculate_list_bounds( + self.search_view.search_result_count(), + rows_for_list, + Some(self.search_view.selected_search_result), + ); + let prev_selected = self.search_view.selected_search_result; + self.search_view.selected_search_result = + (line as usize).saturating_sub(2) + start_index; + if prev_selected == self.search_view.selected_search_result { + self.traverse_dir(); + } + } else { + let (start_index, _selected_index_in_range, _end_index) = calculate_list_bounds( + self.file_list_view.files.len(), + rows_for_list, + self.file_list_view.selected(), + ); + let prev_selected = self.file_list_view.selected(); + *self.file_list_view.selected_mut() = + (line as usize).saturating_sub(2) + start_index; + if prev_selected == self.file_list_view.selected() { + self.traverse_dir(); + } + } + } + } + pub fn descend_to_previous_path(&mut self) { + self.search_term.clear(); + self.search_view.clear_and_reset_selection(); + self.file_list_view.descend_to_previous_path(); + } + pub fn descend_to_root_path(&mut self) { + self.search_term.clear(); + self.search_view.clear_and_reset_selection(); + self.file_list_view.descend_to_root_path(); + refresh_directory(&self.file_list_view.path); } pub fn toggle_hidden_files(&mut self) { self.hide_hidden_files = !self.hide_hidden_files; } - pub fn traverse_dir_or_open_file(&mut self) { - if let Some(f) = self.files.get(self.selected()) { - match f.clone() { - FsEntry::Dir(p) => { - self.path = p; - refresh_directory(self); + pub fn traverse_dir(&mut self) { + let entry = if self.is_searching { + self.search_view.get_selected_entry() + } else { + self.file_list_view.get_selected_entry() + }; + if let Some(entry) = entry { + match &entry { + FsEntry::Dir(_p) => { + self.file_list_view.enter_dir(&entry); + self.search_view.clear_and_reset_selection(); + refresh_directory(&self.file_list_view.path); + }, + FsEntry::File(_p, _) => { + self.file_list_view.enter_dir(&entry); + self.search_view.clear_and_reset_selection(); }, - FsEntry::File(p, _) => open_file(FileToOpen { - path: p.strip_prefix(ROOT).unwrap().into(), - ..Default::default() - }), } } + self.is_searching = false; + self.search_term.clear(); + self.search_view.clear_and_reset_selection(); } -} - -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)] -pub enum FsEntry { - Dir(PathBuf), - File(PathBuf, u64), -} - -impl FsEntry { - pub fn name(&self) -> String { - let path = match self { - FsEntry::Dir(p) => p, - FsEntry::File(p, _) => p, - }; - path.file_name().unwrap().to_string_lossy().into_owned() + pub fn update_files(&mut self, paths: Vec<(PathBuf, Option<FileMetadata>)>) { + self.file_list_view + .update_files(paths, self.hide_hidden_files); } - - pub fn as_line(&self, width: usize) -> String { - let info = match self { - FsEntry::Dir(_s) => "".to_owned(), - FsEntry::File(_, s) => pb::convert(*s as f64), - }; - let space = width.saturating_sub(info.len()); - let name = self.name(); - if space.saturating_sub(1) < name.len() { - [&name[..space.saturating_sub(2)], &info].join("~ ") + pub fn open_selected_path(&mut self) { + if self.file_list_view.path_is_dir { + open_terminal(&self.file_list_view.path); } else { - let padding = " ".repeat(space - name.len()); - [name, padding, info].concat() + if let Some(parent_folder) = self.file_list_view.path.parent() { + open_file( + FileToOpen::new(&self.file_list_view.path).with_cwd(parent_folder.into()), + ); + } else { + open_file(FileToOpen::new(&self.file_list_view.path)); + } + } + if self.close_on_selection { + close_focus(); } } - - pub fn is_hidden_file(&self) -> bool { - self.name().starts_with('.') + pub fn send_filepick_response(&mut self) { + let selected_path = self.initial_cwd.join( + self.file_list_view + .path + .strip_prefix(ROOT) + .map(|p| p.to_path_buf()) + .unwrap_or_else(|_| self.file_list_view.path.clone()), + ); + match &self.handling_filepick_request_from { + Some((PipeSource::Plugin(plugin_id), args)) => { + pipe_message_to_plugin( + MessageToPlugin::new("filepicker_result") + .with_destination_plugin_id(*plugin_id) + .with_args(args.clone()) + .with_payload(selected_path.display().to_string()), + ); + #[cfg(target_family = "wasm")] + close_focus(); + }, + Some((PipeSource::Cli(pipe_id), _args)) => { + #[cfg(target_family = "wasm")] + cli_pipe_output(pipe_id, &selected_path.display().to_string()); + #[cfg(target_family = "wasm")] + unblock_cli_pipe_input(pipe_id); + #[cfg(target_family = "wasm")] + close_focus(); + }, + _ => {}, + } } } -pub(crate) fn refresh_directory(state: &mut State) { - // TODO: might be good to do this asynchronously with a worker - let mut max_lines = (state.current_rows.unwrap_or(50) + state.scroll()) * 2; // 100 is arbitrary for performance reasons - let mut files = vec![]; - for entry in read_dir(Path::new(ROOT).join(&state.path)).unwrap() { - if let Ok(entry) = entry { - if max_lines == 0 { - break; - } - let entry_metadata = entry.metadata().unwrap(); - let entry = if entry_metadata.is_dir() { - FsEntry::Dir(entry.path()) - } else { - let size = entry_metadata.len(); - FsEntry::File(entry.path(), size) - }; - if !entry.is_hidden_file() || !state.hide_hidden_files { - max_lines = max_lines.saturating_sub(1); - files.push(entry); - } - } - } - state.files = files; - state.files.sort_unstable(); +pub(crate) fn refresh_directory(path: &Path) { + let path_on_host = Path::new(ROOT).join(path.strip_prefix("/").unwrap_or(path)); + scan_host_folder(&path_on_host); } |