feat: show IO errors for files in the UI

Resolves #1137.
This commit is contained in:
Lauri Niskanen 2024-07-10 00:57:02 +03:00
parent d6081fbe6f
commit 4d858ae1c2
No known key found for this signature in database
GPG key ID: 807DCEAC3AEB1848
7 changed files with 49 additions and 17 deletions

View file

@ -46,7 +46,11 @@ impl Deref for Files {
impl Files {
pub async fn from_dir(url: &Url) -> std::io::Result<UnboundedReceiver<File>> {
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();
}

View file

@ -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<Step>) -> bool {

View file

@ -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),
}

View file

@ -40,7 +40,7 @@ impl Folder {
lua.register_userdata_type::<Self>(|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::<yazi_core::folder::FolderStage>(|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(())

View file

@ -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 {

View file

@ -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

View file

@ -11,7 +11,7 @@ pub enum FilesOp {
Part(Url, Vec<File>, u64),
Done(Url, Option<SystemTime>, u64),
Size(Url, HashMap<Url, u64>),
IOErr(Url, std::io::ErrorKind),
IOErr(Url, std::io::ErrorKind, String),
Creating(Url, Vec<File>),
Deleting(Url, Vec<Url>),
@ -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()),