aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/cli.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty/src/cli.rs')
-rw-r--r--alacritty/src/cli.rs150
1 files changed, 123 insertions, 27 deletions
diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs
index 5da6332b..418e3791 100644
--- a/alacritty/src/cli.rs
+++ b/alacritty/src/cli.rs
@@ -3,10 +3,12 @@ use std::path::PathBuf;
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
use log::{self, error, LevelFilter};
+use serde_yaml::Value;
use alacritty_terminal::config::Program;
use alacritty_terminal::index::{Column, Line};
+use crate::config::serde_utils;
use crate::config::ui_config::Delta;
use crate::config::window::{Dimensions, DEFAULT_NAME};
use crate::config::Config;
@@ -32,9 +34,10 @@ pub struct Options {
pub log_level: LevelFilter,
pub command: Option<Program>,
pub hold: bool,
- pub working_dir: Option<PathBuf>,
- pub config: Option<PathBuf>,
+ pub working_directory: Option<PathBuf>,
+ pub config_path: Option<PathBuf>,
pub persistent_logging: bool,
+ pub config_options: Value,
}
impl Default for Options {
@@ -52,9 +55,10 @@ impl Default for Options {
log_level: LevelFilter::Warn,
command: None,
hold: false,
- working_dir: None,
- config: None,
+ working_directory: None,
+ config_path: None,
persistent_logging: false,
+ config_options: Value::Null,
}
}
}
@@ -168,11 +172,18 @@ impl Options {
.short("e")
.multiple(true)
.takes_value(true)
- .min_values(1)
.allow_hyphen_values(true)
.help("Command and args to execute (must be last argument)"),
)
.arg(Arg::with_name("hold").long("hold").help("Remain open after child process exits"))
+ .arg(
+ Arg::with_name("option")
+ .long("option")
+ .short("o")
+ .multiple(true)
+ .takes_value(true)
+ .help("Override configuration file options [example: window.title=Alacritty]"),
+ )
.get_matches();
if matches.is_present("ref-test") {
@@ -231,11 +242,11 @@ impl Options {
}
if let Some(dir) = matches.value_of("working-directory") {
- options.working_dir = Some(PathBuf::from(dir.to_string()));
+ options.working_directory = Some(PathBuf::from(dir.to_string()));
}
if let Some(path) = matches.value_of("config-file") {
- options.config = Some(PathBuf::from(path.to_string()));
+ options.config_path = Some(PathBuf::from(path.to_string()));
}
if let Some(mut args) = matches.values_of("command") {
@@ -251,23 +262,47 @@ impl Options {
options.hold = true;
}
+ if let Some(config_options) = matches.values_of("option") {
+ for option in config_options {
+ match option_as_value(option) {
+ Ok(value) => {
+ options.config_options = serde_utils::merge(options.config_options, value);
+ },
+ Err(_) => eprintln!("Invalid CLI config option: {:?}", option),
+ }
+ }
+ }
+
options
}
+ /// Configuration file path.
pub fn config_path(&self) -> Option<PathBuf> {
- self.config.clone()
+ self.config_path.clone()
}
- pub fn into_config(self, mut config: Config) -> Config {
- match self.working_dir.or_else(|| config.working_directory.take()) {
- Some(ref wd) if !wd.is_dir() => error!("Unable to set working directory to {:?}", wd),
- wd => config.working_directory = wd,
+ /// CLI config options as deserializable serde value.
+ pub fn config_options(&self) -> &Value {
+ &self.config_options
+ }
+
+ /// Override configuration file with options from the CLI.
+ pub fn override_config(&self, config: &mut Config) {
+ if let Some(working_directory) = &self.working_directory {
+ if working_directory.is_dir() {
+ config.working_directory = Some(working_directory.to_owned());
+ } else {
+ error!("Invalid working directory: {:?}", working_directory);
+ }
}
if let Some(lcr) = self.live_config_reload {
config.ui_config.set_live_config_reload(lcr);
}
- config.shell = self.command.or(config.shell);
+
+ if let Some(command) = &self.command {
+ config.shell = Some(command.clone());
+ }
config.hold = self.hold;
@@ -275,12 +310,12 @@ impl Options {
config.ui_config.set_dynamic_title(dynamic_title);
replace_if_some(&mut config.ui_config.window.dimensions, self.dimensions);
- replace_if_some(&mut config.ui_config.window.title, self.title);
- config.ui_config.window.position = self.position.or(config.ui_config.window.position);
- config.ui_config.window.embed = self.embed.and_then(|embed| embed.parse().ok());
- replace_if_some(&mut config.ui_config.window.class.instance, self.class_instance);
- replace_if_some(&mut config.ui_config.window.class.general, self.class_general);
+ replace_if_some(&mut config.ui_config.window.title, self.title.clone());
+ replace_if_some(&mut config.ui_config.window.class.instance, self.class_instance.clone());
+ replace_if_some(&mut config.ui_config.window.class.general, self.class_general.clone());
+ config.ui_config.window.position = self.position.or(config.ui_config.window.position);
+ config.ui_config.window.embed = self.embed.as_ref().and_then(|embed| embed.parse().ok());
config.ui_config.debug.print_events |= self.print_events;
config.ui_config.debug.log_level = max(config.ui_config.debug.log_level, self.log_level);
config.ui_config.debug.ref_test |= self.ref_test;
@@ -290,8 +325,6 @@ impl Options {
config.ui_config.debug.log_level =
max(config.ui_config.debug.log_level, LevelFilter::Info);
}
-
- config
}
}
@@ -301,28 +334,54 @@ fn replace_if_some<T>(option: &mut T, value: Option<T>) {
}
}
+/// Format an option in the format of `parent.field=value` to a serde Value.
+fn option_as_value(option: &str) -> Result<Value, serde_yaml::Error> {
+ let mut yaml_text = String::with_capacity(option.len());
+ let mut closing_brackets = String::new();
+
+ for (i, c) in option.chars().enumerate() {
+ match c {
+ '=' => {
+ yaml_text.push_str(": ");
+ yaml_text.push_str(&option[i + 1..]);
+ break;
+ },
+ '.' => {
+ yaml_text.push_str(": {");
+ closing_brackets.push('}');
+ },
+ _ => yaml_text.push(c),
+ }
+ }
+
+ yaml_text += &closing_brackets;
+
+ serde_yaml::from_str(&yaml_text)
+}
+
#[cfg(test)]
mod tests {
- use crate::cli::Options;
- use crate::config::Config;
+ use super::*;
+
+ use serde_yaml::mapping::Mapping;
#[test]
fn dynamic_title_ignoring_options_by_default() {
- let config = Config::default();
+ let mut config = Config::default();
let old_dynamic_title = config.ui_config.dynamic_title();
- let config = Options::default().into_config(config);
+ Options::default().override_config(&mut config);
assert_eq!(old_dynamic_title, config.ui_config.dynamic_title());
}
#[test]
fn dynamic_title_overridden_by_options() {
- let config = Config::default();
+ let mut config = Config::default();
let mut options = Options::default();
options.title = Some("foo".to_owned());
- let config = options.into_config(config);
+ options.override_config(&mut config);
assert!(!config.ui_config.dynamic_title());
}
@@ -332,8 +391,45 @@ mod tests {
let mut config = Config::default();
config.ui_config.window.title = "foo".to_owned();
- let config = Options::default().into_config(config);
+ Options::default().override_config(&mut config);
assert!(config.ui_config.dynamic_title());
}
+
+ #[test]
+ fn valid_option_as_value() {
+ // Test with a single field.
+ let value = option_as_value("field=true").unwrap();
+
+ let mut mapping = Mapping::new();
+ mapping.insert(Value::String(String::from("field")), Value::Bool(true));
+
+ assert_eq!(value, Value::Mapping(mapping));
+
+ // Test with nested fields
+ let value = option_as_value("parent.field=true").unwrap();
+
+ let mut parent_mapping = Mapping::new();
+ parent_mapping.insert(Value::String(String::from("field")), Value::Bool(true));
+ let mut mapping = Mapping::new();
+ mapping.insert(Value::String(String::from("parent")), Value::Mapping(parent_mapping));
+
+ assert_eq!(value, Value::Mapping(mapping));
+ }
+
+ #[test]
+ fn invalid_option_as_value() {
+ let value = option_as_value("}");
+ assert!(value.is_err());
+ }
+
+ #[test]
+ fn float_option_as_value() {
+ let value = option_as_value("float=3.4").unwrap();
+
+ let mut expected = Mapping::new();
+ expected.insert(Value::String(String::from("float")), Value::Number(3.4.into()));
+
+ assert_eq!(value, Value::Mapping(expected));
+ }
}