mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
refactor: config
This commit is contained in:
parent
43ec2ada0b
commit
19fcc78a12
17 changed files with 237 additions and 230 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::BTreeMap, fmt};
|
use std::{collections::BTreeMap, fmt};
|
||||||
|
|
||||||
use serde::{de::Visitor, Deserializer};
|
use serde::{de::{self, Visitor}, Deserializer};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Exec {
|
pub struct Exec {
|
||||||
|
|
@ -44,7 +44,7 @@ impl Exec {
|
||||||
|
|
||||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
where
|
where
|
||||||
A: serde::de::SeqAccess<'de>,
|
A: de::SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut execs = Vec::new();
|
let mut execs = Vec::new();
|
||||||
while let Some(value) = &seq.next_element::<String>()? {
|
while let Some(value) = &seq.next_element::<String>()? {
|
||||||
|
|
@ -55,7 +55,7 @@ impl Exec {
|
||||||
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
where
|
where
|
||||||
E: serde::de::Error,
|
E: de::Error,
|
||||||
{
|
{
|
||||||
Ok(value.split(';').map(Exec::from).collect())
|
Ok(value.split(';').map(Exec::from).collect())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
use anyhow::bail;
|
||||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||||
use serde::{de::Visitor, Deserialize, Deserializer};
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Hash)]
|
||||||
|
#[serde(try_from = "String")]
|
||||||
pub struct Key {
|
pub struct Key {
|
||||||
pub code: KeyCode,
|
pub code: KeyCode,
|
||||||
pub shift: bool,
|
pub shift: bool,
|
||||||
|
|
@ -13,21 +15,36 @@ impl Default for Key {
|
||||||
fn default() -> Self { Self { code: KeyCode::Null, shift: false, ctrl: false, alt: false } }
|
fn default() -> Self { Self { code: KeyCode::Null, shift: false, ctrl: false, alt: false } }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for Key {
|
impl From<KeyEvent> for Key {
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: KeyEvent) -> Self {
|
||||||
let mut key = Default::default();
|
let shift = if let KeyCode::Char(c) = value.code { c.is_ascii_uppercase() } else { false };
|
||||||
if value.is_empty() {
|
|
||||||
return key;
|
Self {
|
||||||
|
code: value.code,
|
||||||
|
shift: shift || value.modifiers.contains(KeyModifiers::SHIFT),
|
||||||
|
ctrl: value.modifiers.contains(KeyModifiers::CONTROL),
|
||||||
|
alt: value.modifiers.contains(KeyModifiers::ALT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for Key {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
|
if s.is_empty() {
|
||||||
|
bail!("empty key")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !value.starts_with("<") || !value.ends_with(">") {
|
let mut key = Self::default();
|
||||||
let c = value.chars().next().unwrap();
|
if !s.starts_with("<") || !s.ends_with(">") {
|
||||||
|
let c = s.chars().next().unwrap();
|
||||||
key.code = KeyCode::Char(c);
|
key.code = KeyCode::Char(c);
|
||||||
key.shift = c.is_ascii_uppercase();
|
key.shift = c.is_ascii_uppercase();
|
||||||
return key;
|
return Ok(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut it = value[1..value.len() - 1].split_inclusive('-').peekable();
|
let mut it = s[1..s.len() - 1].split_inclusive('-').peekable();
|
||||||
while let Some(x) = it.next() {
|
while let Some(x) = it.next() {
|
||||||
match x {
|
match x {
|
||||||
"S-" => key.shift = true,
|
"S-" => key.shift = true,
|
||||||
|
|
@ -65,48 +82,13 @@ impl From<&str> for Key {
|
||||||
c if it.peek().is_none() => {
|
c if it.peek().is_none() => {
|
||||||
key.code = KeyCode::Char(c.chars().next().unwrap());
|
key.code = KeyCode::Char(c.chars().next().unwrap());
|
||||||
}
|
}
|
||||||
_ => {}
|
k => bail!("unknown key: {}", k),
|
||||||
}
|
|
||||||
}
|
|
||||||
key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<KeyEvent> for Key {
|
|
||||||
fn from(value: KeyEvent) -> Self {
|
|
||||||
let shift = if let KeyCode::Char(c) = value.code { c.is_ascii_uppercase() } else { false };
|
|
||||||
|
|
||||||
Self {
|
|
||||||
code: value.code,
|
|
||||||
shift: shift || value.modifiers.contains(KeyModifiers::SHIFT),
|
|
||||||
ctrl: value.modifiers.contains(KeyModifiers::CONTROL),
|
|
||||||
alt: value.modifiers.contains(KeyModifiers::ALT),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Key {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct KeyVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for KeyVisitor {
|
|
||||||
type Value = Key;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
formatter.write_str("a key string, e.g. <C-a>")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
Ok(Key::from(value))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deserializer.deserialize_str(KeyVisitor)
|
if key.code == KeyCode::Null {
|
||||||
|
bail!("empty key")
|
||||||
|
}
|
||||||
|
Ok(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,52 @@
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Deserializer};
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
use super::{Exec, Key};
|
use super::{Exec, Key};
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Single {
|
pub struct Single {
|
||||||
pub on: Vec<Key>,
|
pub on: Vec<Key>,
|
||||||
#[serde(deserialize_with = "Exec::deserialize")]
|
#[serde(deserialize_with = "Exec::deserialize")]
|
||||||
pub exec: Vec<Exec>,
|
pub exec: Vec<Exec>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Keymap {
|
pub struct Keymap {
|
||||||
pub manager: Vec<Single>,
|
pub manager: Vec<Single>,
|
||||||
pub tasks: Vec<Single>,
|
pub tasks: Vec<Single>,
|
||||||
pub input: Vec<Single>,
|
pub input: Vec<Single>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keymap {
|
impl<'de> Deserialize<'de> for Keymap {
|
||||||
pub fn new() -> Self {
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Shadow {
|
||||||
|
manager: Inner,
|
||||||
|
tasks: Inner,
|
||||||
|
input: Inner,
|
||||||
|
}
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Inner {
|
struct Inner {
|
||||||
keymap: Vec<Single>,
|
keymap: Vec<Single>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
let shadow = Shadow::deserialize(deserializer)?;
|
||||||
struct All {
|
Ok(Self {
|
||||||
manager: Inner,
|
manager: shadow.manager.keymap,
|
||||||
tasks: Inner,
|
tasks: shadow.tasks.keymap,
|
||||||
input: Inner,
|
input: shadow.input.keymap,
|
||||||
}
|
})
|
||||||
|
}
|
||||||
let path = BaseDirectories::new().unwrap().get_config_file("yazi/keymap.toml");
|
}
|
||||||
|
|
||||||
let all: All = toml::from_str(&fs::read_to_string(path).unwrap()).unwrap();
|
impl Keymap {
|
||||||
Self { manager: all.manager.keymap, tasks: all.tasks.keymap, input: all.input.keymap }
|
pub fn new() -> Self {
|
||||||
|
let path = BaseDirectories::new().unwrap().get_config_file("yazi/keymap.toml");
|
||||||
|
toml::from_str(&fs::read_to_string(path).unwrap()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use xdg::BaseDirectories;
|
||||||
|
|
||||||
use super::SortBy;
|
use super::SortBy;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Manager {
|
pub struct Manager {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub cwd: PathBuf,
|
pub cwd: PathBuf,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use serde::{de::Visitor, Deserialize, Deserializer};
|
use anyhow::bail;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Deserialize, Clone, Copy)]
|
||||||
|
#[serde(try_from = "String")]
|
||||||
pub enum SortBy {
|
pub enum SortBy {
|
||||||
Alphabetical,
|
Alphabetical,
|
||||||
Created,
|
Created,
|
||||||
|
|
@ -8,39 +10,16 @@ pub enum SortBy {
|
||||||
Size,
|
Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for SortBy {
|
impl TryFrom<String> for SortBy {
|
||||||
fn from(value: &str) -> Self {
|
type Error = anyhow::Error;
|
||||||
match value {
|
|
||||||
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
|
Ok(match s.as_str() {
|
||||||
"created" => Self::Created,
|
"created" => Self::Created,
|
||||||
"modified" => Self::Modified,
|
"modified" => Self::Modified,
|
||||||
"size" => Self::Size,
|
"size" => Self::Size,
|
||||||
_ => Self::Alphabetical,
|
"alphabetical" => Self::Alphabetical,
|
||||||
}
|
_ => bail!("invalid sort_by value: {}", s),
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for SortBy {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<SortBy, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct SortByVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for SortByVisitor {
|
|
||||||
type Value = SortBy;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
formatter.write_str("a sort_by string, e.g. modified")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
Ok(SortBy::from(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_str(SortByVisitor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
mod open;
|
mod open;
|
||||||
|
mod opener;
|
||||||
|
|
||||||
pub use open::*;
|
pub use open::*;
|
||||||
|
pub use opener::*;
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,18 @@
|
||||||
use std::{collections::BTreeMap, fs, path::Path};
|
use std::{collections::BTreeMap, fs, path::Path};
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Deserializer};
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
|
use super::Opener;
|
||||||
use crate::config::Pattern;
|
use crate::config::Pattern;
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug)]
|
||||||
pub struct Opener {
|
|
||||||
pub cmd: String,
|
|
||||||
pub args: Vec<String>,
|
|
||||||
#[serde(default)]
|
|
||||||
pub block: bool,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub spread: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct Open {
|
pub struct Open {
|
||||||
#[serde(skip)]
|
|
||||||
openers: BTreeMap<String, Vec<Opener>>,
|
openers: BTreeMap<String, Vec<Opener>>,
|
||||||
|
rules: Vec<OpenRule>,
|
||||||
rules: Vec<OpenRule>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct OpenRule {
|
struct OpenRule {
|
||||||
name: Option<Pattern>,
|
name: Option<Pattern>,
|
||||||
mime: Option<Pattern>,
|
mime: Option<Pattern>,
|
||||||
|
|
@ -32,23 +21,9 @@ struct OpenRule {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Open {
|
impl Open {
|
||||||
pub fn new() -> Open {
|
pub fn new() -> Self {
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Outer {
|
|
||||||
opener: BTreeMap<String, Vec<Opener>>,
|
|
||||||
open: Open,
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = BaseDirectories::new().unwrap().get_config_file("yazi/yazi.toml");
|
let path = BaseDirectories::new().unwrap().get_config_file("yazi/yazi.toml");
|
||||||
let mut outer = toml::from_str::<Outer>(&fs::read_to_string(path).unwrap()).unwrap();
|
toml::from_str(&fs::read_to_string(path).unwrap()).unwrap()
|
||||||
|
|
||||||
for opener in outer.opener.values_mut() {
|
|
||||||
for one in opener.iter_mut() {
|
|
||||||
one.spread = one.args.iter().any(|a| a == "$*");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Self { openers: outer.opener, rules: outer.open.rules }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn opener(&self, path: &Path, mime: &str) -> Option<&Opener> {
|
pub fn opener(&self, path: &Path, mime: &str) -> Option<&Opener> {
|
||||||
|
|
@ -63,3 +38,23 @@ impl Open {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Open {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Outer {
|
||||||
|
opener: BTreeMap<String, Vec<Opener>>,
|
||||||
|
open: OuterOpen,
|
||||||
|
}
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct OuterOpen {
|
||||||
|
rules: Vec<OpenRule>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let outer = Outer::deserialize(deserializer)?;
|
||||||
|
Ok(Self { openers: outer.opener, rules: outer.open.rules })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
31
src/config/open/opener.rs
Normal file
31
src/config/open/opener.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Opener {
|
||||||
|
pub cmd: String,
|
||||||
|
pub args: Vec<String>,
|
||||||
|
pub block: bool,
|
||||||
|
pub spread: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for Opener {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct Shadow {
|
||||||
|
pub cmd: String,
|
||||||
|
pub args: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub block: bool,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub spread: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
let shadow = Shadow::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
let spread = shadow.args.contains(&"$*".to_string());
|
||||||
|
Ok(Self { cmd: shadow.cmd, args: shadow.args, block: shadow.block, spread })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use serde::{de::Visitor, Deserialize, Deserializer};
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(try_from = "String")]
|
||||||
pub struct Pattern {
|
pub struct Pattern {
|
||||||
inner: glob::Pattern,
|
inner: glob::Pattern,
|
||||||
is_folder: bool,
|
is_folder: bool,
|
||||||
|
|
@ -16,35 +17,17 @@ impl Pattern {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for Pattern {
|
impl TryFrom<&str> for Pattern {
|
||||||
fn from(value: &str) -> Self {
|
type Error = anyhow::Error;
|
||||||
let is_folder = value.ends_with('/');
|
|
||||||
Self { inner: glob::Pattern::new(value.trim_end_matches('/')).unwrap_or_default(), is_folder }
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
|
let is_folder = s.ends_with('/');
|
||||||
|
Ok(Self { inner: glob::Pattern::new(s.trim_end_matches('/'))?, is_folder })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Pattern {
|
impl TryFrom<String> for Pattern {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
type Error = anyhow::Error;
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
struct PatternVisitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for PatternVisitor {
|
fn try_from(s: String) -> Result<Self, Self::Error> { Self::try_from(s.as_str()) }
|
||||||
type Value = Pattern;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
formatter.write_str("a glob pattern, e.g. *.rs")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
|
||||||
where
|
|
||||||
E: serde::de::Error,
|
|
||||||
{
|
|
||||||
Ok(Pattern::from(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer.deserialize_str(PatternVisitor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,7 @@ use std::fs;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Manager {
|
|
||||||
pub sort_by: String,
|
|
||||||
pub sort_reverse: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct Preview {
|
pub struct Preview {
|
||||||
pub tab_size: u32,
|
pub tab_size: u32,
|
||||||
|
|
||||||
|
|
@ -25,6 +19,7 @@ impl Preview {
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = BaseDirectories::new().unwrap().get_config_file("yazi/yazi.toml");
|
let path = BaseDirectories::new().unwrap().get_config_file("yazi/yazi.toml");
|
||||||
toml::from_str::<Outer>(&fs::read_to_string(path).unwrap()).unwrap().preview
|
let outer: Outer = toml::from_str(&fs::read_to_string(path).unwrap()).unwrap();
|
||||||
|
outer.preview
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,42 @@
|
||||||
use anyhow::Result;
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use ratatui::style;
|
use ratatui::style;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Color {
|
#[serde(try_from = "String")]
|
||||||
pub fg: String,
|
pub struct Color(style::Color);
|
||||||
pub bg: String,
|
|
||||||
|
impl Default for Color {
|
||||||
|
fn default() -> Self { Self(style::Color::Reset) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl TryFrom<String> for Color {
|
||||||
pub fn fg_rgb(&self) -> style::Color {
|
type Error = anyhow::Error;
|
||||||
if self.fg.len() < 7 {
|
|
||||||
return style::Color::Reset;
|
|
||||||
}
|
|
||||||
let convert = || -> Result<style::Color> {
|
|
||||||
Ok(style::Color::Rgb(
|
|
||||||
u8::from_str_radix(&self.fg[1..3], 16)?,
|
|
||||||
u8::from_str_radix(&self.fg[3..5], 16)?,
|
|
||||||
u8::from_str_radix(&self.fg[5..7], 16)?,
|
|
||||||
))
|
|
||||||
};
|
|
||||||
convert().unwrap_or(style::Color::Reset)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bg_rgb(&self) -> style::Color {
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||||
if self.bg.len() < 7 {
|
if s.len() < 7 {
|
||||||
return style::Color::Reset;
|
bail!("Invalid color: {}", s);
|
||||||
}
|
}
|
||||||
let convert = || -> Result<style::Color> {
|
Ok(Self(style::Color::Rgb(
|
||||||
Ok(style::Color::Rgb(
|
u8::from_str_radix(&s[1..3], 16)?,
|
||||||
u8::from_str_radix(&self.bg[1..3], 16)?,
|
u8::from_str_radix(&s[3..5], 16)?,
|
||||||
u8::from_str_radix(&self.bg[3..5], 16)?,
|
u8::from_str_radix(&s[5..7], 16)?,
|
||||||
u8::from_str_radix(&self.bg[5..7], 16)?,
|
)))
|
||||||
))
|
|
||||||
};
|
|
||||||
convert().unwrap_or(style::Color::Reset)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for Color {
|
||||||
|
type Target = style::Color;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ColorDual {
|
||||||
|
#[serde(default)]
|
||||||
|
pub fg: Color,
|
||||||
|
#[serde(default)]
|
||||||
|
pub bg: Color,
|
||||||
|
}
|
||||||
|
|
|
||||||
29
src/config/theme/filetype.rs
Normal file
29
src/config/theme/filetype.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
|
|
||||||
|
use crate::config::Pattern;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Filetype {
|
||||||
|
pub name: Option<Pattern>,
|
||||||
|
pub mime: Option<Pattern>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub bg: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub fg: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub bold: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filetype {
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Filetype>, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct FiletypeOuter {
|
||||||
|
rules: Vec<Filetype>,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(FiletypeOuter::deserialize(deserializer)?.rules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use serde::{de::Visitor, Deserializer};
|
use serde::{de::{self, Visitor}, Deserializer};
|
||||||
|
|
||||||
use crate::config::Pattern;
|
use crate::config::Pattern;
|
||||||
|
|
||||||
|
|
@ -11,10 +11,6 @@ pub struct Icon {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Icon {
|
impl Icon {
|
||||||
pub fn new(name: String, display: String) -> Self {
|
|
||||||
Self { name: Pattern::from(name.as_ref()), display }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Icon>, D::Error>
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<Icon>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
|
|
@ -30,11 +26,15 @@ impl Icon {
|
||||||
|
|
||||||
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||||
where
|
where
|
||||||
A: serde::de::MapAccess<'de>,
|
A: de::MapAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut icons = Vec::new();
|
let mut icons = Vec::new();
|
||||||
while let Some((key, value)) = &map.next_entry::<String, String>()? {
|
while let Some((key, value)) = &map.next_entry::<String, String>()? {
|
||||||
icons.push(Icon::new(key.clone(), value.clone()));
|
icons.push(Icon {
|
||||||
|
name: Pattern::try_from(key.clone())
|
||||||
|
.map_err(|e| de::Error::custom(e.to_string()))?,
|
||||||
|
display: value.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Ok(icons)
|
Ok(icons)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
mod color;
|
mod color;
|
||||||
|
mod filetype;
|
||||||
mod icon;
|
mod icon;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
||||||
pub use color::*;
|
pub use color::*;
|
||||||
|
pub use filetype::*;
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
pub use theme::*;
|
pub use theme::*;
|
||||||
|
|
|
||||||
|
|
@ -3,32 +3,29 @@ use std::{fs, path::PathBuf};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
use super::{Color, Icon};
|
use super::{ColorDual, Filetype, Icon};
|
||||||
use crate::misc::absolute_path;
|
use crate::misc::absolute_path;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Mode {
|
pub struct Mode {
|
||||||
pub normal: Color,
|
pub normal: ColorDual,
|
||||||
pub select: Color,
|
pub select: ColorDual,
|
||||||
pub unselect: Color,
|
pub unselect: ColorDual,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
pub active: Color,
|
pub active: ColorDual,
|
||||||
pub inactive: Color,
|
pub inactive: ColorDual,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Selection {
|
pub struct Selection {
|
||||||
pub normal: Color,
|
pub normal: ColorDual,
|
||||||
pub hovered: Color,
|
pub hovered: ColorDual,
|
||||||
pub selected: Color,
|
pub selected: ColorDual,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Filetype {}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Syntect {
|
pub struct Syntect {
|
||||||
pub theme: PathBuf,
|
pub theme: PathBuf,
|
||||||
|
|
@ -39,10 +36,11 @@ pub struct Theme {
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub tab: Tab,
|
pub tab: Tab,
|
||||||
pub selection: Selection,
|
pub selection: Selection,
|
||||||
pub filetype: Filetype,
|
#[serde(deserialize_with = "Filetype::deserialize")]
|
||||||
pub syntect: Syntect,
|
pub filetypes: Vec<Filetype>,
|
||||||
#[serde(deserialize_with = "Icon::deserialize")]
|
#[serde(deserialize_with = "Icon::deserialize")]
|
||||||
pub icons: Vec<Icon>,
|
pub icons: Vec<Icon>,
|
||||||
|
pub syntect: Syntect,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub enum Mode {
|
||||||
|
|
||||||
impl Mode {
|
impl Mode {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn color(&self) -> &theme::Color {
|
pub fn color(&self) -> &theme::ColorDual {
|
||||||
match *self {
|
match *self {
|
||||||
Mode::Normal => &THEME.mode.normal,
|
Mode::Normal => &THEME.mode.normal,
|
||||||
Mode::Select(_) => &THEME.mode.select,
|
Mode::Select(_) => &THEME.mode.select,
|
||||||
|
|
|
||||||
|
|
@ -29,19 +29,16 @@ impl<'a> Widget for Layout<'a> {
|
||||||
)
|
)
|
||||||
.split(area);
|
.split(area);
|
||||||
|
|
||||||
Paragraph::new("").style(Style::default().fg(mode.color().bg_rgb())).render(chunks[0], buf);
|
Paragraph::new("").style(Style::default().fg(*mode.color().bg)).render(chunks[0], buf);
|
||||||
|
|
||||||
Paragraph::new(format!(" {} ", mode))
|
Paragraph::new(format!(" {} ", mode))
|
||||||
.style(
|
.style(
|
||||||
Style::default()
|
Style::default().fg(*mode.color().fg).bg(*mode.color().bg).add_modifier(Modifier::BOLD),
|
||||||
.fg(mode.color().fg_rgb())
|
|
||||||
.bg(mode.color().bg_rgb())
|
|
||||||
.add_modifier(Modifier::BOLD),
|
|
||||||
)
|
)
|
||||||
.render(chunks[1], buf);
|
.render(chunks[1], buf);
|
||||||
|
|
||||||
Paragraph::new(" master ")
|
Paragraph::new(" master ")
|
||||||
.style(Style::default().fg(mode.color().bg_rgb()).bg(Color::Rgb(72, 77, 102)))
|
.style(Style::default().fg(*mode.color().bg).bg(Color::Rgb(72, 77, 102)))
|
||||||
.render(chunks[2], buf);
|
.render(chunks[2], buf);
|
||||||
|
|
||||||
Paragraph::new("").style(Style::default().fg(Color::Rgb(72, 77, 102))).render(chunks[3], buf);
|
Paragraph::new("").style(Style::default().fg(Color::Rgb(72, 77, 102))).render(chunks[3], buf);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue