Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: flavor #753

Merged
merged 5 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions yazi-boot/src/boot.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{ffi::OsString, fs, path::{Path, PathBuf}, process};
use std::{ffi::OsString, path::{Path, PathBuf}, process};

use clap::Parser;
use serde::Serialize;
Expand All @@ -16,7 +16,6 @@ pub struct Boot {
pub config_dir: PathBuf,
pub flavor_dir: PathBuf,
pub plugin_dir: PathBuf,
pub state_dir: PathBuf,
}

impl Boot {
Expand All @@ -37,7 +36,7 @@ impl Boot {

impl Default for Boot {
fn default() -> Self {
let config_dir = Xdg::config_dir().unwrap();
let config_dir = Xdg::config_dir();
let (cwd, file) = Self::parse_entry(ARGS.entry.as_deref());

let boot = Self {
Expand All @@ -47,13 +46,10 @@ impl Default for Boot {
flavor_dir: config_dir.join("flavors"),
plugin_dir: config_dir.join("plugins"),
config_dir,
state_dir: Xdg::state_dir().unwrap(),
};

fs::create_dir_all(&boot.flavor_dir).expect("Failed to create flavor directory");
fs::create_dir_all(&boot.plugin_dir).expect("Failed to create plugin directory");
fs::create_dir_all(&boot.state_dir).expect("Failed to create state directory");

std::fs::create_dir_all(&boot.flavor_dir).expect("Failed to create flavor directory");
std::fs::create_dir_all(&boot.plugin_dir).expect("Failed to create plugin directory");
boot
}
}
Expand All @@ -75,7 +71,7 @@ impl Default for Args {
if args.clear_cache {
if PREVIEW.cache_dir == Xdg::cache_dir() {
println!("Clearing cache directory: \n{:?}", PREVIEW.cache_dir);
fs::remove_dir_all(&PREVIEW.cache_dir).unwrap();
std::fs::remove_dir_all(&PREVIEW.cache_dir).unwrap();
} else {
println!(
"You've changed the default cache directory, for your data's safety, please clear it manually: \n{:?}",
Expand Down
7 changes: 7 additions & 0 deletions yazi-config/preset/theme.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

# vim:fileencoding=utf-8:foldmethod=marker

# : Flavor {{{

[flavor]
use = ""

# : }}}

# : Manager {{{

[manager]
Expand Down
16 changes: 8 additions & 8 deletions yazi-config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(clippy::module_inception)]

use yazi_shared::RoCell;
use yazi_shared::{RoCell, Xdg};

pub mod keymap;
mod layout;
Expand All @@ -23,11 +23,11 @@ pub(crate) use pattern::*;
pub(crate) use preset::*;
pub use priority::*;

pub static LAYOUT: RoCell<arc_swap::ArcSwap<Layout>> = RoCell::new();

static MERGED_YAZI: RoCell<String> = RoCell::new();
static MERGED_KEYMAP: RoCell<String> = RoCell::new();
static MERGED_THEME: RoCell<String> = RoCell::new();
static MERGED_YAZI: RoCell<String> = RoCell::new();

pub static LAYOUT: RoCell<arc_swap::ArcSwap<Layout>> = RoCell::new();

pub static KEYMAP: RoCell<keymap::Keymap> = RoCell::new();
pub static LOG: RoCell<log::Log> = RoCell::new();
Expand All @@ -42,12 +42,12 @@ pub static SELECT: RoCell<popup::Select> = RoCell::new();
pub static WHICH: RoCell<which::Which> = RoCell::new();

pub fn init() {
LAYOUT.with(Default::default);

let config_dir = yazi_shared::Xdg::config_dir().unwrap();
let config_dir = Xdg::config_dir();
MERGED_YAZI.init(Preset::yazi(&config_dir));
MERGED_KEYMAP.init(Preset::keymap(&config_dir));
MERGED_THEME.init(Preset::theme(&config_dir));
MERGED_YAZI.init(Preset::yazi(&config_dir));

LAYOUT.with(Default::default);

KEYMAP.with(Default::default);
LOG.with(Default::default);
Expand Down
56 changes: 39 additions & 17 deletions yazi-config/src/preset.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,60 @@
use std::{mem, path::{Path, PathBuf}};

use anyhow::Context;
use toml::{Table, Value};

use crate::theme::Flavor;

pub(crate) struct Preset;

impl Preset {
#[inline]
pub(crate) fn keymap(dir: &Path) -> String {
Self::merge_str(dir.join("keymap.toml"), include_str!("../preset/keymap.toml"))
pub(crate) fn yazi(p: &Path) -> String {
Self::merge_path(p.join("yazi.toml"), include_str!("../preset/yazi.toml"))
}

#[inline]
pub(crate) fn theme(dir: &Path) -> String {
Self::merge_str(dir.join("theme.toml"), include_str!("../preset/theme.toml"))
pub(crate) fn keymap(p: &Path) -> String {
Self::merge_path(p.join("keymap.toml"), include_str!("../preset/keymap.toml"))
}

#[inline]
pub(crate) fn yazi(dir: &Path) -> String {
Self::merge_str(dir.join("yazi.toml"), include_str!("../preset/yazi.toml"))
pub(crate) fn theme(p: &Path) -> String {
let Ok(user) = std::fs::read_to_string(p.join("theme.toml")) else {
return include_str!("../preset/theme.toml").to_owned();
};
let Some(use_) = Flavor::parse_use(&user) else {
return Self::merge_str(&user, include_str!("../preset/theme.toml"));
};

let p = p.join(format!("flavors/{}.yazi/flavor.toml", use_));
let flavor = std::fs::read_to_string(&p)
.with_context(|| format!("Failed to load flavor {:?}", p))
.unwrap();

Self::merge_str(&user, &Self::merge_str(&flavor, include_str!("../preset/theme.toml")))
}

#[inline]
pub(crate) fn mix<T>(a: &mut Vec<T>, b: Vec<T>, c: Vec<T>) {
*a = b.into_iter().chain(mem::take(a)).chain(c).collect();
}

#[inline]
pub(crate) fn merge_str(user: &str, base: &str) -> String {
let mut t = user.parse().unwrap();
Self::merge(&mut t, base.parse().unwrap(), 2);

t.to_string()
}

#[inline]
fn merge_path(user: PathBuf, base: &str) -> String {
let s = std::fs::read_to_string(user).unwrap_or_default();
if s.is_empty() {
return base.to_string();
}

Self::merge_str(&s, base)
}

fn merge(a: &mut Table, b: Table, max: u8) {
for (k, v) in b {
let Some(a) = a.get_mut(&k) else {
Expand All @@ -45,12 +75,4 @@ impl Preset {
*a = v;
}
}

fn merge_str(user: PathBuf, base: &str) -> String {
let mut user = std::fs::read_to_string(user).unwrap_or_default().parse::<Table>().unwrap();
let base = base.parse::<Table>().unwrap();

Self::merge(&mut user, base, 2);
user.to_string()
}
}
4 changes: 2 additions & 2 deletions yazi-config/src/preview/preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use std::{fs, path::PathBuf, time::{self, SystemTime}};

use serde::{Deserialize, Serialize};
use validator::Validate;
use yazi_shared::{fs::expand_path, Xdg};
use yazi_shared::fs::expand_path;

use crate::{validation::check_validation, MERGED_YAZI};
use crate::{validation::check_validation, Xdg, MERGED_YAZI};

#[derive(Debug, Serialize)]
pub struct Preview {
Expand Down
23 changes: 23 additions & 0 deletions yazi-config/src/theme/flavor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize)]
pub struct Flavor {
#[serde(rename = "use")]
pub use_: String,
}

impl Flavor {
pub fn parse_use(s: &str) -> Option<String> {
#[derive(Deserialize)]
struct Outer {
flavor: Inner,
}
#[derive(Deserialize)]
struct Inner {
#[serde(rename = "use")]
pub use_: String,
}

toml::from_str::<Outer>(s).ok().map(|o| o.flavor.use_).filter(|s| !s.is_empty())
}
}
2 changes: 2 additions & 0 deletions yazi-config/src/theme/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
mod color;
mod filetype;
mod flavor;
mod icon;
mod is;
mod style;
mod theme;

pub use color::*;
pub use filetype::*;
pub use flavor::*;
pub use icon::*;
pub use is::*;
pub use style::*;
Expand Down
72 changes: 39 additions & 33 deletions yazi-config/src/theme/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,48 @@ use std::path::PathBuf;

use serde::{Deserialize, Serialize};
use validator::Validate;
use yazi_shared::fs::expand_path;
use yazi_shared::{fs::expand_path, Xdg};

use super::{Filetype, Icon, Style};
use super::{Filetype, Flavor, Icon, Style};
use crate::{validation::check_validation, MERGED_THEME};

#[derive(Deserialize, Serialize)]
pub struct Theme {
pub flavor: Flavor,
pub manager: Manager,
status: Status,
pub input: Input,
pub select: Select,
pub completion: Completion,
pub tasks: Tasks,
pub which: Which,
pub help: Help,

// File-specific styles
#[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)]
pub filetypes: Vec<Filetype>,
#[serde(rename = "icon", deserialize_with = "Icon::deserialize", skip_serializing)]
pub icons: Vec<Icon>,
}

impl Default for Theme {
fn default() -> Self {
let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap();

check_validation(theme.manager.validate());
check_validation(theme.which.validate());

if theme.flavor.use_.is_empty() {
theme.manager.syntect_theme = expand_path(&theme.manager.syntect_theme);
} else {
theme.manager.syntect_theme =
Xdg::config_dir().join(format!("flavors/{}.yazi/tmtheme.xml", theme.flavor.use_));
}

theme
}
}

#[derive(Deserialize, Serialize, Validate)]
pub struct Manager {
cwd: Style,
Expand Down Expand Up @@ -123,34 +160,3 @@ pub struct Help {
pub hovered: Style,
pub footer: Style,
}

#[derive(Deserialize, Serialize)]
pub struct Theme {
pub manager: Manager,
status: Status,
pub input: Input,
pub select: Select,
pub completion: Completion,
pub tasks: Tasks,
pub which: Which,
pub help: Help,

// File-specific styles
#[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)]
pub filetypes: Vec<Filetype>,
#[serde(rename = "icon", deserialize_with = "Icon::deserialize", skip_serializing)]
pub icons: Vec<Icon>,
}

impl Default for Theme {
fn default() -> Self {
let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap();

check_validation(theme.manager.validate());
check_validation(theme.which.validate());

theme.manager.syntect_theme = expand_path(&theme.manager.syntect_theme);

theme
}
}
6 changes: 1 addition & 5 deletions yazi-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,4 @@ pub mod which;
pub use clipboard::*;
pub use step::*;

pub fn init() {
CLIPBOARD.with(Default::default);

yazi_scheduler::init();
}
pub fn init() { CLIPBOARD.with(Default::default); }
5 changes: 4 additions & 1 deletion yazi-fm/src/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ pub(super) struct Logs;

impl Logs {
pub(super) fn start() {
let appender = tracing_appender::rolling::never(Xdg::state_dir().unwrap(), "yazi.log");
let state_dir = Xdg::state_dir();
std::fs::create_dir_all(&state_dir).expect("Failed to create state directory");

let appender = tracing_appender::rolling::never(state_dir, "yazi.log");
let (handle, guard) = tracing_appender::non_blocking(appender);

// let filter = EnvFilter::from_default_env();
Expand Down
18 changes: 16 additions & 2 deletions yazi-shared/src/ro_cell.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{cell::UnsafeCell, fmt::{self, Display}, ops::Deref};
use std::{cell::UnsafeCell, fmt::{self, Display}, mem, ops::Deref};

// Read-only cell. It's safe to use this in a static variable, but it's not safe
// to mutate it. This is useful for storing static data that is expensive to
Expand All @@ -13,6 +13,7 @@ impl<T> RoCell<T> {

#[inline]
pub fn init(&self, value: T) {
debug_assert!(!self.is_initialized());
unsafe {
*self.0.get() = Some(value);
}
Expand All @@ -26,18 +27,31 @@ impl<T> RoCell<T> {
self.init(f());
}

#[inline]
pub fn replace(&self, value: T) -> T {
debug_assert!(self.is_initialized());
unsafe { mem::replace(&mut *self.0.get(), Some(value)).unwrap_unchecked() }
}

#[inline]
pub fn drop(&self) {
unsafe {
*self.0.get() = None;
}
}

#[cfg(debug_assertions)]
#[inline]
fn is_initialized(&self) -> bool { unsafe { (*self.0.get()).is_some() } }
}

impl<T> Deref for RoCell<T> {
type Target = T;

fn deref(&self) -> &Self::Target { unsafe { (*self.0.get()).as_ref().unwrap() } }
fn deref(&self) -> &Self::Target {
debug_assert!(self.is_initialized());
unsafe { (*self.0.get()).as_ref().unwrap_unchecked() }
}
}

impl<T> Display for RoCell<T>
Expand Down
Loading