feat: add self-provided themes (#2224)

* chore: move themes to default assets

* feat: add self-provided themes

* fix: embed themes into binary
This commit is contained in:
Jae-Heon Ji 2023-03-06 23:36:12 +09:00 committed by GitHub
parent a263c34925
commit 63bfe9c5e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 111 additions and 41 deletions

20
Cargo.lock generated
View File

@ -1270,6 +1270,25 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "include_dir"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e"
dependencies = [
"include_dir_macros",
]
[[package]]
name = "include_dir_macros"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "indexmap"
version = "1.8.2"
@ -4085,6 +4104,7 @@ dependencies = [
"colorsys",
"crossbeam",
"directories-next",
"include_dir",
"insta",
"interprocess",
"kdl",

View File

@ -1,19 +1,5 @@
# Themes
Themes can contain different flavors in one file, or can be created as individual files.
It contains examples showing how to write a theme.
Example:
```
gruvbox.kdl
├─ gruvbox-light
└─ gruvbox-dark
or
gruvbox-light.kdl
└─ gruvbox-light
gruvbox-dark.kdl
└─ gruvbox-dark
```
If you would like to add a theme to zellij, please refer [zellij-utils/assets/themes](../../zellij-utils/assets/themes).

View File

@ -0,0 +1,34 @@
// This file shows how to write a theme file
// using `gruvbox` theme.
themes {
// example of how to set a theme in RGB format
gruvbox-light {
fg 60 56 54
bg 251 82 75
black 40 40 40
red 205 75 69
green 152 151 26
yellow 215 153 33
blue 69 133 136
magenta 177 98 134
cyan 104 157 106
white 213 196 161
orange 214 93 14
}
// example of how to set a theme in HEX format
gruvbox-dark {
fg "#D5C4A1"
bg "#282828"
black "#3C3836"
red "#CC241D"
green "#98971A"
yellow "#D79921"
blue "#3C8588"
magenta "#B16286"
cyan "#689D6A"
white "#FBF1C7"
orange "#D65D0E"
}
}

View File

@ -38,6 +38,7 @@ miette = { version = "3.3.0", features = ["fancy"] }
regex = "1.5.5"
tempfile = "3.2.0"
kdl = { version = "4.5.0", features = ["span"] }
include_dir = "0.7.3"
#[cfg(not(target_family = "wasm"))]
[target.'cfg(not(target_family = "wasm"))'.dependencies]

View File

@ -1,6 +1,7 @@
//! Zellij program-wide constants.
use directories_next::ProjectDirs;
use include_dir::{include_dir, Dir};
use lazy_static::lazy_static;
use once_cell::sync::OnceCell;
use std::path::PathBuf;
@ -15,6 +16,7 @@ pub static DEBUG_MODE: OnceCell<bool> = OnceCell::new();
pub const SYSTEM_DEFAULT_CONFIG_DIR: &str = "/etc/zellij";
pub const SYSTEM_DEFAULT_DATA_DIR_PREFIX: &str = system_default_data_dir();
pub static ZELLIJ_THEMES_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/assets/themes");
const fn system_default_data_dir() -> &'static str {
if let Some(data_dir) = std::option_env!("PREFIX") {

View File

@ -1734,12 +1734,8 @@ impl Themes {
Ok(themes)
}
pub fn from_path(path_to_theme_file: PathBuf) -> Result<Self, ConfigError> {
// String is the theme name
let mut file = File::open(path_to_theme_file.clone())?;
let mut kdl_config = String::new();
file.read_to_string(&mut kdl_config)?;
let kdl_config: KdlDocument = kdl_config.parse()?;
pub fn from_string(raw_string: String) -> Result<Self, ConfigError> {
let kdl_config: KdlDocument = raw_string.parse()?;
let kdl_themes = kdl_config.get("themes").ok_or(ConfigError::new_kdl_error(
"No theme node found in file".into(),
kdl_config.span().offset(),
@ -1748,4 +1744,26 @@ impl Themes {
let all_themes_in_file = Themes::from_kdl(kdl_themes)?;
Ok(all_themes_in_file)
}
pub fn from_path(path_to_theme_file: PathBuf) -> Result<Self, ConfigError> {
// String is the theme name
let mut file = File::open(path_to_theme_file.clone())?;
let mut kdl_config = String::new();
file.read_to_string(&mut kdl_config)?;
Themes::from_string(kdl_config)
}
pub fn from_dir(path_to_theme_dir: PathBuf) -> Result<Self, ConfigError> {
let mut themes = Themes::default();
for entry in std::fs::read_dir(path_to_theme_dir)? {
let entry = entry?;
let path = entry.path();
if let Some(extension) = path.extension() {
if extension == "kdl" {
themes = themes.merge(Themes::from_path(path)?);
}
}
}
Ok(themes)
}
}

View File

@ -1,5 +1,6 @@
#[cfg(not(target_family = "wasm"))]
use crate::consts::ASSET_MAP;
use crate::consts::ZELLIJ_THEMES_DIR;
use crate::input::theme::Themes;
use crate::{
cli::{CliArgs, Command},
@ -51,6 +52,22 @@ fn default_config_dirs() -> Vec<Option<PathBuf>> {
]
}
#[cfg(not(test))]
fn get_default_themes() -> Result<Themes, ConfigError> {
let mut themes = Themes::default();
for entry in ZELLIJ_THEMES_DIR.files() {
if let Some(entry) = entry.contents_utf8() {
themes = themes.merge(Themes::from_string(entry.to_string())?);
}
}
Ok(themes)
}
#[cfg(test)]
fn get_default_themes() -> Result<Themes, ConfigError> {
Ok(Themes::default())
}
/// Looks for an existing dir, uses that, else returns a
/// dir matching the config spec.
pub fn get_default_data_dir() -> PathBuf {
@ -87,6 +104,7 @@ pub fn get_layout_dir(config_dir: Option<PathBuf>) -> Option<PathBuf> {
pub fn get_theme_dir(config_dir: Option<PathBuf>) -> Option<PathBuf> {
config_dir.map(|dir| dir.join("themes"))
}
pub fn dump_asset(asset: &[u8]) -> std::io::Result<()> {
std::io::stdout().write_all(asset)?;
Ok(())
@ -310,25 +328,13 @@ impl Setup {
None => config.options.clone(),
};
if let Some(theme_dir) = config_options
.theme_dir
.clone()
.or_else(|| get_theme_dir(cli_args.config_dir.clone().or_else(find_default_config_dir)))
{
if theme_dir.is_dir() {
for entry in (theme_dir.read_dir()?).flatten() {
if let Some(extension) = entry.path().extension() {
if extension == "kdl" {
match Themes::from_path(entry.path()) {
Ok(themes) => config.themes = config.themes.merge(themes),
Err(e) => {
log::error!("error loading theme file: {:?}", e);
},
}
}
}
}
}
config.themes = config.themes.merge(get_default_themes()?);
let user_theme_dir = config_options.theme_dir.clone().or_else(|| {
get_theme_dir(cli_args.config_dir.clone().or_else(find_default_config_dir))
});
if let Some(user_dir) = user_theme_dir {
config.themes = config.themes.merge(Themes::from_dir(user_dir)?);
}
if let Some(Command::Setup(ref setup)) = &cli_args.command {
@ -514,6 +520,7 @@ impl Setup {
Ok(())
}
fn generate_completion(shell: &str) {
let shell: Shell = match shell.to_lowercase().parse() {
Ok(shell) => shell,
@ -564,6 +571,7 @@ impl Setup {
_ => {},
}
}
fn parse_layout_and_override_config(
cli_config_options: Option<&Options>,
config: Config,
@ -593,6 +601,7 @@ impl Setup {
// that needs to take precedence
Layout::from_path_or_default(chosen_layout.as_ref(), layout_dir.clone(), config)
}
fn handle_setup_commands(cli_args: &CliArgs) {
if let Some(Command::Setup(ref setup)) = &cli_args.command {
setup.from_cli().map_or_else(