From 2c35f82758dc34403aac5d094cf684c6e7e96c6b Mon Sep 17 00:00:00 2001 From: Aloxaf Date: Tue, 15 Dec 2020 20:11:02 +0800 Subject: [PATCH] feat: add support of config file close #130 --- Cargo.lock | 8 ++++ Cargo.toml | 14 +++---- README.md | 13 ++++++ src/{ => bin/silicon}/config.rs | 38 +++++++++++++++-- src/{bin.rs => bin/silicon/main.rs} | 23 ++++++----- src/directories.rs | 63 +++++++++++++++++++++++++++++ src/lib.rs | 1 + src/utils.rs | 46 ++++++--------------- 8 files changed, 149 insertions(+), 57 deletions(-) rename src/{ => bin/silicon}/config.rs (87%) rename src/{bin.rs => bin/silicon/main.rs} (87%) create mode 100644 src/directories.rs diff --git a/Cargo.lock b/Cargo.lock index fe2c2ec..92ebcbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1285,6 +1285,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "shell-words" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074" + [[package]] name = "silicon" version = "0.4.0" @@ -1298,9 +1304,11 @@ dependencies = [ "font-kit", "image", "imageproc", + "lazy_static", "log", "pasteboard", "pathfinder_geometry", + "shell-words", "structopt 0.3.20", "syntect", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index 5705985..3ece93a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,14 +9,6 @@ repository = "https://github.com/Aloxaf/silicon" license = "MIT" edition = "2018" -[lib] -name = "silicon" -path = "src/lib.rs" - -[[bin]] -name = "silicon" -path = "src/bin.rs" - [dependencies] dirs = "3.0" imageproc = "0.22.0" @@ -26,6 +18,8 @@ tempfile = "3.1.0" conv = "0.3.3" pathfinder_geometry = "0.5.1" log = "0.4.11" +lazy_static = "1.4.0" +shell-words = { version = "1.0.0", optional = true } [target.'cfg(target_os = "macos")'.dependencies] pasteboard = "0.1.1" @@ -61,5 +55,7 @@ features = ["termcolor", "atty", "humantime"] optional = true [features] +# fearures required for silicon as a application +# disable it when using as a library default = ["bin"] -bin = ["structopt", "env_logger", "anyhow"] +bin = ["structopt", "env_logger", "anyhow", "shell-words"] diff --git a/README.md b/README.md index 97c0fc8..5f2cfe2 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,19 @@ Silicon reads syntax-definition and theme cache from bat's cache directory. You can find the steps to add new syntaxes / themes for bat here: [sharkdp/bat#adding-new-syntaxes--language-definitions](https://github.com/sharkdp/bat#adding-new-syntaxes--language-definitions). +## Configuration file + +You can write some common args to `silicon --config-file`. + +Example: +```shell +# enable shadow +--shadow-color '#555' +--background '#fff' +--shadow-blur-radius 30 +--no-window-controls +``` + # Related projects - [vim-silicon](https://github.com/segeljakt/vim-silicon) diff --git a/src/config.rs b/src/bin/silicon/config.rs similarity index 87% rename from src/config.rs rename to src/bin/silicon/config.rs index 9a9bee5..1ea40e6 100644 --- a/src/config.rs +++ b/src/bin/silicon/config.rs @@ -1,16 +1,43 @@ -use crate::formatter::{ImageFormatter, ImageFormatterBuilder}; -use crate::utils::{Background, ShadowAdder, ToRgba}; use anyhow::{Context, Error}; use clipboard::{ClipboardContext, ClipboardProvider}; use image::Rgba; +use silicon::directories::PROJECT_DIRS; +use silicon::formatter::{ImageFormatter, ImageFormatterBuilder}; +use silicon::utils::{Background, ShadowAdder, ToRgba}; +use std::ffi::OsString; use std::fs::File; use std::io::{stdin, Read}; use std::num::ParseIntError; use std::path::PathBuf; +use structopt::clap::AppSettings::ColoredHelp; use structopt::StructOpt; use syntect::highlighting::{Theme, ThemeSet}; use syntect::parsing::{SyntaxReference, SyntaxSet}; +pub fn config_file() -> PathBuf { + std::env::var("SILICON_CONFIG_PATH") + .ok() + .map(PathBuf::from) + .filter(|config_path| config_path.is_file()) + .unwrap_or_else(|| PROJECT_DIRS.config_dir().join("config")) +} + +pub fn get_args_from_config_file() -> Vec { + let args = std::fs::read_to_string(config_file()) + .ok() + .and_then(|content| { + content + .split('\n') + .map(|line| line.trim()) + .filter(|line| !line.starts_with('#') && !line.is_empty()) + .map(|line| shell_words::split(line)) + .collect::, _>>() + .ok() + }) + .unwrap_or_default(); + args.iter().flatten().map(OsString::from).collect() +} + fn parse_str_color(s: &str) -> Result, Error> { Ok(s.to_rgba() .map_err(|_| format_err!("Invalid color: `{}`", s))?) @@ -54,6 +81,7 @@ type Lines = Vec; #[derive(StructOpt, Debug)] #[structopt(name = "silicon")] +#[structopt(global_setting(ColoredHelp))] pub struct Config { /// Background image #[structopt(long, value_name = "IMAGE", conflicts_with = "background")] @@ -69,6 +97,10 @@ pub struct Config { )] pub background: Rgba, + /// Show the path of silicon config file + #[structopt(long)] + pub config_file: bool, + /// Read input from clipboard. #[structopt(long)] pub from_clipboard: bool, @@ -110,7 +142,7 @@ pub struct Config { short, long, value_name = "PATH", - required_unless_one = &["list-fonts", "list-themes", "to-clipboard"] + required_unless_one = &["config-file", "list-fonts", "list-themes", "to-clipboard"] )] pub output: Option, diff --git a/src/bin.rs b/src/bin/silicon/main.rs similarity index 87% rename from src/bin.rs rename to src/bin/silicon/main.rs index b09e657..b77ec37 100644 --- a/src/bin.rs +++ b/src/bin/silicon/main.rs @@ -1,10 +1,6 @@ #[macro_use] -extern crate log; -#[macro_use] extern crate anyhow; -use crate::config::Config; -use crate::utils::*; use anyhow::Error; use image::DynamicImage; use structopt::StructOpt; @@ -20,12 +16,10 @@ use {image::ImageOutputFormat, pasteboard::Pasteboard}; #[cfg(target_os = "linux")] use {image::ImageOutputFormat, std::process::Command}; -pub mod blur; -pub mod config; -pub mod error; -pub mod font; -pub mod formatter; -pub mod utils; +mod config; +use config::Config; +use silicon::utils::init_syntect; +use crate::config::{get_args_from_config_file, config_file}; #[cfg(target_os = "linux")] pub fn dump_image_to_clipboard(image: &DynamicImage) -> Result<(), Error> { @@ -81,7 +75,11 @@ pub fn dump_image_to_clipboard(_image: &DynamicImage) -> Result<(), Error> { } fn run() -> Result<(), Error> { - let config: Config = Config::from_args(); + let mut args = get_args_from_config_file(); + let mut args_cli = std::env::args_os(); + args.insert(0, args_cli.next().unwrap()); + args.extend(args_cli); + let config: Config = Config::from_iter(args); let (ps, ts) = init_syntect(); @@ -96,6 +94,9 @@ fn run() -> Result<(), Error> { println!("{}", font); } return Ok(()); + } else if config.config_file { + println!("{}", config_file().to_string_lossy()); + return Ok(()); } let (syntax, code) = config.get_source_code(&ps)?; diff --git a/src/directories.rs b/src/directories.rs new file mode 100644 index 0000000..a098c63 --- /dev/null +++ b/src/directories.rs @@ -0,0 +1,63 @@ +use lazy_static::lazy_static; +use std::env; +use std::path::{Path, PathBuf}; + +pub struct SiliconProjectDirs { + cache_dir: PathBuf, + config_dir: PathBuf, +} + +impl SiliconProjectDirs { + fn new() -> Option { + let cache_dir = Self::get_cache_dir()?; + + #[cfg(target_os = "macos")] + let config_dir_op = env::var_os("XDG_CONFIG_HOME") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .or_else(|| dirs::home_dir().map(|d| d.join(".config"))); + + #[cfg(not(target_os = "macos"))] + let config_dir_op = dirs::config_dir(); + + let config_dir = config_dir_op.map(|d| d.join("silicon"))?; + + Some(Self { + cache_dir, + config_dir, + }) + } + + // silicon use bat's cache directory + fn get_cache_dir() -> Option { + // on all OS prefer BAT_CACHE_PATH if set + let cache_dir_op = env::var_os("BAT_CACHE_PATH").map(PathBuf::from); + if cache_dir_op.is_some() { + return cache_dir_op; + } + + #[cfg(target_os = "macos")] + let cache_dir_op = env::var_os("XDG_CACHE_HOME") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .or_else(|| dirs::home_dir().map(|d| d.join(".cache"))); + + #[cfg(not(target_os = "macos"))] + let cache_dir_op = dirs::cache_dir(); + + cache_dir_op.map(|d| d.join("bat")) + } + + pub fn cache_dir(&self) -> &Path { + &self.cache_dir + } + + pub fn config_dir(&self) -> &Path { + &self.config_dir + } +} + +lazy_static! { + pub static ref PROJECT_DIRS: SiliconProjectDirs = + SiliconProjectDirs::new().expect("Could not get home directory"); +} diff --git a/src/lib.rs b/src/lib.rs index 5cbbd87..7289d85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,7 @@ extern crate log; pub mod blur; +pub mod directories; pub mod error; pub mod font; pub mod formatter; diff --git a/src/utils.rs b/src/utils.rs index e2c415d..d013e46 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,49 +1,27 @@ +use crate::directories::PROJECT_DIRS; use crate::error::ParseColorError; use image::imageops::{crop, resize, FilterType}; use image::Pixel; use image::{DynamicImage, GenericImage, GenericImageView, Rgba, RgbaImage}; use imageproc::drawing::{draw_filled_rect_mut, draw_line_segment_mut}; use imageproc::rect::Rect; -use std::env; -use std::path::PathBuf; use syntect::dumps; use syntect::highlighting::ThemeSet; use syntect::parsing::SyntaxSet; -// Copied from https://github.com/sharkdp/bat/blob/12a1fe3ad417f7694c4490b94b793387c7a7b536/src/bin/bat/directories.rs#L35 -fn get_cache_dir() -> Option { - // on all OS prefer BAT_CACHE_PATH if set - let cache_dir_op = env::var_os("BAT_CACHE_PATH").map(PathBuf::from); - if cache_dir_op.is_some() { - return cache_dir_op; - } - - #[cfg(target_os = "macos")] - let cache_dir_op = env::var_os("XDG_CACHE_HOME") - .map(PathBuf::from) - .filter(|p| p.is_absolute()) - .or_else(|| dirs::home_dir().map(|d| d.join(".cache"))); - - #[cfg(not(target_os = "macos"))] - let cache_dir_op = dirs::cache_dir(); - - cache_dir_op.map(|d| d.join("bat")) -} - pub fn read_from_bat_cache() -> Option<(SyntaxSet, ThemeSet)> { - get_cache_dir().and_then(|cache_dir| { - let syntax_cache = cache_dir.join("syntaxes.bin"); - let theme_cache = cache_dir.join("themes.bin"); - if syntax_cache.exists() && theme_cache.exists() { - if let (Ok(a), Ok(b)) = ( - dumps::from_dump_file(syntax_cache), - dumps::from_dump_file(theme_cache), - ) { - return Some((a, b)); - } + let cache_dir = PROJECT_DIRS.cache_dir(); + let syntax_cache = cache_dir.join("syntaxes.bin"); + let theme_cache = cache_dir.join("themes.bin"); + if syntax_cache.exists() && theme_cache.exists() { + if let (Ok(a), Ok(b)) = ( + dumps::from_dump_file(syntax_cache), + dumps::from_dump_file(theme_cache), + ) { + return Some((a, b)); } - None - }) + } + None } /// Load the default SyntaxSet and ThemeSet.