diff options
author | Jeff Zhao <jeff.no.zhao@gmail.com> | 2022-08-31 12:13:55 -0400 |
---|---|---|
committer | Jeff Zhao <jeff.no.zhao@gmail.com> | 2022-08-31 12:13:55 -0400 |
commit | 39b131a910ab8da1df2385280367e3fcc0750fb6 (patch) | |
tree | aeb6640522e08a5242cbc2143fd294dc5cedb407 | |
parent | 0ca4ffd657f1a2884ca8422be8a78baa42f00de4 (diff) |
more fine grain error handling for previewing directories
-rw-r--r-- | src/event/app_event.rs | 8 | ||||
-rw-r--r-- | src/event/process_event.rs | 42 | ||||
-rw-r--r-- | src/preview/preview_default.rs | 34 | ||||
-rw-r--r-- | src/preview/preview_dir.rs | 20 | ||||
-rw-r--r-- | src/tab/tab_struct.rs | 15 | ||||
-rw-r--r-- | src/ui/views/tui_folder_view.rs | 59 | ||||
-rw-r--r-- | src/ui/widgets/mod.rs | 2 | ||||
-rw-r--r-- | src/ui/widgets/tui_message.rs | 27 |
8 files changed, 171 insertions, 36 deletions
diff --git a/src/event/app_event.rs b/src/event/app_event.rs index 05f5d02..4153f0b 100644 --- a/src/event/app_event.rs +++ b/src/event/app_event.rs @@ -10,6 +10,8 @@ use signal_hook::iterator::SignalsInfo; use termion::event::Event; use termion::input::TermRead; +use uuid::Uuid; + use crate::fs::JoshutoDirList; use crate::io::FileOperationProgress; use crate::preview::preview_file::FilePreview; @@ -28,7 +30,11 @@ pub enum AppEvent { ChildProcessComplete(u32), // preview thread events - PreviewDir(io::Result<Box<JoshutoDirList>>), + PreviewDir { + id: Uuid, + path: path::PathBuf, + res: Box<io::Result<JoshutoDirList>>, + }, PreviewFile(path::PathBuf, Box<io::Result<FilePreview>>), // terminal size change events diff --git a/src/event/process_event.rs b/src/event/process_event.rs index 446b98c..80970b9 100644 --- a/src/event/process_event.rs +++ b/src/event/process_event.rs @@ -1,9 +1,11 @@ -use notify; -use signal_hook::consts::signal; use std::io; use std::path; + +use notify; +use signal_hook::consts::signal; use termion::event::{Event, Key, MouseButton, MouseEvent}; use tui::layout::{Constraint, Direction, Layout}; +use uuid::Uuid; use crate::commands::{cursor_move, parent_cursor_move, reload}; use crate::config::{AppKeyMapping, KeyMapping}; @@ -13,6 +15,7 @@ use crate::fs::JoshutoDirList; use crate::history::DirectoryHistory; use crate::io::{FileOperation, FileOperationProgress}; use crate::key_command::{AppExecute, Command, CommandKeybind}; +use crate::preview::preview_default::PreviewState; use crate::preview::preview_file::FilePreview; use crate::ui; use crate::ui::views::TuiCommandMenu; @@ -58,7 +61,7 @@ pub fn process_noninteractive(event: AppEvent, context: &mut AppContext) { AppEvent::IoWorkerCreate => process_new_worker(context), AppEvent::FileOperationProgress(res) => process_worker_progress(context, res), AppEvent::IoWorkerResult(res) => process_finished_worker(context, res), - AppEvent::PreviewDir(Ok(b)) => process_dir_preview(context, *b), + AppEvent::PreviewDir { id, path, res } => process_dir_preview(context, id, path, res), AppEvent::PreviewFile(path, b) => process_file_preview(context, path, *b), AppEvent::Signal(signal::SIGWINCH) => {} AppEvent::Filesystem(e) => process_filesystem_event(e, context), @@ -141,11 +144,36 @@ pub fn process_finished_worker( } } -pub fn process_dir_preview(context: &mut AppContext, dirlist: JoshutoDirList) { - let history = context.tab_context_mut().curr_tab_mut().history_mut(); +pub fn process_dir_preview( + context: &mut AppContext, + id: Uuid, + path: path::PathBuf, + res: Box<io::Result<JoshutoDirList>>, +) { + for (tab_id, tab) in context.tab_context_mut().iter_mut() { + if *tab_id == id { + match *res { + Ok(dirlist) => { + // remove from loading state + tab.history_metadata_mut().remove(dirlist.file_path()); - let dir_path = dirlist.file_path().to_path_buf(); - history.insert(dir_path, dirlist); + let history = tab.history_mut(); + let dir_path = dirlist.file_path().to_path_buf(); + history.insert(dir_path, dirlist); + } + Err(e) => { + // set to false so we don't load again + tab.history_metadata_mut().insert( + path, + PreviewState::Error { + message: e.to_string(), + }, + ); + } + } + break; + } + } } pub fn process_file_preview( diff --git a/src/preview/preview_default.rs b/src/preview/preview_default.rs index bb5ea64..9f2fb66 100644 --- a/src/preview/preview_default.rs +++ b/src/preview/preview_default.rs @@ -4,17 +4,37 @@ use crate::context::AppContext; use crate::fs::JoshutoMetadata; use crate::preview::{preview_dir, preview_file}; +#[derive(Debug, Clone)] +pub enum PreviewState { + Loading, + Error { message: String }, +} + +impl PreviewState { + pub fn is_loading(&self) -> bool { + match *self { + Self::Loading => true, + _ => false, + } + } +} + pub fn load_preview_path(context: &mut AppContext, p: path::PathBuf, metadata: JoshutoMetadata) { let preview_options = context.config_ref().preview_options_ref(); - if metadata.is_dir() { - let need_to_load = context - .tab_context_ref() - .curr_tab_ref() - .history_ref() + let tab = context.tab_context_ref().curr_tab_ref(); + // only load if there doesn't already exist a loading thread and + // there isn't an entry in history + let need_to_load = tab + .history_metadata_ref() .get(p.as_path()) - .map(|e| e.need_update()) - .unwrap_or(true); + .map(|m| m.is_loading()) + .unwrap_or(true) + && tab + .history_ref() + .get(p.as_path()) + .map(|e| e.need_update()) + .unwrap_or(true); if need_to_load { preview_dir::Background::load_preview(context, p); diff --git a/src/preview/preview_dir.rs b/src/preview/preview_dir.rs index cf592a9..ab0931b 100644 --- a/src/preview/preview_dir.rs +++ b/src/preview/preview_dir.rs @@ -4,6 +4,7 @@ use std::thread; use crate::context::AppContext; use crate::event::AppEvent; use crate::fs::JoshutoDirList; +use crate::preview::preview_default::PreviewState; pub struct Background {} @@ -16,11 +17,24 @@ impl Background { .curr_tab_ref() .option_ref() .clone(); + let tab_id = context.tab_context_ref().curr_tab_id(); + + // add to loading state + context + .tab_context_mut() + .curr_tab_mut() + .history_metadata_mut() + .insert(p.clone(), PreviewState::Loading); thread::spawn(move || { - if let Ok(dirlist) = JoshutoDirList::from_path(p, &options, &tab_options) { - let _ = event_tx.send(AppEvent::PreviewDir(Ok(Box::new(dirlist)))); - } + let path_clone = p.clone(); + let dir_res = JoshutoDirList::from_path(p, &options, &tab_options); + let res = AppEvent::PreviewDir { + id: tab_id, + path: path_clone, + res: Box::new(dir_res), + }; + let _ = event_tx.send(res); }) } } diff --git a/src/tab/tab_struct.rs b/src/tab/tab_struct.rs index b1aee8c..61ebe49 100644 --- a/src/tab/tab_struct.rs +++ b/src/tab/tab_struct.rs @@ -1,15 +1,20 @@ +use std::collections::HashMap; use std::path; use crate::config::option::{DisplayOption, TabDisplayOption}; use crate::context::UiContext; use crate::fs::JoshutoDirList; use crate::history::{DirectoryHistory, JoshutoHistory}; +use crate::preview::preview_default::PreviewState; + +type HistoryMetadata = HashMap<path::PathBuf, PreviewState>; pub struct JoshutoTab { _cwd: path::PathBuf, // history is just a HashMap, so we have this property to store last workdir _previous_dir: Option<path::PathBuf>, history: JoshutoHistory, + history_metadata: HistoryMetadata, options: TabDisplayOption, } @@ -24,9 +29,10 @@ impl JoshutoTab { history.populate_to_root(cwd.as_path(), ui_context, options, &tab_options)?; let new_tab = Self { - history, _cwd: cwd, _previous_dir: None, + history, + history_metadata: HashMap::new(), options: tab_options, }; @@ -64,6 +70,13 @@ impl JoshutoTab { &mut self.history } + pub fn history_metadata_ref(&self) -> &HistoryMetadata { + &self.history_metadata + } + pub fn history_metadata_mut(&mut self) -> &mut HistoryMetadata { + &mut self.history_metadata + } + pub fn curr_list_ref(&self) -> Option<&JoshutoDirList> { self.history.get(self.cwd()) } diff --git a/src/ui/views/tui_folder_view.rs b/src/ui/views/tui_folder_view.rs index dd5e2e7..f1f4c88 100644 --- a/src/ui/views/tui_folder_view.rs +++ b/src/ui/views/tui_folder_view.rs @@ -6,10 +6,11 @@ use tui::text::Span; use tui::widgets::{Block, Borders, Paragraph, Widget, Wrap}; use crate::context::AppContext; +use crate::preview::preview_default::PreviewState; use crate::ui; use crate::ui::widgets::{ - TuiDirList, TuiDirListDetailed, TuiDirListLoading, TuiFilePreview, TuiFooter, TuiTabBar, - TuiTopBar, + TuiDirList, TuiDirListDetailed, TuiDirListLoading, TuiFilePreview, TuiFooter, TuiMessage, + TuiTabBar, TuiTopBar, }; use crate::ui::PreviewArea; @@ -34,6 +35,7 @@ impl<'a> Widget for TuiFolderView<'a> { let preview_context = self.context.preview_context_ref(); let curr_tab = self.context.tab_context_ref().curr_tab_ref(); let curr_tab_id = self.context.tab_context_ref().curr_tab_id(); + let curr_tab_cwd = curr_tab.cwd(); let curr_list = curr_tab.curr_list_ref(); let child_list = curr_tab.child_list_ref(); @@ -139,27 +141,50 @@ impl<'a> Widget for TuiFolderView<'a> { TuiFooter::new(list).render(rect, buf); } } + } else { + match curr_tab.history_metadata_ref().get(curr_tab_cwd) { + Some(PreviewState::Loading) => { + TuiDirListLoading::new().render(layout_rect[1], buf); + } + Some(PreviewState::Error { message }) => { + TuiMessage::new(&message, Style::default().fg(Color::Red)) + .render(layout_rect[1], buf); + } + None => {} + } } if let Some(list) = child_list.as_ref() { TuiDirList::new(list, true).render(layout_rect[2], buf); - } else if curr_entry.is_some() { - let preview_area = calculate_preview(self.context, layout_rect[2]); - if let Some(preview_area) = preview_area { - let area = Rect { - x: preview_area.preview_area.x, - y: preview_area.preview_area.y, - width: preview_area.preview_area.width, - height: preview_area.preview_area.height, - }; - if let Some(Some(preview)) = - preview_context.get_preview_ref(&preview_area.file_preview_path) - { - TuiFilePreview::new(preview).render(area, buf); + } else if let Some(entry) = curr_entry { + match curr_tab.history_metadata_ref().get(entry.file_path()) { + Some(PreviewState::Loading) => { + TuiDirListLoading::new().render(layout_rect[2], buf); + } + Some(PreviewState::Error { message }) => { + TuiMessage::new(&message, Style::default().fg(Color::Red)) + .render(layout_rect[2], buf); + } + None => { + let preview_area = calculate_preview(self.context, layout_rect[2]); + if let Some(preview_area) = preview_area { + let area = Rect { + x: preview_area.preview_area.x, + y: preview_area.preview_area.y, + width: preview_area.preview_area.width, + height: preview_area.preview_area.height, + }; + if let Some(Some(preview)) = + preview_context.get_preview_ref(&preview_area.file_preview_path) + { + TuiFilePreview::new(preview).render(area, buf); + } + } } } } else { - TuiDirListLoading::new().render(layout_rect[2], buf); + TuiMessage::new("Error loading directory", Style::default().fg(Color::Red)) + .render(layout_rect[2], buf); } let topbar_width = area.width; @@ -169,7 +194,7 @@ impl<'a> Widget for TuiFolderView<'a> { width: topbar_width, height: 1, }; - TuiTopBar::new(self.context, curr_tab.cwd()).render(rect, buf); + TuiTopBar::new(self.context, curr_tab_cwd).render(rect, buf); // render tabs if self.context.tab_context_ref().len() > 1 { diff --git a/src/ui/widgets/mod.rs b/src/ui/widgets/mod.rs index ab74629..1871da6 100644 --- a/src/ui/widgets/mod.rs +++ b/src/ui/widgets/mod.rs @@ -5,6 +5,7 @@ mod tui_file_preview; mod tui_footer; mod tui_help; mod tui_menu; +mod tui_message; mod tui_prompt; mod tui_tab; mod tui_text; @@ -18,6 +19,7 @@ pub use self::tui_file_preview::TuiFilePreview; pub use self::tui_footer::TuiFooter; pub use self::tui_help::{get_keymap_table, TuiHelp}; pub use self::tui_menu::TuiMenu; +pub use self::tui_message::TuiMessage; pub use self::tui_prompt::TuiPrompt; pub use self::tui_tab::TuiTabBar; pub use self::tui_text::TuiMultilineText; diff --git a/src/ui/widgets/tui_message.rs b/src/ui/widgets/tui_message.rs new file mode 100644 index 0000000..d4d245c --- /dev/null +++ b/src/ui/widgets/tui_message.rs @@ -0,0 +1,27 @@ +use tui::buffer::Buffer; +use tui::layout::Rect; +use tui::style::Style; +use tui::widgets::Widget; + +pub struct TuiMessage<'a> { + message: &'a str, + style: Style, +} + +impl<'a> TuiMessage<'a> { + pub fn new(message: &'a str, style: Style) -> Self { + Self { message, style } + } +} + +impl<'a> Widget for TuiMessage<'a> { + fn render(self, area: Rect, buf: &mut Buffer) { + if area.width < 4 || area.height < 1 { + return; + } + let x = area.left(); + let y = area.top(); + + buf.set_stringn(x, y, self.message, area.width as usize, self.style); + } +} |