mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-25 14:05:41 -05:00
Unify CLI config override mechanisms
This patch changes the way the `-o` config option works when specified at startup to function the same way as the IPC mechanism. While this should technically perform the exact same way, it should hopefully make it a little easier to understand how CLI config replacement works.
This commit is contained in:
parent
4a26667060
commit
683b5a2cb4
6 changed files with 77 additions and 76 deletions
|
@ -4,13 +4,14 @@ use std::path::PathBuf;
|
|||
use clap::{ArgAction, Args, Parser, Subcommand, ValueHint};
|
||||
use log::{self, error, LevelFilter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use toml::{Table, Value};
|
||||
use toml::Value;
|
||||
|
||||
use crate::logging::LOG_TARGET_CONFIG;
|
||||
use alacritty_terminal::tty::Options as PtyOptions;
|
||||
|
||||
use crate::config::ui_config::Program;
|
||||
use crate::config::window::{Class, Identity};
|
||||
use crate::config::{serde_utils, UiConfig};
|
||||
use crate::config::UiConfig;
|
||||
|
||||
/// CLI options for the main Alacritty executable.
|
||||
#[derive(Parser, Default, Debug)]
|
||||
|
@ -57,13 +58,9 @@ pub struct Options {
|
|||
#[clap(short, conflicts_with("quiet"), action = ArgAction::Count)]
|
||||
verbose: u8,
|
||||
|
||||
/// Override configuration file options [example: cursor.style=Beam].
|
||||
#[clap(short = 'o', long, num_args = 1..)]
|
||||
option: Vec<String>,
|
||||
|
||||
/// CLI options for config overrides.
|
||||
#[clap(skip)]
|
||||
pub config_options: TomlValue,
|
||||
pub config_options: Vec<(String, Value)>,
|
||||
|
||||
/// Options which can be passed via IPC.
|
||||
#[clap(flatten)]
|
||||
|
@ -78,8 +75,16 @@ impl Options {
|
|||
pub fn new() -> Self {
|
||||
let mut options = Self::parse();
|
||||
|
||||
// Convert `--option` flags into serde `Value`.
|
||||
options.config_options = TomlValue(options_as_value(&options.option));
|
||||
for option in options.window_options.option.drain(..) {
|
||||
let parsed = match toml::from_str(&option) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(err) => {
|
||||
eprintln!("Ignoring invalid CLI option '{option}': {err}");
|
||||
continue;
|
||||
},
|
||||
};
|
||||
options.config_options.push((option, parsed));
|
||||
}
|
||||
|
||||
options
|
||||
}
|
||||
|
@ -100,6 +105,14 @@ impl Options {
|
|||
if config.debug.print_events {
|
||||
config.debug.log_level = max(config.debug.log_level, LevelFilter::Info);
|
||||
}
|
||||
|
||||
// Replace CLI options.
|
||||
use alacritty_config::SerdeReplace;
|
||||
for (option, parsed) in &self.config_options {
|
||||
if let Err(err) = config.replace(parsed.clone()) {
|
||||
error!(target: LOG_TARGET_CONFIG, "Unable to set CLI option '{}': {}", option, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Logging filter level.
|
||||
|
@ -123,17 +136,6 @@ impl Options {
|
|||
}
|
||||
}
|
||||
|
||||
/// Combine multiple options into a [`toml::Value`].
|
||||
pub fn options_as_value(options: &[String]) -> Value {
|
||||
options.iter().fold(Value::Table(Table::new()), |value, option| match toml::from_str(option) {
|
||||
Ok(new_value) => serde_utils::merge(value, new_value),
|
||||
Err(_) => {
|
||||
eprintln!("Ignoring invalid option: {:?}", option);
|
||||
value
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse the class CLI parameter.
|
||||
fn parse_class(input: &str) -> Result<Class, String> {
|
||||
let (general, instance) = match input.split_once(',') {
|
||||
|
@ -302,6 +304,10 @@ pub struct WindowOptions {
|
|||
#[cfg(target_os = "macos")]
|
||||
/// The window tabbing identifier to use when building a window.
|
||||
pub window_tabbing_id: Option<String>,
|
||||
|
||||
/// Override configuration file options [example: cursor.style=Beam].
|
||||
#[clap(short = 'o', long, num_args = 1..)]
|
||||
option: Vec<String>,
|
||||
}
|
||||
|
||||
/// Parameters to the `config` IPC subcommand.
|
||||
|
@ -323,16 +329,6 @@ pub struct IpcConfig {
|
|||
pub reset: bool,
|
||||
}
|
||||
|
||||
/// Toml value with default implementation.
|
||||
#[derive(Debug)]
|
||||
pub struct TomlValue(pub Value);
|
||||
|
||||
impl Default for TomlValue {
|
||||
fn default() -> Self {
|
||||
Self(Value::Table(Table::new()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -127,7 +127,6 @@ impl From<YamlError> for Error {
|
|||
|
||||
/// Load the configuration file.
|
||||
pub fn load(options: &Options) -> UiConfig {
|
||||
let config_options = options.config_options.0.clone();
|
||||
let config_path = options
|
||||
.config_file
|
||||
.clone()
|
||||
|
@ -140,9 +139,9 @@ pub fn load(options: &Options) -> UiConfig {
|
|||
// - Default
|
||||
let mut config = config_path
|
||||
.as_ref()
|
||||
.and_then(|config_path| load_from(config_path, config_options.clone()).ok())
|
||||
.and_then(|config_path| load_from(config_path).ok())
|
||||
.unwrap_or_else(|| {
|
||||
let mut config = UiConfig::deserialize(config_options).unwrap_or_default();
|
||||
let mut config = UiConfig::default();
|
||||
match config_path {
|
||||
Some(config_path) => config.config_paths.push(config_path),
|
||||
None => info!(target: LOG_TARGET_CONFIG, "No config file found; using default"),
|
||||
|
@ -160,8 +159,7 @@ pub fn reload(config_path: &Path, options: &Options) -> Result<UiConfig> {
|
|||
debug!("Reloading configuration file: {:?}", config_path);
|
||||
|
||||
// Load config, propagating errors.
|
||||
let config_options = options.config_options.0.clone();
|
||||
let mut config = load_from(config_path, config_options)?;
|
||||
let mut config = load_from(config_path)?;
|
||||
|
||||
after_loading(&mut config, options);
|
||||
|
||||
|
@ -178,8 +176,8 @@ fn after_loading(config: &mut UiConfig, options: &Options) {
|
|||
}
|
||||
|
||||
/// Load configuration file and log errors.
|
||||
fn load_from(path: &Path, cli_config: Value) -> Result<UiConfig> {
|
||||
match read_config(path, cli_config) {
|
||||
fn load_from(path: &Path) -> Result<UiConfig> {
|
||||
match read_config(path) {
|
||||
Ok(config) => Ok(config),
|
||||
Err(err) => {
|
||||
error!(target: LOG_TARGET_CONFIG, "Unable to load config {:?}: {}", path, err);
|
||||
|
@ -189,12 +187,9 @@ fn load_from(path: &Path, cli_config: Value) -> Result<UiConfig> {
|
|||
}
|
||||
|
||||
/// Deserialize configuration file from path.
|
||||
fn read_config(path: &Path, cli_config: Value) -> Result<UiConfig> {
|
||||
fn read_config(path: &Path) -> Result<UiConfig> {
|
||||
let mut config_paths = Vec::new();
|
||||
let mut config_value = parse_config(path, &mut config_paths, IMPORT_RECURSION_LIMIT)?;
|
||||
|
||||
// Override config with CLI options.
|
||||
config_value = serde_utils::merge(config_value, cli_config);
|
||||
let config_value = parse_config(path, &mut config_paths, IMPORT_RECURSION_LIMIT)?;
|
||||
|
||||
// Deserialize to concrete type.
|
||||
let mut config = UiConfig::deserialize(config_value)?;
|
||||
|
|
|
@ -479,7 +479,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
|
|||
#[inline]
|
||||
fn start_search(&mut self, direction: Direction) {
|
||||
// Only create new history entry if the previous regex wasn't empty.
|
||||
if self.search_state.history.get(0).map_or(true, |regex| !regex.is_empty()) {
|
||||
if self.search_state.history.front().map_or(true, |regex| !regex.is_empty()) {
|
||||
self.search_state.history.push_front(String::new());
|
||||
self.search_state.history.truncate(MAX_SEARCH_HISTORY_SIZE);
|
||||
}
|
||||
|
@ -1539,8 +1539,7 @@ impl Processor {
|
|||
#[cfg(unix)]
|
||||
{
|
||||
let options = self.global_ipc_options.clone();
|
||||
let ipc_config = IpcConfig { options, window_id: None, reset: false };
|
||||
window_context.update_ipc_config(self.config.clone(), ipc_config);
|
||||
window_context.add_window_config(self.config.clone(), &options);
|
||||
}
|
||||
|
||||
self.windows.insert(window_context.id(), window_context);
|
||||
|
@ -1741,7 +1740,12 @@ impl Processor {
|
|||
.iter_mut()
|
||||
.filter(|(id, _)| window_id.is_none() || window_id == Some(**id))
|
||||
{
|
||||
window_context.update_ipc_config(self.config.clone(), ipc_config.clone());
|
||||
if ipc_config.reset {
|
||||
window_context.reset_window_config(self.config.clone());
|
||||
} else {
|
||||
window_context
|
||||
.add_window_config(self.config.clone(), &ipc_config.options);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Create a new terminal window.
|
||||
|
|
|
@ -20,8 +20,8 @@ use crate::cli::Options;
|
|||
use crate::event::{Event, EventType};
|
||||
use crate::message_bar::{Message, MessageType};
|
||||
|
||||
/// Logging target for IPC config error messages.
|
||||
pub const LOG_TARGET_IPC_CONFIG: &str = "alacritty_log_ipc_config";
|
||||
/// Logging target for per-window config error messages.
|
||||
pub const LOG_TARGET_WINDOW_CONFIG: &str = "alacritty_log_window_config";
|
||||
|
||||
/// Name for the environment variable containing the log file's path.
|
||||
const ALACRITTY_LOG_ENV: &str = "ALACRITTY_LOG";
|
||||
|
@ -42,7 +42,7 @@ static EXTRA_LOG_TARGETS: Lazy<Vec<String>> = Lazy::new(|| {
|
|||
|
||||
/// List of targets which will be logged by Alacritty.
|
||||
const ALLOWED_TARGETS: &[&str] = &[
|
||||
LOG_TARGET_IPC_CONFIG,
|
||||
LOG_TARGET_WINDOW_CONFIG,
|
||||
LOG_TARGET_CONFIG,
|
||||
"alacritty_config_derive",
|
||||
"alacritty_terminal",
|
||||
|
|
|
@ -69,7 +69,7 @@ impl Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
self.timers.get(0).map(|timer| timer.deadline)
|
||||
self.timers.front().map(|timer| timer.deadline)
|
||||
}
|
||||
|
||||
/// Schedule a new event.
|
||||
|
|
|
@ -31,8 +31,6 @@ use alacritty_terminal::term::test::TermSize;
|
|||
use alacritty_terminal::term::{Term, TermMode};
|
||||
use alacritty_terminal::tty;
|
||||
|
||||
#[cfg(unix)]
|
||||
use crate::cli::IpcConfig;
|
||||
use crate::cli::WindowOptions;
|
||||
use crate::clipboard::Clipboard;
|
||||
use crate::config::UiConfig;
|
||||
|
@ -41,7 +39,7 @@ use crate::display::Display;
|
|||
use crate::event::{
|
||||
ActionContext, Event, EventProxy, InlineSearchState, Mouse, SearchState, TouchPurpose,
|
||||
};
|
||||
use crate::logging::LOG_TARGET_IPC_CONFIG;
|
||||
use crate::logging::LOG_TARGET_WINDOW_CONFIG;
|
||||
use crate::message_bar::MessageBuffer;
|
||||
use crate::scheduler::Scheduler;
|
||||
use crate::{input, renderer};
|
||||
|
@ -67,7 +65,7 @@ pub struct WindowContext {
|
|||
master_fd: RawFd,
|
||||
#[cfg(not(windows))]
|
||||
shell_pid: u32,
|
||||
ipc_config: Vec<toml::Value>,
|
||||
window_config: Vec<toml::Value>,
|
||||
config: Rc<UiConfig>,
|
||||
}
|
||||
|
||||
|
@ -252,7 +250,7 @@ impl WindowContext {
|
|||
message_buffer: Default::default(),
|
||||
search_state: Default::default(),
|
||||
event_queue: Default::default(),
|
||||
ipc_config: Default::default(),
|
||||
window_config: Default::default(),
|
||||
modifiers: Default::default(),
|
||||
occluded: Default::default(),
|
||||
mouse: Default::default(),
|
||||
|
@ -266,20 +264,20 @@ impl WindowContext {
|
|||
let old_config = mem::replace(&mut self.config, new_config);
|
||||
|
||||
// Apply ipc config if there are overrides.
|
||||
if !self.ipc_config.is_empty() {
|
||||
if !self.window_config.is_empty() {
|
||||
let mut config = (*self.config).clone();
|
||||
|
||||
// Apply each option, removing broken ones.
|
||||
let mut i = 0;
|
||||
while i < self.ipc_config.len() {
|
||||
let option = &self.ipc_config[i];
|
||||
while i < self.window_config.len() {
|
||||
let option = &self.window_config[i];
|
||||
match config.replace(option.clone()) {
|
||||
Err(err) => {
|
||||
error!(
|
||||
target: LOG_TARGET_IPC_CONFIG,
|
||||
target: LOG_TARGET_WINDOW_CONFIG,
|
||||
"Unable to override option '{}': {}", option, err
|
||||
);
|
||||
self.ipc_config.swap_remove(i);
|
||||
self.window_config.swap_remove(i);
|
||||
},
|
||||
Ok(_) => i += 1,
|
||||
}
|
||||
|
@ -355,24 +353,32 @@ impl WindowContext {
|
|||
self.dirty = true;
|
||||
}
|
||||
|
||||
/// Update the IPC config overrides.
|
||||
/// Clear the window config overrides.
|
||||
#[cfg(unix)]
|
||||
pub fn update_ipc_config(&mut self, config: Rc<UiConfig>, ipc_config: IpcConfig) {
|
||||
// Clear previous IPC errors.
|
||||
self.message_buffer.remove_target(LOG_TARGET_IPC_CONFIG);
|
||||
pub fn reset_window_config(&mut self, config: Rc<UiConfig>) {
|
||||
// Clear previous window errors.
|
||||
self.message_buffer.remove_target(LOG_TARGET_WINDOW_CONFIG);
|
||||
|
||||
if ipc_config.reset {
|
||||
self.ipc_config.clear();
|
||||
} else {
|
||||
for option in &ipc_config.options {
|
||||
// Try and parse option as toml.
|
||||
match toml::from_str(option) {
|
||||
Ok(value) => self.ipc_config.push(value),
|
||||
Err(err) => error!(
|
||||
target: LOG_TARGET_IPC_CONFIG,
|
||||
"'{}': Invalid IPC config value: {:?}", option, err
|
||||
),
|
||||
}
|
||||
self.window_config.clear();
|
||||
|
||||
// Reload current config to pull new IPC config.
|
||||
self.update_config(config);
|
||||
}
|
||||
|
||||
/// Add new window config overrides.
|
||||
#[cfg(unix)]
|
||||
pub fn add_window_config(&mut self, config: Rc<UiConfig>, options: &[String]) {
|
||||
// Clear previous window errors.
|
||||
self.message_buffer.remove_target(LOG_TARGET_WINDOW_CONFIG);
|
||||
|
||||
for option in options {
|
||||
// Try and parse option as toml.
|
||||
match toml::from_str(option) {
|
||||
Ok(value) => self.window_config.push(value),
|
||||
Err(err) => error!(
|
||||
target: LOG_TARGET_WINDOW_CONFIG,
|
||||
"'{}': Invalid window config value: {:?}", option, err
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue