feat: launch from preset settings if the user's config cannot be parsed (#1832)

This commit is contained in:
三咲雅 · Misaki Masa 2024-10-25 16:27:26 +08:00 committed by GitHub
parent 81fd949016
commit 60cfa7dba9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 187 additions and 81 deletions

18
Cargo.lock generated
View File

@ -61,9 +61,9 @@ dependencies = [
[[package]]
name = "ansi-to-tui"
version = "6.0.1"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd01ee70df708c9ecb68705af08cee6fe85493992a7e205c52ddbe569314ba47"
checksum = "67555e1f1ece39d737e28c8a017721287753af3f93225e4a445b29ccb0f5912c"
dependencies = [
"nom",
"ratatui",
@ -1137,6 +1137,12 @@ dependencies = [
"serde",
]
[[package]]
name = "indoc"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]]
name = "inotify"
version = "0.10.2"
@ -1909,23 +1915,23 @@ dependencies = [
[[package]]
name = "ratatui"
version = "0.28.1"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d"
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
dependencies = [
"bitflags 2.6.0",
"cassowary",
"compact_str",
"crossterm",
"indoc",
"instability",
"itertools 0.13.0",
"lru",
"paste",
"strum",
"strum_macros",
"unicode-segmentation",
"unicode-truncate",
"unicode-width 0.1.14",
"unicode-width 0.2.0",
]
[[package]]

View File

@ -10,7 +10,7 @@ panic = "abort"
strip = true
[workspace.dependencies]
ansi-to-tui = "6.0.0"
ansi-to-tui = "7.0.0"
anyhow = "1.0.91"
arc-swap = "1.7.1"
base64 = "0.22.1"
@ -24,7 +24,7 @@ libc = "0.2.161"
md-5 = "0.10.6"
mlua = { version = "0.9.9", features = [ "lua54", "serialize", "macros", "async" ] }
parking_lot = "0.12.3"
ratatui = { version = "0.28.1", features = [ "unstable-rendered-line-info" ] }
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info" ] }
regex = "1.11.1"
scopeguard = "1.2.0"
serde = { version = "1.0.213", features = [ "derive" ] }

View File

@ -1,5 +1,6 @@
use std::{collections::HashSet, str::FromStr};
use anyhow::Context;
use indexmap::IndexSet;
use serde::{Deserialize, Deserializer};
use yazi_shared::Layer;
@ -20,7 +21,7 @@ pub struct Keymap {
impl Keymap {
#[inline]
pub fn get(&self, layer: Layer) -> &Vec<Chord> {
pub fn get(&self, layer: Layer) -> &[Chord] {
match layer {
Layer::App => unreachable!(),
Layer::Manager => &self.manager,
@ -36,9 +37,11 @@ impl Keymap {
}
impl FromStr for Keymap {
type Err = toml::de::Error;
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
fn from_str(s: &str) -> Result<Self, Self::Err> {
toml::from_str(s).context("Failed to parse your keymap.toml")
}
}
impl<'de> Deserialize<'de> for Keymap {

View File

@ -24,25 +24,25 @@ pub static PICK: RoCell<popup::Pick> = RoCell::new();
pub static WHICH: RoCell<which::Which> = RoCell::new();
pub fn init() -> anyhow::Result<()> {
let config_dir = Xdg::config_dir();
let yazi_toml = &Preset::yazi(&config_dir)?;
let keymap_toml = &Preset::keymap(&config_dir)?;
let theme_toml = &Preset::theme(&config_dir)?;
if let Err(e) = try_init(true) {
eprintln!("{e}");
if let Some(src) = e.source() {
eprintln!("\nCaused by:\n{src}");
}
LAYOUT.with(<_>::default);
use crossterm::style::{Attribute, Print, SetAttributes};
crossterm::execute!(
std::io::stderr(),
SetAttributes(Attribute::Reverse.into()),
SetAttributes(Attribute::Bold.into()),
Print("Press <Enter> to continue with preset settings..."),
SetAttributes(Attribute::Reset.into()),
Print("\n"),
)?;
KEYMAP.init(<_>::from_str(keymap_toml)?);
LOG.init(<_>::from_str(yazi_toml)?);
MANAGER.init(<_>::from_str(yazi_toml)?);
OPEN.init(<_>::from_str(yazi_toml)?);
PLUGIN.init(<_>::from_str(yazi_toml)?);
PREVIEW.init(<_>::from_str(yazi_toml)?);
TASKS.init(<_>::from_str(yazi_toml)?);
THEME.init(<_>::from_str(theme_toml)?);
INPUT.init(<_>::from_str(yazi_toml)?);
CONFIRM.init(<_>::from_str(yazi_toml)?);
PICK.init(<_>::from_str(yazi_toml)?);
WHICH.init(<_>::from_str(yazi_toml)?);
std::io::stdin().read_line(&mut String::new())?;
try_init(false)?;
}
// TODO: Remove in v0.3.2
for c in &KEYMAP.manager {
@ -78,3 +78,43 @@ Please change `create_title = "Create:"` to `create_title = ["Create:", "Create
Ok(())
}
fn try_init(merge: bool) -> anyhow::Result<()> {
let (yazi_toml, keymap_toml, theme_toml) = if merge {
let p = Xdg::config_dir();
(Preset::yazi(&p)?, Preset::keymap(&p)?, Preset::theme(&p)?)
} else {
use yazi_macro::config_preset as preset;
(preset!("yazi"), preset!("keymap"), preset!("theme"))
};
let keymap = <_>::from_str(&keymap_toml)?;
let log = <_>::from_str(&yazi_toml)?;
let manager = <_>::from_str(&yazi_toml)?;
let open = <_>::from_str(&yazi_toml)?;
let plugin = <_>::from_str(&yazi_toml)?;
let preview = <_>::from_str(&yazi_toml)?;
let tasks = <_>::from_str(&yazi_toml)?;
let theme = <_>::from_str(&theme_toml)?;
let input = <_>::from_str(&yazi_toml)?;
let confirm = <_>::from_str(&yazi_toml)?;
let pick = <_>::from_str(&yazi_toml)?;
let which = <_>::from_str(&yazi_toml)?;
LAYOUT.with(<_>::default);
KEYMAP.init(keymap);
LOG.init(log);
MANAGER.init(manager);
OPEN.init(open);
PLUGIN.init(plugin);
PREVIEW.init(preview);
TASKS.init(tasks);
THEME.init(theme);
INPUT.init(input);
CONFIRM.init(confirm);
PICK.init(pick);
WHICH.init(which);
Ok(())
}

View File

@ -1,5 +1,6 @@
use std::str::FromStr;
use anyhow::Context;
use serde::{Deserialize, Deserializer};
#[derive(Debug)]
@ -8,9 +9,11 @@ pub struct Log {
}
impl FromStr for Log {
type Err = toml::de::Error;
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
fn from_str(s: &str) -> Result<Self, Self::Err> {
toml::from_str(s).context("Failed to parse the [log] section in your yazi.toml")
}
}
impl<'de> Deserialize<'de> for Log {

View File

@ -1,5 +1,6 @@
use std::str::FromStr;
use anyhow::Context;
use serde::{Deserialize, Serialize};
use validator::Validate;
@ -35,9 +36,10 @@ impl FromStr for Manager {
manager: Manager,
}
let manager = toml::from_str::<Outer>(s)?.manager;
manager.validate()?;
let outer = toml::from_str::<Outer>(s)
.context("Failed to parse the [manager] section in your yazi.toml")?;
outer.manager.validate()?;
Ok(manager)
Ok(outer.manager)
}
}

View File

@ -1,5 +1,6 @@
use std::{collections::HashMap, path::Path, str::FromStr};
use anyhow::Context;
use indexmap::IndexSet;
use serde::{Deserialize, Deserializer};
use yazi_shared::MIME_DIR;
@ -55,9 +56,11 @@ impl Open {
}
impl FromStr for Open {
type Err = toml::de::Error;
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { toml::from_str(s) }
fn from_str(s: &str) -> Result<Self, Self::Err> {
toml::from_str(s).context("Failed to parse the [open] or [opener] section in your yazi.toml")
}
}
impl<'de> Deserialize<'de> for Open {

View File

@ -1,11 +1,11 @@
use std::{collections::HashSet, path::Path, str::FromStr};
use serde::Deserialize;
use anyhow::Context;
use serde::{Deserialize, Deserializer};
use super::{Fetcher, Preloader, Previewer};
use crate::{Preset, plugin::MAX_PREWORKERS};
#[derive(Deserialize)]
pub struct Plugin {
pub fetchers: Vec<Fetcher>,
pub preloaders: Vec<Preloader>,
@ -55,9 +55,18 @@ impl Plugin {
}
impl FromStr for Plugin {
type Err = toml::de::Error;
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
toml::from_str(s).context("Failed to parse the [plugin] section in your yazi.toml")
}
}
impl<'de> Deserialize<'de> for Plugin {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Outer {
plugin: Shadow,
@ -84,7 +93,7 @@ impl FromStr for Plugin {
append_previewers: Vec<Previewer>,
}
let mut shadow = toml::from_str::<Outer>(s)?.plugin;
let mut shadow = Outer::deserialize(deserializer)?.plugin;
if shadow.append_previewers.iter().any(|r| r.any_file()) {
shadow.previewers.retain(|r| !r.any_file());
}

View File

@ -1,5 +1,6 @@
use std::str::FromStr;
use anyhow::Context;
use serde::Deserialize;
use super::{Offset, Origin};
@ -30,7 +31,7 @@ pub struct Confirm {
}
impl FromStr for Confirm {
type Err = toml::de::Error;
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[derive(Deserialize)]
@ -38,7 +39,10 @@ impl FromStr for Confirm {
confirm: Confirm,
}
Ok(toml::from_str::<Outer>(s)?.confirm)
let outer = toml::from_str::<Outer>(s)
.context("Failed to parse the [confirm] section in your yazi.toml")?;
Ok(outer.confirm)
}
}

View File

@ -1,5 +1,6 @@
use std::str::FromStr;
use anyhow::Context;
use serde::Deserialize;
use super::{Offset, Origin};
@ -49,7 +50,7 @@ impl Input {
}
impl FromStr for Input {
type Err = toml::de::Error;
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[derive(Deserialize)]
@ -57,7 +58,10 @@ impl FromStr for Input {
input: Input,
}
Ok(toml::from_str::<Outer>(s)?.input)
let outer = toml::from_str::<Outer>(s)
.context("Failed to parse the [input] section in your yazi.toml")?;
Ok(outer.input)
}
}

View File

@ -1,5 +1,6 @@
use std::str::FromStr;
use anyhow::Context;
use serde::Deserialize;
use super::{Offset, Origin};
@ -17,7 +18,7 @@ impl Pick {
}
impl FromStr for Pick {
type Err = toml::de::Error;
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
#[derive(Deserialize)]
@ -25,6 +26,9 @@ impl FromStr for Pick {
pick: Pick,
}
Ok(toml::from_str::<Outer>(s)?.pick)
let outer =
toml::from_str::<Outer>(s).context("Failed to parse the [pick] section in your yazi.toml")?;
Ok(outer.pick)
}
}

View File

@ -9,15 +9,15 @@ use crate::theme::Flavor;
pub(crate) struct Preset;
impl Preset {
pub(crate) fn yazi(p: &Path) -> Result<Cow<str>> {
pub(crate) fn yazi(p: &Path) -> Result<Cow<'static, str>> {
Self::merge_path(p.join("yazi.toml"), preset!("yazi"))
}
pub(crate) fn keymap(p: &Path) -> Result<Cow<str>> {
pub(crate) fn keymap(p: &Path) -> Result<Cow<'static, str>> {
Self::merge_path(p.join("keymap.toml"), preset!("keymap"))
}
pub(crate) fn theme(p: &Path) -> Result<Cow<str>> {
pub(crate) fn theme(p: &Path) -> Result<Cow<'static, str>> {
let Ok(user) = std::fs::read_to_string(p.join("theme.toml")) else {
return Ok(preset!("theme"));
};

View File

@ -1,7 +1,7 @@
use std::{borrow::Cow, path::PathBuf, str::FromStr, time::{SystemTime, UNIX_EPOCH}};
use anyhow::Context;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use validator::Validate;
use yazi_shared::fs::expand_path;
@ -49,6 +49,20 @@ impl FromStr for Preview {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let preview: Self =
toml::from_str(s).context("Failed to parse the [preview] section in your yazi.toml")?;
std::fs::create_dir_all(&preview.cache_dir).context("Failed to create cache directory")?;
Ok(preview)
}
}
impl<'de> Deserialize<'de> for Preview {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Outer {
preview: Shadow,
@ -74,27 +88,26 @@ impl FromStr for Preview {
ueberzug_offset: (f32, f32, f32, f32),
}
let preview = toml::from_str::<Outer>(s)?.preview;
preview.validate()?;
let cache_dir =
preview.cache_dir.filter(|p| !p.is_empty()).map_or_else(Xdg::cache_dir, expand_path);
std::fs::create_dir_all(&cache_dir).context("Failed to create cache directory")?;
let preview = Outer::deserialize(deserializer)?.preview;
preview.validate().map_err(serde::de::Error::custom)?;
Ok(Preview {
wrap: preview.wrap,
tab_size: preview.tab_size,
max_width: preview.max_width,
wrap: preview.wrap,
tab_size: preview.tab_size,
max_width: preview.max_width,
max_height: preview.max_height,
cache_dir,
cache_dir: preview
.cache_dir
.filter(|p| !p.is_empty())
.map_or_else(Xdg::cache_dir, expand_path),
image_delay: preview.image_delay,
image_filter: preview.image_filter,
image_quality: preview.image_quality,
image_delay: preview.image_delay,
image_filter: preview.image_filter,
image_quality: preview.image_quality,
sixel_fraction: preview.sixel_fraction,
ueberzug_scale: preview.ueberzug_scale,
ueberzug_scale: preview.ueberzug_scale,
ueberzug_offset: preview.ueberzug_offset,
})
}

View File

@ -1,5 +1,6 @@
use std::str::FromStr;
use anyhow::Context;
use serde::Deserialize;
use validator::Validate;
@ -27,9 +28,10 @@ impl FromStr for Tasks {
tasks: Tasks,
}
let tasks = toml::from_str::<Outer>(s)?.tasks;
tasks.validate()?;
let outer = toml::from_str::<Outer>(s)
.context("Failed to parse the [tasks] section in your yazi.toml")?;
outer.tasks.validate()?;
Ok(tasks)
Ok(outer.tasks)
}
}

View File

@ -1,5 +1,6 @@
use std::{path::PathBuf, str::FromStr};
use anyhow::Context;
use serde::{Deserialize, Serialize};
use validator::Validate;
use yazi_shared::{Xdg, fs::expand_path, theme::Style};
@ -31,7 +32,7 @@ impl FromStr for Theme {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut theme: Self = toml::from_str(s)?;
let mut theme: Self = toml::from_str(s).context("Failed to parse your yazi.toml")?;
theme.manager.validate()?;
theme.which.validate()?;

View File

@ -1,5 +1,6 @@
use std::str::FromStr;
use anyhow::Context;
use serde::{Deserialize, Serialize};
use validator::Validate;
@ -23,6 +24,9 @@ impl FromStr for Which {
which: Which,
}
Ok(toml::from_str::<Outer>(s)?.which)
let outer = toml::from_str::<Outer>(s)
.context("Failed to parse the [which] section in your yazi.toml")?;
Ok(outer.which)
}
}

View File

@ -105,7 +105,7 @@ function M:tidy(from, to, tmp)
fs.remove("dir", tmp)
end
function M.tmp_name(url) return ".tmp_" .. ya.md5(string.format("extract//%s//%.10f", url, ya.time())) end
function M.tmp_name(url) return ".tmp_" .. ya.hash(string.format("extract//%s//%.10f", url, ya.time())) end
function M.trim_ext(name)
-- stylua: ignore

View File

@ -9,17 +9,17 @@ use crate::elements::Constraint;
pub struct Table {
area: Rect,
rows: Vec<ratatui::widgets::Row<'static>>,
header: Option<ratatui::widgets::Row<'static>>,
footer: Option<ratatui::widgets::Row<'static>>,
widths: Vec<ratatui::layout::Constraint>,
column_spacing: u16,
block: Option<ratatui::widgets::Block<'static>>,
style: ratatui::style::Style,
highlight_style: ratatui::style::Style,
highlight_symbol: ratatui::text::Text<'static>,
highlight_spacing: ratatui::widgets::HighlightSpacing,
flex: ratatui::layout::Flex,
rows: Vec<ratatui::widgets::Row<'static>>,
header: Option<ratatui::widgets::Row<'static>>,
footer: Option<ratatui::widgets::Row<'static>>,
widths: Vec<ratatui::layout::Constraint>,
column_spacing: u16,
block: Option<ratatui::widgets::Block<'static>>,
style: ratatui::style::Style,
row_highlight_style: ratatui::style::Style,
highlight_symbol: ratatui::text::Text<'static>,
highlight_spacing: ratatui::widgets::HighlightSpacing,
flex: ratatui::layout::Flex,
state: ratatui::widgets::TableState,
}
@ -61,7 +61,7 @@ impl Renderable for Table {
let mut table = ratatui::widgets::Table::new(self.rows, self.widths)
.column_spacing(self.column_spacing)
.style(self.style)
.highlight_style(self.highlight_style)
.row_highlight_style(self.row_highlight_style)
.highlight_symbol(self.highlight_symbol)
.highlight_spacing(self.highlight_spacing)
.flex(self.flex);

View File

@ -9,6 +9,7 @@ use crate::CLIPBOARD;
impl Utils {
pub(super) fn text(lua: &Lua, ya: &Table) -> mlua::Result<()> {
// TODO: deprecate this in the future
ya.raw_set(
"md5",
lua.create_async_function(|_, s: mlua::String| async move {
@ -16,6 +17,13 @@ impl Utils {
})?,
)?;
ya.raw_set(
"hash",
lua.create_async_function(|_, s: mlua::String| async move {
Ok(format!("{:x}", Md5::new_with_prefix(s.as_bytes()).finalize()))
})?,
)?;
ya.raw_set(
"quote",
lua.create_function(|_, (s, unix): (mlua::String, Option<bool>)| {