feat: add support of config file

close #130
This commit is contained in:
Aloxaf 2020-12-15 20:11:02 +08:00
parent 1e3089447c
commit 2c35f82758
No known key found for this signature in database
GPG Key ID: A07920B86500DE6C
8 changed files with 149 additions and 57 deletions

8
Cargo.lock generated
View File

@ -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",

View File

@ -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"]

View File

@ -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)

View File

@ -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<OsString> {
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::<Result<Vec<_>, _>>()
.ok()
})
.unwrap_or_default();
args.iter().flatten().map(OsString::from).collect()
}
fn parse_str_color(s: &str) -> Result<Rgba<u8>, Error> {
Ok(s.to_rgba()
.map_err(|_| format_err!("Invalid color: `{}`", s))?)
@ -54,6 +81,7 @@ type Lines = Vec<u32>;
#[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<u8>,
/// 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<PathBuf>,

View File

@ -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)?;

63
src/directories.rs Normal file
View File

@ -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<Self> {
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<PathBuf> {
// 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");
}

View File

@ -35,6 +35,7 @@
extern crate log;
pub mod blur;
pub mod directories;
pub mod error;
pub mod font;
pub mod formatter;

View File

@ -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<PathBuf> {
// 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.