Repository: alacritty/alacritty (language: rust)

Issue:
`alacritty msg create-window` should support configuration
I've been using `alacritty msg create-window` to manage multiple terminals at once, which works great.
However, it does not support configuration flags, such as `-o=...` or `--config-file ...`.

I would imagine this could be due to technical reasons, as settings like `ipc_socket` or `debug.*` only make sense from an instance, rather than an individual window.

I request that window-specific configuration is implemented in a way that overrides the instance configuration, using the same flags as currently exposed on the main executable.

If an instance-specific configuration flag such as `ipc_socket` or `debug.*` is set to a mismatching value, either on the command line or in a file, when it doesn't make sense to override, alacritty could throw an error.

I'm guessing that configuration would need to be refactored for this change to be implemented, because it's probably not structured per-window at this time, so I don't expect it to be easy to implement.

### System

OS: Arch Linux
Version: `alacritty 0.10.1`
Linux: Wayland, Sway

### Logs

```
$ alacritty msg create-window -o="colors.primary.background='#FFFFFF'"
error: Found argument '-o' which wasn't expected, or isn't valid in this context

USAGE:
    alacritty msg create-window [FLAGS] [OPTIONS]

For more information try --help
```


Discussion / hints:
See https://github.com/alacritty/alacritty/pull/6174.
Rather than using `config` subcommand, would it be better if we can add option directly to `CreateNewWindow` action, so we can create custom key binding, example: 

`alacritty msg create-window -o 'font.size=15' -o 'window.decoration=full'`
`  - { key: N, mods: Control|Shift, action: CreateNewWindow, options: [ "font.size=15", "window.decoration=full" ] }`

Thanks in advance,
Aswad
Yeah, adding `-o` should be trivial now and I think it was one of the goals of IPC subcommand to config.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c387e8a1097..1ca8c1f5e5c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Debug option `prefer_egl` to prioritize EGL over other display APIs
 - Inline vi-mode search using `f`/`F`/`t`/`T`
 - `window.blur` config option to request blur for transparent windows
+- `--option` argument for `alacritty msg create-window`
 
 ### Changed
 
diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs
index 4745ee8d2ed..67446faba67 100644
--- a/alacritty/src/cli.rs
+++ b/alacritty/src/cli.rs
@@ -1,16 +1,20 @@
 use std::cmp::max;
+use std::ops::{Deref, DerefMut};
 use std::path::PathBuf;
+use std::rc::Rc;
 
+use alacritty_config::SerdeReplace;
 use clap::{ArgAction, Args, Parser, Subcommand, ValueHint};
 use log::{self, error, LevelFilter};
 use serde::{Deserialize, Serialize};
-use toml::{Table, Value};
+use toml::Value;
 
 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;
+use crate::logging::LOG_TARGET_IPC_CONFIG;
 
 /// CLI options for the main Alacritty executable.
 #[derive(Parser, Default, Debug)]
@@ -57,13 +61,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: ParsedOptions,
 
     /// Options which can be passed via IPC.
     #[clap(flatten)]
@@ -78,14 +78,14 @@ 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));
+        // Parse CLI config overrides.
+        options.config_options = options.window_options.config_overrides();
 
         options
     }
 
     /// Override configuration file with options from the CLI.
-    pub fn override_config(&self, config: &mut UiConfig) {
+    pub fn override_config(&mut self, config: &mut UiConfig) {
         #[cfg(unix)]
         {
             config.ipc_socket |= self.socket.is_some();
@@ -100,6 +100,9 @@ impl Options {
         if config.debug.print_events {
             config.debug.log_level = max(config.debug.log_level, LevelFilter::Info);
         }
+
+        // Replace CLI options.
+        self.config_options.override_config(config);
     }
 
     /// Logging filter level.
@@ -123,17 +126,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,13 +294,24 @@ 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>,
+}
+
+impl WindowOptions {
+    /// Get the parsed set of CLI config overrides.
+    pub fn config_overrides(&self) -> ParsedOptions {
+        ParsedOptions::from_options(&self.option)
+    }
 }
 
 /// Parameters to the `config` IPC subcommand.
 #[cfg(unix)]
 #[derive(Args, Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
 pub struct IpcConfig {
-    /// Configuration file options [example: cursor.style=Beam].
+    /// Configuration file options [example: 'cursor.style="Beam"'].
     #[clap(required = true, value_name = "CONFIG_OPTIONS")]
     pub options: Vec<String>,
 
@@ -323,13 +326,75 @@ pub struct IpcConfig {
     pub reset: bool,
 }
 
-/// Toml value with default implementation.
-#[derive(Debug)]
-pub struct TomlValue(pub Value);
+/// Parsed CLI config overrides.
+#[derive(Debug, Default)]
+pub struct ParsedOptions {
+    config_options: Vec<(String, Value)>,
+}
+
+impl ParsedOptions {
+    /// Parse CLI config overrides.
+    pub fn from_options(options: &[String]) -> Self {
+        let mut config_options = Vec::new();
+
+        for option in options {
+            let parsed = match toml::from_str(option) {
+                Ok(parsed) => parsed,
+                Err(err) => {
+                    eprintln!("Ignoring invalid CLI option '{option}': {err}");
+                    continue;
+                },
+            };
+            config_options.push((option.clone(), parsed));
+        }
+
+        Self { config_options }
+    }
+
+    /// Apply CLI config overrides, removing broken ones.
+    pub fn override_config(&mut self, config: &mut UiConfig) {
+        let mut i = 0;
+        while i < self.config_options.len() {
+            let (option, parsed) = &self.config_options[i];
+            match config.replace(parsed.clone()) {
+                Err(err) => {
+                    error!(
+                        target: LOG_TARGET_IPC_CONFIG,
+                        "Unable to override option '{}': {}", option, err
+                    );
+                    self.config_options.swap_remove(i);
+                },
+                Ok(_) => i += 1,
+            }
+        }
+    }
+
+    /// Apply CLI config overrides to a CoW config.
+    pub fn override_config_rc(&mut self, config: Rc<UiConfig>) -> Rc<UiConfig> {
+        // Skip clone without write requirement.
+        if self.config_options.is_empty() {
+            return config;
+        }
+
+        // Override cloned config.
+        let mut config = (*config).clone();
+        self.override_config(&mut config);
+
+        Rc::new(config)
+    }
+}
+
+impl Deref for ParsedOptions {
+    type Target = Vec<(String, Value)>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.config_options
+    }
+}
 
-impl Default for TomlValue {
-    fn default() -> Self {
-        Self(Value::Table(Table::new()))
+impl DerefMut for ParsedOptions {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.config_options
     }
 }
 
@@ -365,7 +430,7 @@ mod tests {
         let title = Some(String::from("foo"));
         let window_identity = WindowIdentity { title, ..WindowIdentity::default() };
         let new_window_options = WindowOptions { window_identity, ..WindowOptions::default() };
-        let options = Options { window_options: new_window_options, ..Options::default() };
+        let mut options = Options { window_options: new_window_options, ..Options::default() };
         options.override_config(&mut config);
 
         assert!(!config.window.dynamic_title);
diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs
index 678e08d5d8b..95a1ff007ed 100644
--- a/alacritty/src/config/mod.rs
+++ b/alacritty/src/config/mod.rs
@@ -126,8 +126,7 @@ impl From<YamlError> for Error {
 }
 
 /// Load the configuration file.
-pub fn load(options: &Options) -> UiConfig {
-    let config_options = options.config_options.0.clone();
+pub fn load(options: &mut Options) -> UiConfig {
     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"),
@@ -156,12 +155,11 @@ pub fn load(options: &Options) -> UiConfig {
 }
 
 /// Attempt to reload the configuration file.
-pub fn reload(config_path: &Path, options: &Options) -> Result<UiConfig> {
+pub fn reload(config_path: &Path, options: &mut 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);
 
@@ -169,7 +167,7 @@ pub fn reload(config_path: &Path, options: &Options) -> Result<UiConfig> {
 }
 
 /// Modifications after the `UiConfig` object is created.
-fn after_loading(config: &mut UiConfig, options: &Options) {
+fn after_loading(config: &mut UiConfig, options: &mut Options) {
     // Override config with CLI options.
     options.override_config(config);
 
@@ -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)?;
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs
index 3dc1a262e39..f7fd044c270 100644
--- a/alacritty/src/event.rs
+++ b/alacritty/src/event.rs
@@ -36,7 +36,7 @@ use alacritty_terminal::term::search::{Match, RegexSearch};
 use alacritty_terminal::term::{self, ClipboardType, Term, TermMode};
 
 #[cfg(unix)]
-use crate::cli::IpcConfig;
+use crate::cli::{IpcConfig, ParsedOptions};
 use crate::cli::{Options as CliOptions, WindowOptions};
 use crate::clipboard::Clipboard;
 use crate::config::ui_config::{HintAction, HintInternalAction};
@@ -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);
         }
@@ -1479,7 +1479,7 @@ pub struct Processor {
     windows: HashMap<WindowId, WindowContext, RandomState>,
     gl_display: Option<GlutinDisplay>,
     #[cfg(unix)]
-    global_ipc_options: Vec<String>,
+    global_ipc_options: ParsedOptions,
     cli_options: CliOptions,
     config: Rc<UiConfig>,
 }
@@ -1531,17 +1531,16 @@ impl Processor {
     ) -> Result<(), Box<dyn Error>> {
         let window = self.windows.iter().next().as_ref().unwrap().1;
 
+        // Overide config with CLI/IPC options.
+        let mut config_overrides = options.config_overrides();
+        #[cfg(unix)]
+        config_overrides.extend_from_slice(&self.global_ipc_options);
+        let mut config = self.config.clone();
+        config = config_overrides.override_config_rc(config);
+
         #[allow(unused_mut)]
         let mut window_context =
-            window.additional(event_loop, proxy, self.config.clone(), options)?;
-
-        // Apply global IPC options.
-        #[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.additional(event_loop, proxy, config, options, config_overrides)?;
 
         self.windows.insert(window_context.id(), window_context);
         Ok(())
@@ -1713,7 +1712,7 @@ impl Processor {
                     }
 
                     // Load config and update each terminal.
-                    if let Ok(config) = config::reload(&path, &self.cli_options) {
+                    if let Ok(config) = config::reload(&path, &mut self.cli_options) {
                         self.config = Rc::new(config);
 
                         for window_context in self.windows.values_mut() {
@@ -1727,21 +1726,29 @@ impl Processor {
                     payload: EventType::IpcConfig(ipc_config),
                     window_id,
                 }) => {
-                    // Persist global options for future windows.
-                    if window_id.is_none() {
-                        if ipc_config.reset {
-                            self.global_ipc_options.clear();
-                        } else {
-                            self.global_ipc_options.extend_from_slice(&ipc_config.options);
-                        }
-                    }
+                    // Try and parse options as toml.
+                    let mut options = ParsedOptions::from_options(&ipc_config.options);
 
+                    // Override IPC config for each window with matching ID.
                     for (_, window_context) in self
                         .windows
                         .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(), &options);
+                        }
+                    }
+
+                    // Persist global options for future windows.
+                    if window_id.is_none() {
+                        if ipc_config.reset {
+                            self.global_ipc_options.clear();
+                        } else {
+                            self.global_ipc_options.append(&mut options);
+                        }
                     }
                 },
                 // Create a new terminal window.
diff --git a/alacritty/src/logging.rs b/alacritty/src/logging.rs
index 4c92a9de98f..6bede4e79f9 100644
--- a/alacritty/src/logging.rs
+++ b/alacritty/src/logging.rs
@@ -21,7 +21,7 @@ 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";
+pub const LOG_TARGET_IPC_CONFIG: &str = "alacritty_log_window_config";
 
 /// Name for the environment variable containing the log file's path.
 const ALACRITTY_LOG_ENV: &str = "ALACRITTY_LOG";
@@ -50,6 +50,7 @@ const ALLOWED_TARGETS: &[&str] = &[
     "crossfont",
 ];
 
+/// Initialize the logger to its defaults.
 pub fn initialize(
     options: &Options,
     event_proxy: EventLoopProxy<Event>,
diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs
index 028a42e8337..91d99cbea4e 100644
--- a/alacritty/src/main.rs
+++ b/alacritty/src/main.rs
@@ -122,7 +122,7 @@ impl Drop for TemporaryFiles {
 ///
 /// Creates a window, the terminal state, PTY, I/O event loop, input processor,
 /// config change monitor, and runs the main display loop.
-fn alacritty(options: Options) -> Result<(), Box<dyn Error>> {
+fn alacritty(mut options: Options) -> Result<(), Box<dyn Error>> {
     // Setup winit event loop.
     let window_event_loop = WinitEventLoopBuilder::<Event>::with_user_event().build()?;
 
@@ -139,7 +139,7 @@ fn alacritty(options: Options) -> Result<(), Box<dyn Error>> {
     info!("Running on Wayland");
 
     // Load configuration file.
-    let config = config::load(&options);
+    let config = config::load(&mut options);
     log_config_path(&config);
 
     // Update the log level from config.
diff --git a/alacritty/src/scheduler.rs b/alacritty/src/scheduler.rs
index ea8e627137e..aebed5e9855 100644
--- a/alacritty/src/scheduler.rs
+++ b/alacritty/src/scheduler.rs
@@ -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.
diff --git a/alacritty/src/window_context.rs b/alacritty/src/window_context.rs
index be8eba4744e..329e7ef804c 100644
--- a/alacritty/src/window_context.rs
+++ b/alacritty/src/window_context.rs
@@ -14,14 +14,13 @@ use glutin::config::GetGlConfig;
 use glutin::display::GetGlDisplay;
 #[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
 use glutin::platform::x11::X11GlConfigExt;
-use log::{error, info};
+use log::info;
 use raw_window_handle::HasRawDisplayHandle;
 use serde_json as json;
 use winit::event::{Event as WinitEvent, Modifiers, WindowEvent};
 use winit::event_loop::{EventLoopProxy, EventLoopWindowTarget};
 use winit::window::WindowId;
 
-use alacritty_config::SerdeReplace;
 use alacritty_terminal::event::Event as TerminalEvent;
 use alacritty_terminal::event_loop::{EventLoop as PtyEventLoop, Msg, Notifier};
 use alacritty_terminal::grid::{Dimensions, Scroll};
@@ -31,9 +30,7 @@ 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::cli::{ParsedOptions, WindowOptions};
 use crate::clipboard::Clipboard;
 use crate::config::UiConfig;
 use crate::display::window::Window;
@@ -41,6 +38,7 @@ use crate::display::Display;
 use crate::event::{
     ActionContext, Event, EventProxy, InlineSearchState, Mouse, SearchState, TouchPurpose,
 };
+#[cfg(unix)]
 use crate::logging::LOG_TARGET_IPC_CONFIG;
 use crate::message_bar::MessageBuffer;
 use crate::scheduler::Scheduler;
@@ -67,7 +65,7 @@ pub struct WindowContext {
     master_fd: RawFd,
     #[cfg(not(windows))]
     shell_pid: u32,
-    ipc_config: Vec<toml::Value>,
+    window_config: ParsedOptions,
     config: Rc<UiConfig>,
 }
 
@@ -128,6 +126,7 @@ impl WindowContext {
         proxy: EventLoopProxy<Event>,
         config: Rc<UiConfig>,
         options: WindowOptions,
+        config_overrides: ParsedOptions,
     ) -> Result<Self, Box<dyn Error>> {
         // Get any window and take its GL config and display to build a new context.
         let (gl_display, gl_config) = {
@@ -164,7 +163,14 @@ impl WindowContext {
 
         let display = Display::new(window, gl_context, &config, tabbed)?;
 
-        Self::new(display, config, options, proxy)
+        let mut window_context = Self::new(display, config, options, proxy)?;
+
+        // Set the config overrides at startup.
+        //
+        // These are already applied to `config`, so no update is necessary.
+        window_context.window_config = config_overrides;
+
+        Ok(window_context)
     }
 
     /// Create a new terminal window context.
@@ -250,9 +256,9 @@ impl WindowContext {
             cursor_blink_timed_out: Default::default(),
             inline_search_state: Default::default(),
             message_buffer: Default::default(),
+            window_config: Default::default(),
             search_state: Default::default(),
             event_queue: Default::default(),
-            ipc_config: Default::default(),
             modifiers: Default::default(),
             occluded: Default::default(),
             mouse: Default::default(),
@@ -266,27 +272,7 @@ 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() {
-            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];
-                match config.replace(option.clone()) {
-                    Err(err) => {
-                        error!(
-                            target: LOG_TARGET_IPC_CONFIG,
-                            "Unable to override option '{}': {}", option, err
-                        );
-                        self.ipc_config.swap_remove(i);
-                    },
-                    Ok(_) => i += 1,
-                }
-            }
-
-            self.config = Rc::new(config);
-        }
+        self.config = self.window_config.override_config_rc(self.config.clone());
 
         self.display.update_config(&self.config);
         self.terminal.lock().set_options(self.config.term_options());
@@ -355,26 +341,25 @@ 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.
+    pub fn reset_window_config(&mut self, config: Rc<UiConfig>) {
+        // Clear previous window errors.
         self.message_buffer.remove_target(LOG_TARGET_IPC_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: &ParsedOptions) {
+        // Clear previous window errors.
+        self.message_buffer.remove_target(LOG_TARGET_IPC_CONFIG);
+
+        self.window_config.extend_from_slice(options);
 
         // Reload current config to pull new IPC config.
         self.update_config(config);
diff --git a/extra/completions/_alacritty b/extra/completions/_alacritty
index 6f21f4506ad..a1739c4e1da 100644
--- a/extra/completions/_alacritty
+++ b/extra/completions/_alacritty
@@ -18,14 +18,14 @@ _alacritty() {
 '--embed=[X11 window ID to embed Alacritty within (decimal or hexadecimal with "0x" prefix)]:EMBED: ' \
 '--config-file=[Specify alternative configuration file \[default\: \$XDG_CONFIG_HOME/alacritty/alacritty.toml\]]:CONFIG_FILE:_files' \
 '--socket=[Path for IPC socket creation]:SOCKET:_files' \
-'*-o+[Override configuration file options \[example\: cursor.style=Beam\]]:OPTION: ' \
-'*--option=[Override configuration file options \[example\: cursor.style=Beam\]]:OPTION: ' \
 '--working-directory=[Start the shell in the specified working directory]:WORKING_DIRECTORY:_files' \
 '*-e+[Command and args to execute (must be last argument)]:COMMAND: ' \
 '*--command=[Command and args to execute (must be last argument)]:COMMAND: ' \
 '-T+[Defines the window title \[default\: Alacritty\]]:TITLE: ' \
 '--title=[Defines the window title \[default\: Alacritty\]]:TITLE: ' \
 '--class=[Defines window class/app_id on X11/Wayland \[default\: Alacritty\]]:general> | <general>,<instance: ' \
+'*-o+[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION: ' \
+'*--option=[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION: ' \
 '--print-events[Print all events to stdout]' \
 '--ref-test[Generates ref test]' \
 '(-v)*-q[Reduces the level of verbosity (the min level is -qq)]' \
@@ -68,6 +68,8 @@ _arguments "${_arguments_options[@]}" \
 '-T+[Defines the window title \[default\: Alacritty\]]:TITLE: ' \
 '--title=[Defines the window title \[default\: Alacritty\]]:TITLE: ' \
 '--class=[Defines window class/app_id on X11/Wayland \[default\: Alacritty\]]:general> | <general>,<instance: ' \
+'*-o+[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION: ' \
+'*--option=[Override configuration file options \[example\: '\''cursor.style="Beam"'\''\]]:OPTION: ' \
 '--hold[Remain open after child process exit]' \
 '-h[Print help]' \
 '--help[Print help]' \
@@ -81,7 +83,7 @@ _arguments "${_arguments_options[@]}" \
 '()--reset[Clear all runtime configuration changes]' \
 '-h[Print help (see more with '\''--help'\'')]' \
 '--help[Print help (see more with '\''--help'\'')]' \
-'*::options -- Configuration file options \[example\: cursor.style=Beam\]:' \
+'*::options -- Configuration file options \[example\: '\''cursor.style="Beam"'\''\]:' \
 && ret=0
 ;;
 (help)
diff --git a/extra/completions/alacritty.bash b/extra/completions/alacritty.bash
index 41547422178..c1546f500a9 100644
--- a/extra/completions/alacritty.bash
+++ b/extra/completions/alacritty.bash
@@ -61,7 +61,7 @@ _alacritty() {
 
     case "${cmd}" in
         alacritty)
-            opts="-q -v -o -e -T -h -V --print-events --ref-test --embed --config-file --socket --option --working-directory --hold --command --title --class --help --version msg migrate help"
+            opts="-q -v -e -T -o -h -V --print-events --ref-test --embed --config-file --socket --working-directory --hold --command --title --class --option --help --version msg migrate help"
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -79,14 +79,6 @@ _alacritty() {
                     COMPREPLY=($(compgen -f "${cur}"))
                     return 0
                     ;;
-                --option)
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    return 0
-                    ;;
-                -o)
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    return 0
-                    ;;
                 --working-directory)
                     COMPREPLY=($(compgen -f "${cur}"))
                     return 0
@@ -111,6 +103,14 @@ _alacritty() {
                     COMPREPLY=($(compgen -f "${cur}"))
                     return 0
                     ;;
+                --option)
+                    COMPREPLY=($(compgen -f "${cur}"))
+                    return 0
+                    ;;
+                -o)
+                    COMPREPLY=($(compgen -f "${cur}"))
+                    return 0
+                    ;;
                 *)
                     COMPREPLY=()
                     ;;
@@ -269,7 +269,7 @@ _alacritty() {
             return 0
             ;;
         alacritty__msg__create__window)
-            opts="-e -T -h --working-directory --hold --command --title --class --help"
+            opts="-e -T -o -h --working-directory --hold --command --title --class --option --help"
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -299,6 +299,14 @@ _alacritty() {
                     COMPREPLY=($(compgen -f "${cur}"))
                     return 0
                     ;;
+                --option)
+                    COMPREPLY=($(compgen -f "${cur}"))
+                    return 0
+                    ;;
+                -o)
+                    COMPREPLY=($(compgen -f "${cur}"))
+                    return 0
+                    ;;
                 *)
                     COMPREPLY=()
                     ;;
diff --git a/extra/completions/alacritty.fish b/extra/completions/alacritty.fish
index fe02ea82e71..243f144a575 100644
--- a/extra/completions/alacritty.fish
+++ b/extra/completions/alacritty.fish
@@ -1,11 +1,11 @@
 complete -c alacritty -n "__fish_use_subcommand" -l embed -d 'X11 window ID to embed Alacritty within (decimal or hexadecimal with "0x" prefix)' -r
 complete -c alacritty -n "__fish_use_subcommand" -l config-file -d 'Specify alternative configuration file [default: $XDG_CONFIG_HOME/alacritty/alacritty.toml]' -r -F
 complete -c alacritty -n "__fish_use_subcommand" -l socket -d 'Path for IPC socket creation' -r -F
-complete -c alacritty -n "__fish_use_subcommand" -s o -l option -d 'Override configuration file options [example: cursor.style=Beam]' -r
 complete -c alacritty -n "__fish_use_subcommand" -l working-directory -d 'Start the shell in the specified working directory' -r -F
 complete -c alacritty -n "__fish_use_subcommand" -s e -l command -d 'Command and args to execute (must be last argument)' -r
 complete -c alacritty -n "__fish_use_subcommand" -s T -l title -d 'Defines the window title [default: Alacritty]' -r
 complete -c alacritty -n "__fish_use_subcommand" -l class -d 'Defines window class/app_id on X11/Wayland [default: Alacritty]' -r
+complete -c alacritty -n "__fish_use_subcommand" -s o -l option -d 'Override configuration file options [example: \'cursor.style="Beam"\']' -r
 complete -c alacritty -n "__fish_use_subcommand" -l print-events -d 'Print all events to stdout'
 complete -c alacritty -n "__fish_use_subcommand" -l ref-test -d 'Generates ref test'
 complete -c alacritty -n "__fish_use_subcommand" -s q -d 'Reduces the level of verbosity (the min level is -qq)'
@@ -25,6 +25,7 @@ complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subco
 complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -s e -l command -d 'Command and args to execute (must be last argument)' -r
 complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -s T -l title -d 'Defines the window title [default: Alacritty]' -r
 complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -l class -d 'Defines window class/app_id on X11/Wayland [default: Alacritty]' -r
+complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -s o -l option -d 'Override configuration file options [example: \'cursor.style="Beam"\']' -r
 complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -l hold -d 'Remain open after child process exit'
 complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from create-window" -s h -l help -d 'Print help'
 complete -c alacritty -n "__fish_seen_subcommand_from msg; and __fish_seen_subcommand_from config" -s w -l window-id -d 'Window ID for the new config' -r
diff --git a/extra/man/alacritty-msg.1.scd b/extra/man/alacritty-msg.1.scd
index f7f271aaf6d..100b763ec8b 100644
--- a/extra/man/alacritty-msg.1.scd
+++ b/extra/man/alacritty-msg.1.scd
@@ -19,14 +19,24 @@ making it possible to control Alacritty without directly accessing it.
 *create-window*
 	Create a new window in the same Alacritty process
 
-	*OPTIONS*
+	*FLAGS*
 		*--hold*
 			Remain open after child process exits
 
+	*OPTIONS*
 		*--working-directory* <working-directory>
 			Start the shell in the specified working directory
 
-		*command* <command>...
+		*-T, --title* <TITLE>
+			Defines the window title [default: Alacritty]
+
+		*--class* <GENERAL> | <GENERAL>,<INSTANCE>
+			Defines window class/app_id on X11/Wayland [default: Alacritty]
+
+		*-o, --option* <OPTION>...
+			Override configuration file options [example: 'cursor.style="Beam"']
+
+		*-e, --command* <COMMAND>...
 			Command and args to execute (must be last argument)
 
 *config*
@@ -34,7 +44,11 @@ making it possible to control Alacritty without directly accessing it.
 
 	*ARGS*
 		*<CONFIG_OPTIONS>...*
-			Configuration file options [example: cursor.style=Beam]
+			Configuration file options [example: 'cursor.style="Beam"']
+
+	*FLAGS*
+		*-r, --reset*
+			Clear all runtime configuration changes
 
 	*OPTIONS*
 		*-w, --window-id* <WINDOW_ID>
diff --git a/extra/man/alacritty.1.scd b/extra/man/alacritty.1.scd
index 333d23100fa..6436db75063 100644
--- a/extra/man/alacritty.1.scd
+++ b/extra/man/alacritty.1.scd
@@ -61,7 +61,7 @@ set of features with high performance.
 	X11 window ID to embed Alacritty within (decimal or hexadecimal with _0x_ prefix)
 
 *-o, --option* <option>...
-	Override configuration file options [example: cursor.style=Beam]
+	Override configuration file options [example: 'cursor.style="Beam"']
 
 *--socket* <socket>
 	Path for IPC socket creation


Test patch:
diff --git a/alacritty_terminal/tests/ref.rs b/alacritty_terminal/tests/ref.rs
index 4f8bc74acab..7f864ae8a93 100644
--- a/alacritty_terminal/tests/ref.rs
+++ b/alacritty_terminal/tests/ref.rs
@@ -104,8 +104,8 @@ fn ref_test(dir: &Path) {
     let grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap();
     let ref_config: RefConfig = json::from_str(&serialized_cfg).unwrap();
 
-    let mut options = Config::default();
-    options.scrolling_history = ref_config.history_size as usize;
+    let options =
+        Config { scrolling_history: ref_config.history_size as usize, ..Default::default() };
 
     let mut terminal = Term::new(options, &size, Mock);
     let mut parser: ansi::Processor = ansi::Processor::new();