summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJeff Zhao <jeff.no.zhao@gmail.com>2022-08-31 12:13:55 -0400
committerJeff Zhao <jeff.no.zhao@gmail.com>2022-08-31 12:13:55 -0400
commit39b131a910ab8da1df2385280367e3fcc0750fb6 (patch)
treeaeb6640522e08a5242cbc2143fd294dc5cedb407 /src
parent0ca4ffd657f1a2884ca8422be8a78baa42f00de4 (diff)
more fine grain error handling for previewing directories
Diffstat (limited to 'src')
-rw-r--r--src/event/app_event.rs8
-rw-r--r--src/event/process_event.rs42
-rw-r--r--src/preview/preview_default.rs34
-rw-r--r--src/preview/preview_dir.rs20
-rw-r--r--src/tab/tab_struct.rs15
-rw-r--r--src/ui/views/tui_folder_view.rs59
-rw-r--r--src/ui/widgets/mod.rs2
-rw-r--r--src/ui/widgets/tui_message.rs27
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);
+ }
+}