diff options
Diffstat (limited to 'alacritty/src/config')
-rw-r--r-- | alacritty/src/config/bindings.rs | 38 | ||||
-rw-r--r-- | alacritty/src/config/color.rs | 7 | ||||
-rw-r--r-- | alacritty/src/config/mod.rs | 202 | ||||
-rw-r--r-- | alacritty/src/config/mouse.rs | 28 | ||||
-rw-r--r-- | alacritty/src/config/serde_utils.rs | 66 | ||||
-rw-r--r-- | alacritty/src/config/ui_config.rs | 109 | ||||
-rw-r--r-- | alacritty/src/config/window.rs | 4 |
7 files changed, 269 insertions, 185 deletions
diff --git a/alacritty/src/config/bindings.rs b/alacritty/src/config/bindings.rs index 692cf7e9..8fd16361 100644 --- a/alacritty/src/config/bindings.rs +++ b/alacritty/src/config/bindings.rs @@ -5,7 +5,7 @@ use std::fmt::{self, Debug, Display}; use bitflags::bitflags; use serde::de::{self, Error as SerdeError, MapAccess, Unexpected, Visitor}; use serde::{Deserialize, Deserializer}; -use serde_yaml::Value as SerdeValue; +use toml::Value as SerdeValue; use winit::event::VirtualKeyCode::*; use winit::event::{ModifiersState, MouseButton, VirtualKeyCode}; @@ -1011,19 +1011,20 @@ impl<'a> Deserialize<'a> for RawBinding { return Err(<V::Error as Error>::duplicate_field("key")); } - let val = map.next_value::<SerdeValue>()?; - if val.is_u64() { - let scancode = val.as_u64().unwrap(); - if scancode > u64::from(u32::MAX) { - return Err(<V::Error as Error>::custom(format!( - "Invalid key binding, scancode too big: {}", - scancode - ))); - } - key = Some(Key::Scancode(scancode as u32)); - } else { - let k = Key::deserialize(val).map_err(V::Error::custom)?; - key = Some(k); + let value = map.next_value::<SerdeValue>()?; + match value.as_integer() { + Some(scancode) => match u32::try_from(scancode) { + Ok(scancode) => key = Some(Key::Scancode(scancode)), + Err(_) => { + return Err(<V::Error as Error>::custom(format!( + "Invalid key binding, scancode is too big: {}", + scancode + ))); + }, + }, + None => { + key = Some(Key::deserialize(value).map_err(V::Error::custom)?); + }, } }, Field::Mods => { @@ -1066,15 +1067,6 @@ impl<'a> Deserialize<'a> for RawBinding { Err(err) => { let value = match value { SerdeValue::String(string) => string, - SerdeValue::Mapping(map) if map.len() == 1 => { - match map.into_iter().next() { - Some(( - SerdeValue::String(string), - SerdeValue::Null, - )) => string, - _ => return Err(err), - } - }, _ => return Err(err), }; return Err(V::Error::custom(format!( diff --git a/alacritty/src/config/color.rs b/alacritty/src/config/color.rs index 23c18e50..e08b08b4 100644 --- a/alacritty/src/config/color.rs +++ b/alacritty/src/config/color.rs @@ -18,16 +18,17 @@ pub struct Colors { pub line_indicator: LineIndicatorColors, pub hints: HintColors, pub transparent_background_colors: bool, + pub draw_bold_text_with_bright_colors: bool, footer_bar: BarColors, } impl Colors { pub fn footer_bar_foreground(&self) -> Rgb { - self.search.bar.foreground.or(self.footer_bar.foreground).unwrap_or(self.primary.background) + self.footer_bar.foreground.unwrap_or(self.primary.background) } pub fn footer_bar_background(&self) -> Rgb { - self.search.bar.background.or(self.footer_bar.background).unwrap_or(self.primary.foreground) + self.footer_bar.background.unwrap_or(self.primary.foreground) } } @@ -126,8 +127,6 @@ impl Default for InvertedCellColors { pub struct SearchColors { pub focused_match: FocusedMatchColors, pub matches: MatchColors, - #[config(deprecated = "use `colors.footer_bar` instead")] - bar: BarColors, } #[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)] diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs index 2f230b06..821e9b6b 100644 --- a/alacritty/src/config/mod.rs +++ b/alacritty/src/config/mod.rs @@ -1,11 +1,14 @@ use std::fmt::{self, Display, Formatter}; use std::path::{Path, PathBuf}; +use std::result::Result as StdResult; use std::{env, fs, io}; -use log::{debug, error, info}; +use log::{debug, error, info, warn}; use serde::Deserialize; -use serde_yaml::mapping::Mapping; -use serde_yaml::Value; +use serde_yaml::Error as YamlError; +use toml::de::Error as TomlError; +use toml::ser::Error as TomlSeError; +use toml::{Table, Value}; use alacritty_terminal::config::LOG_TARGET_CONFIG; @@ -30,7 +33,7 @@ pub use crate::config::mouse::{ClickHandler, Mouse}; pub use crate::config::ui_config::UiConfig; /// Maximum number of depth for the configuration file imports. -const IMPORT_RECURSION_LIMIT: usize = 5; +pub const IMPORT_RECURSION_LIMIT: usize = 5; /// Result from config loading. pub type Result<T> = std::result::Result<T, Error>; @@ -47,8 +50,14 @@ pub enum Error { /// io error reading file. Io(io::Error), - /// Not valid yaml or missing parameters. - Yaml(serde_yaml::Error), + /// Invalid toml. + Toml(TomlError), + + /// Failed toml serialization. + TomlSe(TomlSeError), + + /// Invalid yaml. + Yaml(YamlError), } impl std::error::Error for Error { @@ -57,6 +66,8 @@ impl std::error::Error for Error { Error::NotFound => None, Error::ReadingEnvHome(err) => err.source(), Error::Io(err) => err.source(), + Error::Toml(err) => err.source(), + Error::TomlSe(err) => err.source(), Error::Yaml(err) => err.source(), } } @@ -70,6 +81,8 @@ impl Display for Error { write!(f, "Unable to read $HOME environment variable: {}", err) }, Error::Io(err) => write!(f, "Error reading config file: {}", err), + Error::Toml(err) => write!(f, "Config error: {}", err), + Error::TomlSe(err) => write!(f, "Yaml conversion error: {}", err), Error::Yaml(err) => write!(f, "Config error: {}", err), } } @@ -91,16 +104,32 @@ impl From<io::Error> for Error { } } -impl From<serde_yaml::Error> for Error { - fn from(val: serde_yaml::Error) -> Self { +impl From<TomlError> for Error { + fn from(val: TomlError) -> Self { + Error::Toml(val) + } +} + +impl From<TomlSeError> for Error { + fn from(val: TomlSeError) -> Self { + Error::TomlSe(val) + } +} + +impl From<YamlError> for Error { + fn from(val: YamlError) -> Self { Error::Yaml(val) } } /// Load the configuration file. pub fn load(options: &Options) -> UiConfig { - let config_options = options.config_options.clone(); - let config_path = options.config_file.clone().or_else(installed_config); + let config_options = options.config_options.0.clone(); + let config_path = options + .config_file + .clone() + .or_else(|| installed_config("toml")) + .or_else(|| installed_config("yml")); // Load the config using the following fallback behavior: // - Config path + CLI overrides @@ -128,7 +157,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.clone(); + let config_options = options.config_options.0.clone(); let mut config = load_from(config_path, config_options)?; after_loading(&mut config, options); @@ -179,6 +208,16 @@ fn parse_config( ) -> Result<Value> { config_paths.push(path.to_owned()); + // Deserialize the configuration file. + let config = deserialize_config(path)?; + + // Merge config with imports. + let imports = load_imports(&config, config_paths, recursion_limit); + Ok(serde_utils::merge(imports, config)) +} + +/// Deserialize a configuration file. +pub fn deserialize_config(path: &Path) -> Result<Value> { let mut contents = fs::read_to_string(path)?; // Remove UTF-8 BOM. @@ -186,51 +225,84 @@ fn parse_config( contents = contents.split_off(3); } + // Convert YAML to TOML as a transitionary fallback mechanism. + let extension = path.extension().unwrap_or_default(); + if (extension == "yaml" || extension == "yml") && !contents.trim().is_empty() { + warn!("YAML config {path:?} is deprecated, please migrate to TOML"); + + let value: serde_yaml::Value = serde_yaml::from_str(&contents)?; + contents = toml::to_string(&value)?; + } + // Load configuration file as Value. - let config: Value = match serde_yaml::from_str(&contents) { - Ok(config) => config, - Err(error) => { - // Prevent parsing error with an empty string and commented out file. - if error.to_string() == "EOF while parsing a value" { - Value::Mapping(Mapping::new()) - } else { - return Err(Error::Yaml(error)); - } - }, - }; + let config: Value = toml::from_str(&contents)?; - // Merge config with imports. - let imports = load_imports(&config, config_paths, recursion_limit); - Ok(serde_utils::merge(imports, config)) + Ok(config) } /// Load all referenced configuration files. fn load_imports(config: &Value, config_paths: &mut Vec<PathBuf>, recursion_limit: usize) -> Value { - let imports = match config.get("import") { - Some(Value::Sequence(imports)) => imports, - Some(_) => { - error!(target: LOG_TARGET_CONFIG, "Invalid import type: expected a sequence"); - return Value::Null; + // Get paths for all imports. + let import_paths = match imports(config, recursion_limit) { + Ok(import_paths) => import_paths, + Err(err) => { + error!(target: LOG_TARGET_CONFIG, "{err}"); + return Value::Table(Table::new()); }, - None => return Value::Null, + }; + + // Parse configs for all imports recursively. + let mut merged = Value::Table(Table::new()); + for import_path in import_paths { + let path = match import_path { + Ok(path) => path, + Err(err) => { + error!(target: LOG_TARGET_CONFIG, "{err}"); + continue; + }, + }; + + if !path.exists() { + info!(target: LOG_TARGET_CONFIG, "Config import not found:\n {:?}", path.display()); + continue; + } + + match parse_config(&path, config_paths, recursion_limit - 1) { + Ok(config) => merged = serde_utils::merge(merged, config), + Err(err) => { + error!(target: LOG_TARGET_CONFIG, "Unable to import config {:?}: {}", path, err) + }, + } + } + + merged +} + +// TODO: Merge back with `load_imports` once `alacritty migrate` is dropped. +// +/// Get all import paths for a configuration. +pub fn imports( + config: &Value, + recursion_limit: usize, +) -> StdResult<Vec<StdResult<PathBuf, String>>, String> { + let imports = match config.get("import") { + Some(Value::Array(imports)) => imports, + Some(_) => return Err("Invalid import type: expected a sequence".into()), + None => return Ok(Vec::new()), }; // Limit recursion to prevent infinite loops. if !imports.is_empty() && recursion_limit == 0 { - error!(target: LOG_TARGET_CONFIG, "Exceeded maximum configuration import depth"); - return Value::Null; + return Err("Exceeded maximum configuration import depth".into()); } - let mut merged = Value::Null; + let mut import_paths = Vec::new(); for import in imports { let mut path = match import { Value::String(path) => PathBuf::from(path), _ => { - error!( - target: LOG_TARGET_CONFIG, - "Invalid import element type: expected path string" - ); + import_paths.push(Err("Invalid import element type: expected path string".into())); continue; }, }; @@ -240,49 +312,42 @@ fn load_imports(config: &Value, config_paths: &mut Vec<PathBuf>, recursion_limit path = home_dir.join(stripped); } - if !path.exists() { - info!(target: LOG_TARGET_CONFIG, "Config import not found:\n {:?}", path.display()); - continue; - } - - match parse_config(&path, config_paths, recursion_limit - 1) { - Ok(config) => merged = serde_utils::merge(merged, config), - Err(err) => { - error!(target: LOG_TARGET_CONFIG, "Unable to import config {:?}: {}", path, err) - }, - } + import_paths.push(Ok(path)); } - merged + Ok(import_paths) } /// Get the location of the first found default config file paths /// according to the following order: /// -/// 1. $XDG_CONFIG_HOME/alacritty/alacritty.yml -/// 2. $XDG_CONFIG_HOME/alacritty.yml -/// 3. $HOME/.config/alacritty/alacritty.yml -/// 4. $HOME/.alacritty.yml +/// 1. $XDG_CONFIG_HOME/alacritty/alacritty.toml +/// 2. $XDG_CONFIG_HOME/alacritty.toml +/// 3. $HOME/.config/alacritty/alacritty.toml +/// 4. $HOME/.alacritty.toml #[cfg(not(windows))] -fn installed_config() -> Option<PathBuf> { +pub fn installed_config(suffix: &str) -> Option<PathBuf> { + let file_name = format!("alacritty.{suffix}"); + // Try using XDG location by default. xdg::BaseDirectories::with_prefix("alacritty") .ok() - .and_then(|xdg| xdg.find_config_file("alacritty.yml")) + .and_then(|xdg| xdg.find_config_file(&file_name)) .or_else(|| { xdg::BaseDirectories::new() .ok() - .and_then(|fallback| fallback.find_config_file("alacritty.yml")) + .and_then(|fallback| fallback.find_config_file(&file_name)) }) .or_else(|| { if let Ok(home) = env::var("HOME") { - // Fallback path: $HOME/.config/alacritty/alacritty.yml. - let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml"); + // Fallback path: $HOME/.config/alacritty/alacritty.toml. + let fallback = PathBuf::from(&home).join(".config/alacritty").join(&file_name); if fallback.exists() { return Some(fallback); } - // Fallback path: $HOME/.alacritty.yml. - let fallback = PathBuf::from(&home).join(".alacritty.yml"); + // Fallback path: $HOME/.alacritty.toml. + let hidden_name = format!(".{file_name}"); + let fallback = PathBuf::from(&home).join(hidden_name); if fallback.exists() { return Some(fallback); } @@ -292,22 +357,17 @@ fn installed_config() -> Option<PathBuf> { } #[cfg(windows)] -fn installed_config() -> Option<PathBuf> { - dirs::config_dir().map(|path| path.join("alacritty\\alacritty.yml")).filter(|new| new.exists()) +pub fn installed_config(suffix: &str) -> Option<PathBuf> { + let file_name = format!("alacritty.{suffix}"); + dirs::config_dir().map(|path| path.join("alacritty").join(file_name)).filter(|new| new.exists()) } #[cfg(test)] mod tests { use super::*; - static DEFAULT_ALACRITTY_CONFIG: &str = - concat!(env!("CARGO_MANIFEST_DIR"), "/../alacritty.yml"); - #[test] - fn config_read_eof() { - let config_path: PathBuf = DEFAULT_ALACRITTY_CONFIG.into(); - let mut config = read_config(&config_path, Value::Null).unwrap(); - config.config_paths = Vec::new(); - assert_eq!(config, UiConfig::default()); + fn empty_config() { + toml::from_str::<UiConfig>("").unwrap(); } } diff --git a/alacritty/src/config/mouse.rs b/alacritty/src/config/mouse.rs index 291e4c61..b6556a2c 100644 --- a/alacritty/src/config/mouse.rs +++ b/alacritty/src/config/mouse.rs @@ -1,14 +1,18 @@ use std::time::Duration; -use alacritty_config_derive::ConfigDeserialize; +use serde::{Deserialize, Deserializer}; + +use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; + +use crate::config::bindings::{self, MouseBinding}; +use crate::config::ui_config; #[derive(ConfigDeserialize, Default, Clone, Debug, PartialEq, Eq)] pub struct Mouse { pub double_click: ClickHandler, pub triple_click: ClickHandler, pub hide_when_typing: bool, - #[config(deprecated = "use `hints` section instead")] - pub url: Option<serde_yaml::Value>, + pub bindings: MouseBindings, } #[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)] @@ -27,3 +31,21 @@ impl ClickHandler { Duration::from_millis(self.threshold as u64) } } + +#[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)] +pub struct MouseBindings(pub Vec<MouseBinding>); + +impl Default for MouseBindings { + fn default() -> Self { + Self(bindings::default_mouse_bindings()) + } +} + +impl<'de> Deserialize<'de> for MouseBindings { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + Ok(Self(ui_config::deserialize_bindings(deserializer, Self::default().0)?)) + } +} diff --git a/alacritty/src/config/serde_utils.rs b/alacritty/src/config/serde_utils.rs index beb9c36b..476133e0 100644 --- a/alacritty/src/config/serde_utils.rs +++ b/alacritty/src/config/serde_utils.rs @@ -1,7 +1,6 @@ //! Serde helpers. -use serde_yaml::mapping::Mapping; -use serde_yaml::Value; +use toml::{Table, Value}; /// Merge two serde structures. /// @@ -9,20 +8,19 @@ use serde_yaml::Value; /// `replacement`. pub fn merge(base: Value, replacement: Value) -> Value { match (base, replacement) { - (Value::Sequence(mut base), Value::Sequence(mut replacement)) => { + (Value::Array(mut base), Value::Array(mut replacement)) => { base.append(&mut replacement); - Value::Sequence(base) + Value::Array(base) }, - (Value::Mapping(base), Value::Mapping(replacement)) => { - Value::Mapping(merge_mapping(base, replacement)) + (Value::Table(base), Value::Table(replacement)) => { + Value::Table(merge_tables(base, replacement)) }, - (value, Value::Null) => value, (_, value) => value, } } -/// Merge two key/value mappings. -fn merge_mapping(mut base: Mapping, replacement: Mapping) -> Mapping { +/// Merge two key/value tables. +fn merge_tables(mut base: Table, replacement: Table) -> Table { for (key, value) in replacement { let value = match base.remove(&key) { Some(base_value) => merge(base_value, value), @@ -40,54 +38,54 @@ mod tests { #[test] fn merge_primitive() { - let base = Value::Null; - let replacement = Value::Bool(true); + let base = Value::Table(Table::new()); + let replacement = Value::Boolean(true); assert_eq!(merge(base, replacement.clone()), replacement); - let base = Value::Bool(false); - let replacement = Value::Bool(true); + let base = Value::Boolean(false); + let replacement = Value::Boolean(true); assert_eq!(merge(base, replacement.clone()), replacement); - let base = Value::Number(0.into()); - let replacement = Value::Number(1.into()); + let base = Value::Integer(0.into()); + let replacement = Value::Integer(1.into()); assert_eq!(merge(base, replacement.clone()), replacement); let base = Value::String(String::new()); let replacement = Value::String(String::from("test")); assert_eq!(merge(base, replacement.clone()), replacement); - let base = Value::Mapping(Mapping::new()); - let replacement = Value::Null; + let base = Value::Table(Table::new()); + let replacement = Value::Table(Table::new()); assert_eq!(merge(base.clone(), replacement), base); } #[test] fn merge_sequence() { - let base = Value::Sequence(vec![Value::Null]); - let replacement = Value::Sequence(vec![Value::Bool(true)]); - let expected = Value::Sequence(vec![Value::Null, Value::Bool(true)]); + let base = Value::Array(vec![Value::Table(Table::new())]); + let replacement = Value::Array(vec![Value::Boolean(true)]); + let expected = Value::Array(vec![Value::Table(Table::new()), Value::Boolean(true)]); assert_eq!(merge(base, replacement), expected); } #[test] - fn merge_mapping() { - let mut base_mapping = Mapping::new(); - base_mapping.insert(Value::String(String::from("a")), Value::Bool(true)); - base_mapping.insert(Value::String(String::from("b")), Value::Bool(false)); - let base = Value::Mapping(base_mapping); + fn merge_tables() { + let mut base_table = Table::new(); + base_table.insert(String::from("a"), Value::Boolean(true)); + base_table.insert(String::from("b"), Value::Boolean(false)); + let base = Value::Table(base_table); - let mut replacement_mapping = Mapping::new(); - replacement_mapping.insert(Value::String(String::from("a")), Value::Bool(true)); - replacement_mapping.insert(Value::String(String::from("c")), Value::Bool(false)); - let replacement = Value::Mapping(replacement_mapping); + let mut replacement_table = Table::new(); + replacement_table.insert(String::from("a"), Value::Boolean(true)); + replacement_table.insert(String::from("c"), Value::Boolean(false)); + let replacement = Value::Table(replacement_table); let merged = merge(base, replacement); - let mut expected_mapping = Mapping::new(); - expected_mapping.insert(Value::String(String::from("b")), Value::Bool(false)); - expected_mapping.insert(Value::String(String::from("a")), Value::Bool(true)); - expected_mapping.insert(Value::String(String::from("c")), Value::Bool(false)); - let expected = Value::Mapping(expected_mapping); + let mut expected_table = Table::new(); + expected_table.insert(String::from("b"), Value::Boolean(false)); + expected_table.insert(String::from("a"), Value::Boolean(true)); + expected_table.insert(String::from("c"), Value::Boolean(false)); + let expected = Value::Table(expected_table); assert_eq!(merged, expected); } diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs index 29ff2c4c..0933ea42 100644 --- a/alacritty/src/config/ui_config.rs +++ b/alacritty/src/config/ui_config.rs @@ -3,16 +3,14 @@ use std::fmt::{self, Formatter}; use std::path::PathBuf; use std::rc::Rc; -use log::error; +use log::{error, warn}; use serde::de::{Error as SerdeError, MapAccess, Visitor}; use serde::{self, Deserialize, Deserializer}; use unicode_width::UnicodeWidthChar; use winit::event::{ModifiersState, VirtualKeyCode}; use alacritty_config_derive::{ConfigDeserialize, SerdeReplace}; -use alacritty_terminal::config::{ - Config as TerminalConfig, Percentage, Program, LOG_TARGET_CONFIG, -}; +use alacritty_terminal::config::{Config as TerminalConfig, Program, LOG_TARGET_CONFIG}; use alacritty_terminal::term::search::RegexSearch; use crate::config::bell::BellConfig; @@ -22,7 +20,7 @@ use crate::config::bindings::{ use crate::config::color::Colors; use crate::config::debug::Debug; use crate::config::font::Font; -use crate::config::mouse::Mouse; +use crate::config::mouse::{Mouse, MouseBindings}; use crate::config::window::WindowConfig; /// Regex used for the default URL hint. @@ -38,6 +36,7 @@ pub struct UiConfig { /// Window configuration. pub window: WindowConfig, + /// Mouse configuration. pub mouse: Mouse, /// Debug options. @@ -57,9 +56,6 @@ pub struct UiConfig { /// RGB values for colors. pub colors: Colors, - /// Should draw bold text with brighter colors instead of bold font. - pub draw_bold_text_with_bright_colors: bool, - /// Path where config was loaded from. #[config(skip)] pub config_paths: Vec<PathBuf>, @@ -75,37 +71,42 @@ pub struct UiConfig { #[config(flatten)] pub terminal_config: TerminalConfig, + /// Keyboard configuration. + keyboard: Keyboard, + + /// Should draw bold text with brighter colors instead of bold font. + #[config(deprecated = "use colors.draw_bold_text_with_bright_colors instead")] + draw_bold_text_with_bright_colors: bool, + /// Keybindings. + #[config(deprecated = "use keyboard.bindings instead")] key_bindings: KeyBindings, /// Bindings for the mouse. + #[config(deprecated = "use mouse.bindings instead")] mouse_bindings: MouseBindings, - - /// Background opacity from 0.0 to 1.0. - #[config(deprecated = "use window.opacity instead")] - background_opacity: Option<Percentage>, } impl Default for UiConfig { fn default() -> Self { Self { live_config_reload: true, - alt_send_esc: Default::default(), #[cfg(unix)] ipc_socket: true, - font: Default::default(), - window: Default::default(), - mouse: Default::default(), - debug: Default::default(), + draw_bold_text_with_bright_colors: Default::default(), + terminal_config: Default::default(), + mouse_bindings: Default::default(), config_paths: Default::default(), key_bindings: Default::default(), - mouse_bindings: Default::default(), - terminal_config: Default::default(), - background_opacity: Default::default(), - bell: Default::default(), + alt_send_esc: Default::default(), + keyboard: Default::default(), + window: Default::default(), colors: Default::default(), - draw_bold_text_with_bright_colors: Default::default(), + mouse: Default::default(), + debug: Default::default(), hints: Default::default(), + font: Default::default(), + bell: Default::default(), } } } @@ -113,6 +114,15 @@ impl Default for UiConfig { impl UiConfig { /// Generate key bindings for all keyboard hints. pub fn generate_hint_bindings(&mut self) { + // Check which key bindings is most likely to be the user's configuration. + // + // Both will be non-empty due to the presence of the default keybindings. + let key_bindings = if self.keyboard.bindings.0.len() >= self.key_bindings.0.len() { + &mut self.keyboard.bindings.0 + } else { + &mut self.key_bindings.0 + }; + for hint in &self.hints.enabled { let binding = match hint.binding { Some(binding) => binding, @@ -127,54 +137,56 @@ impl UiConfig { action: Action::Hint(hint.clone()), }; - self.key_bindings.0.push(binding); + key_bindings.push(binding); } } #[inline] pub fn window_opacity(&self) -> f32 { - self.background_opacity.unwrap_or(self.window.opacity).as_f32() + self.window.opacity.as_f32() } #[inline] pub fn key_bindings(&self) -> &[KeyBinding] { - self.key_bindings.0.as_slice() + if self.keyboard.bindings.0.len() >= self.key_bindings.0.len() { + self.keyboard.bindings.0.as_slice() + } else { + self.key_bindings.0.as_slice() + } } #[inline] pub fn mouse_bindings(&self) -> &[MouseBinding] { - self.mouse_bindings.0.as_slice() + if self.mouse.bindings.0.len() >= self.mouse_bindings.0.len() { + self.mouse.bindings.0.as_slice() + } else { + self.mouse_bindings.0.as_slice() + } } -} - -#[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)] -struct KeyBindings(Vec<KeyBinding>); -impl Default for KeyBindings { - fn default() -> Self { - Self(bindings::default_key_bindings()) + #[inline] + pub fn draw_bold_text_with_bright_colors(&self) -> bool { + self.colors.draw_bold_text_with_bright_colors || self.draw_bold_text_with_bright_colors } } -impl<'de> Deserialize<'de> for KeyBindings { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: Deserializer<'de>, - { - Ok(Self(deserialize_bindings(deserializer, Self::default().0)?)) - } +/// Keyboard configuration. +#[derive(ConfigDeserialize, Default, Clone, Debug, PartialEq)] +struct Keyboard { + /// Keybindings. + bindings: KeyBindings, } #[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)] -struct MouseBindings(Vec<MouseBinding>); +struct KeyBindings(Vec<KeyBinding>); -impl Default for MouseBindings { +impl Default for KeyBindings { fn default() -> Self { - Self(bindings::default_mouse_bindings()) + Self(bindings::default_key_bindings()) } } -impl<'de> Deserialize<'de> for MouseBindings { +impl<'de> Deserialize<'de> for KeyBindings { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, @@ -183,7 +195,7 @@ impl<'de> Deserialize<'de> for MouseBindings { } } -fn deserialize_bindings<'a, D, T>( +pub fn deserialize_bindings<'a, D, T>( deserializer: D, mut default: Vec<Binding<T>>, ) -> Result<Vec<Binding<T>>, D::Error> @@ -192,7 +204,7 @@ where T: Copy + Eq, Binding<T>: Deserialize<'a>, { - let values = Vec::<serde_yaml::Value>::deserialize(deserializer)?; + let values = Vec::<toml::Value>::deserialize(deserializer)?; // Skip all invalid values. let mut bindings = Vec::with_capacity(values.len()); @@ -388,7 +400,7 @@ impl<'de> Deserialize<'de> for HintContent { { let mut content = Self::Value::default(); - while let Some((key, value)) = map.next_entry::<String, serde_yaml::Value>()? { + while let Some((key, value)) = map.next_entry::<String, toml::Value>()? { match key.as_str() { "regex" => match Option::<LazyRegex>::deserialize(value) { Ok(regex) => content.regex = regex, @@ -408,7 +420,8 @@ impl<'de> Deserialize<'de> for HintContent { ); }, }, - _ => (), + "command" | "action" => (), + key => warn!(target: LOG_TARGET_CONFIG, "Unrecognized hint field: {key}"), } } diff --git a/alacritty/src/config/window.rs b/alacritty/src/config/window.rs index db29fd85..e4236b99 100644 --- a/alacritty/src/config/window.rs +++ b/alacritty/src/config/window.rs @@ -232,7 +232,7 @@ impl<'de> Deserialize<'de> for Class { { let mut class = Self::Value::default(); - while let Some((key, value)) = map.next_entry::<String, serde_yaml::Value>()? { + while let Some((key, value)) = map.next_entry::<String, toml::Value>()? { match key.as_str() { "instance" => match String::deserialize(value) { Ok(instance) => class.instance = instance, @@ -252,7 +252,7 @@ impl<'de> Deserialize<'de> for Class { ); }, }, - _ => (), + key => warn!(target: LOG_TARGET_CONFIG, "Unrecognized class field: {key}"), } } |