From 4d858ae1c2781d91a5e8671d4e5c4d1ee670a2f0 Mon Sep 17 00:00:00 2001 From: Lauri Niskanen Date: Wed, 10 Jul 2024 00:57:02 +0300 Subject: [PATCH] feat: show IO errors for files in the UI Resolves #1137. --- yazi-core/src/folder/files.rs | 15 ++++++++++++--- yazi-core/src/folder/folder.rs | 8 ++++---- yazi-core/src/folder/stage.rs | 4 ++-- yazi-fm/src/lives/folder.rs | 15 +++++++++++++-- yazi-plugin/preset/components/current.lua | 6 +++++- yazi-plugin/preset/plugins/folder.lua | 12 ++++++++++-- yazi-shared/src/fs/op.rs | 6 +++--- 7 files changed, 49 insertions(+), 17 deletions(-) diff --git a/yazi-core/src/folder/files.rs b/yazi-core/src/folder/files.rs index a9bb0e2f3..6ae736d93 100644 --- a/yazi-core/src/folder/files.rs +++ b/yazi-core/src/folder/files.rs @@ -46,7 +46,11 @@ impl Deref for Files { impl Files { pub async fn from_dir(url: &Url) -> std::io::Result> { - let mut it = fs::read_dir(url).await?; + let mut it = fs::read_dir(url).await.map_err(|e| { + FilesOp::IOErr(url.clone(), e.kind(), e.to_string()).emit(); + e + })?; + let (tx, rx) = mpsc::unbounded_channel(); tokio::spawn(async move { @@ -100,13 +104,18 @@ impl Files { match fs::metadata(url).await { Ok(m) if !m.is_dir() => { // FIXME: use `ErrorKind::NotADirectory` instead once it gets stabilized - FilesOp::IOErr(url.clone(), std::io::ErrorKind::AlreadyExists).emit(); + FilesOp::IOErr( + url.clone(), + std::io::ErrorKind::AlreadyExists, + "Not a directory".to_string(), + ) + .emit(); } Ok(m) if mtime == m.modified().ok() => {} Ok(m) => return Some(m), Err(e) => { if maybe_exists(url).await { - FilesOp::IOErr(url.clone(), e.kind()).emit(); + FilesOp::IOErr(url.clone(), e.kind(), e.to_string()).emit(); } else if let Some(p) = url.parent_url() { FilesOp::Deleting(p, vec![url.clone()]).emit(); } diff --git a/yazi-core/src/folder/folder.rs b/yazi-core/src/folder/folder.rs index 16178886a..b94ec1a02 100644 --- a/yazi-core/src/folder/folder.rs +++ b/yazi-core/src/folder/folder.rs @@ -28,7 +28,7 @@ impl From<&Url> for Folder { impl Folder { pub fn update(&mut self, op: FilesOp) -> bool { - let (stage, revision) = (self.stage, self.files.revision); + let (stage, revision) = (self.stage.clone(), self.files.revision); match op { FilesOp::Full(_, _, mtime) => { (self.mtime, self.stage) = (mtime, FolderStage::Loaded); @@ -39,8 +39,8 @@ impl Folder { FilesOp::Done(_, mtime, ticket) if ticket == self.files.ticket() => { (self.mtime, self.stage) = (mtime, FolderStage::Loaded); } - FilesOp::IOErr(_, kind) => { - (self.mtime, self.stage) = (None, FolderStage::Failed(kind)); + FilesOp::IOErr(_, kind, ref msg) => { + (self.mtime, self.stage) = (None, FolderStage::Failed(kind, msg.clone())); } _ => {} } @@ -59,7 +59,7 @@ impl Folder { } self.arrow(0); - (stage, revision) != (self.stage, self.files.revision) + (stage, revision) != (self.stage.clone(), self.files.revision) } pub fn arrow(&mut self, step: impl Into) -> bool { diff --git a/yazi-core/src/folder/stage.rs b/yazi-core/src/folder/stage.rs index 89f3ac504..0a70507b8 100644 --- a/yazi-core/src/folder/stage.rs +++ b/yazi-core/src/folder/stage.rs @@ -1,7 +1,7 @@ -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] pub enum FolderStage { #[default] Loading, Loaded, - Failed(std::io::ErrorKind), + Failed(std::io::ErrorKind, String), } diff --git a/yazi-fm/src/lives/folder.rs b/yazi-fm/src/lives/folder.rs index 95e3754e8..489514393 100644 --- a/yazi-fm/src/lives/folder.rs +++ b/yazi-fm/src/lives/folder.rs @@ -40,7 +40,7 @@ impl Folder { lua.register_userdata_type::(|reg| { reg.add_field_method_get("cwd", |lua, me| Url::cast(lua, me.cwd.clone())); reg.add_field_method_get("files", |_, me| Files::make(0..me.files.len(), me, me.tab())); - reg.add_field_method_get("stage", |lua, me| lua.create_any_userdata(me.stage)); + reg.add_field_method_get("stage", |lua, me| lua.create_any_userdata(me.stage.clone())); reg.add_field_method_get("window", |_, me| Files::make(me.window.clone(), me, me.tab())); reg.add_field_method_get("offset", |_, me| Ok(me.offset)); @@ -53,12 +53,23 @@ impl Folder { lua.register_userdata_type::(|reg| { reg.add_meta_method(MetaMethod::ToString, |lua, me, ()| { use yazi_core::folder::FolderStage::{Failed, Loaded, Loading}; + lua.create_string(match me { Loading => "loading", Loaded => "loaded", - Failed(_) => "failed", + Failed(..) => "failed", }) }); + + reg.add_field_method_get("error", |lua, me| { + use yazi_core::folder::FolderStage::{Failed, Loaded, Loading}; + + match me { + Loading => Ok(None), + Loaded => Ok(None), + Failed(_, msg) => Some(lua.create_string(msg)).transpose(), + } + }); })?; Ok(()) diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index e4d308835..d2bfdba99 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -14,8 +14,12 @@ function Current:empty() local line if self._folder.files.filter then line = ui.Line("No filter results") + elseif tostring(self._folder.stage) == "loading" then + line = ui.Line("Loading...") + elseif tostring(self._folder.stage) == "failed" then + line = ui.Line(self._folder.stage.error) else - line = ui.Line(self._folder.stage == "loading" and "Loading..." or "No items") + line = ui.Line("No items") end return { diff --git a/yazi-plugin/preset/plugins/folder.lua b/yazi-plugin/preset/plugins/folder.lua index 3f2064d3f..534bd15c8 100644 --- a/yazi-plugin/preset/plugins/folder.lua +++ b/yazi-plugin/preset/plugins/folder.lua @@ -12,9 +12,17 @@ function M:peek() end if #folder.files == 0 then + local line + if tostring(folder.stage) == "loading" then + line = ui.Line("Loading...") + elseif tostring(folder.stage) == "failed" then + line = ui.Line(folder.stage.error) + else + line = ui.Line("No items") + end + return ya.preview_widgets(self, { - ui.Paragraph(self.area, { ui.Line(folder.stage == "loading" and "Loading..." or "No items") }) - :align(ui.Paragraph.CENTER), + ui.Paragraph(self.area, { line }):align(ui.Paragraph.CENTER), }) end diff --git a/yazi-shared/src/fs/op.rs b/yazi-shared/src/fs/op.rs index 43c6c6c96..361f5d7cb 100644 --- a/yazi-shared/src/fs/op.rs +++ b/yazi-shared/src/fs/op.rs @@ -11,7 +11,7 @@ pub enum FilesOp { Part(Url, Vec, u64), Done(Url, Option, u64), Size(Url, HashMap), - IOErr(Url, std::io::ErrorKind), + IOErr(Url, std::io::ErrorKind, String), Creating(Url, Vec), Deleting(Url, Vec), @@ -27,7 +27,7 @@ impl FilesOp { Self::Part(url, ..) => url, Self::Done(url, ..) => url, Self::Size(url, _) => url, - Self::IOErr(url, _) => url, + Self::IOErr(url, ..) => url, Self::Creating(url, _) => url, Self::Deleting(url, _) => url, @@ -83,7 +83,7 @@ impl FilesOp { Self::Part(_, files, ticket) => Self::Part(u, files!(files), *ticket), Self::Done(_, mtime, ticket) => Self::Done(u, *mtime, *ticket), Self::Size(_, map) => Self::Size(u, map.iter().map(|(k, v)| (new!(k), *v)).collect()), - Self::IOErr(_, err) => Self::IOErr(u, *err), + Self::IOErr(_, err, msg) => Self::IOErr(u, *err, msg.clone()), Self::Creating(_, files) => Self::Creating(u, files!(files)), Self::Deleting(_, urls) => Self::Deleting(u, urls.iter().map(|u| new!(u)).collect()),