aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty/src')
-rw-r--r--alacritty/src/cli.rs116
-rw-r--r--alacritty/src/config/bindings.rs38
-rw-r--r--alacritty/src/config/color.rs7
-rw-r--r--alacritty/src/config/mod.rs202
-rw-r--r--alacritty/src/config/mouse.rs28
-rw-r--r--alacritty/src/config/serde_utils.rs66
-rw-r--r--alacritty/src/config/ui_config.rs109
-rw-r--r--alacritty/src/config/window.rs4
-rw-r--r--alacritty/src/display/content.rs4
-rw-r--r--alacritty/src/main.rs15
-rw-r--r--alacritty/src/migrate.rs249
-rw-r--r--alacritty/src/window_context.rs6
12 files changed, 593 insertions, 251 deletions
diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs
index 7f39e527..7eb281d9 100644
--- a/alacritty/src/cli.rs
+++ b/alacritty/src/cli.rs
@@ -2,12 +2,10 @@ use std::cmp::max;
use std::os::raw::c_ulong;
use std::path::PathBuf;
-#[cfg(unix)]
-use clap::Subcommand;
-use clap::{ArgAction, Args, Parser, ValueHint};
+use clap::{ArgAction, Args, Parser, Subcommand, ValueHint};
use log::{self, error, LevelFilter};
use serde::{Deserialize, Serialize};
-use serde_yaml::Value;
+use toml::{Table, Value};
use alacritty_terminal::config::{Program, PtyConfig};
@@ -30,17 +28,18 @@ pub struct Options {
#[clap(long)]
pub embed: Option<String>,
- /// Specify alternative configuration file [default: $XDG_CONFIG_HOME/alacritty/alacritty.yml].
+ /// Specify alternative configuration file [default:
+ /// $XDG_CONFIG_HOME/alacritty/alacritty.toml].
#[cfg(not(any(target_os = "macos", windows)))]
#[clap(long, value_hint = ValueHint::FilePath)]
pub config_file: Option<PathBuf>,
- /// Specify alternative configuration file [default: %APPDATA%\alacritty\alacritty.yml].
+ /// Specify alternative configuration file [default: %APPDATA%\alacritty\alacritty.toml].
#[cfg(windows)]
#[clap(long, value_hint = ValueHint::FilePath)]
pub config_file: Option<PathBuf>,
- /// Specify alternative configuration file [default: $HOME/.config/alacritty/alacritty.yml].
+ /// Specify alternative configuration file [default: $HOME/.config/alacritty/alacritty.toml].
#[cfg(target_os = "macos")]
#[clap(long, value_hint = ValueHint::FilePath)]
pub config_file: Option<PathBuf>,
@@ -64,14 +63,13 @@ pub struct Options {
/// CLI options for config overrides.
#[clap(skip)]
- pub config_options: Value,
+ pub config_options: TomlValue,
/// Options which can be passed via IPC.
#[clap(flatten)]
pub window_options: WindowOptions,
/// Subcommand passed to the CLI.
- #[cfg(unix)]
#[clap(subcommand)]
pub subcommands: Option<Subcommands>,
}
@@ -81,7 +79,7 @@ impl Options {
let mut options = Self::parse();
// Convert `--option` flags into serde `Value`.
- options.config_options = options_as_value(&options.option);
+ options.config_options = TomlValue(options_as_value(&options.option));
options
}
@@ -125,9 +123,9 @@ impl Options {
}
}
-/// Combine multiple options into a [`serde_yaml::Value`].
+/// Combine multiple options into a [`toml::Value`].
pub fn options_as_value(options: &[String]) -> Value {
- options.iter().fold(Value::default(), |value, option| match option_as_value(option) {
+ 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);
@@ -136,31 +134,6 @@ pub fn options_as_value(options: &[String]) -> Value {
})
}
-/// Parse an option in the format of `parent.field=value` as 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)
-}
-
/// Parse the class CLI parameter.
fn parse_class(input: &str) -> Result<Class, String> {
let (general, instance) = match input.split_once(',') {
@@ -259,10 +232,11 @@ impl WindowIdentity {
}
/// Available CLI subcommands.
-#[cfg(unix)]
#[derive(Subcommand, Debug)]
pub enum Subcommands {
+ #[cfg(unix)]
Msg(MessageOptions),
+ Migrate(MigrateOptions),
}
/// Send a message to the Alacritty socket.
@@ -289,6 +263,30 @@ pub enum SocketMessage {
Config(IpcConfig),
}
+/// Migrate the configuration file.
+#[derive(Args, Clone, Debug)]
+pub struct MigrateOptions {
+ /// Path to the configuration file.
+ #[clap(short, long, value_hint = ValueHint::FilePath)]
+ pub config_file: Option<PathBuf>,
+
+ /// Only output TOML config to stdout.
+ #[clap(short, long)]
+ pub dry_run: bool,
+
+ /// Do not recurse over imports.
+ #[clap(short = 'i', long)]
+ pub skip_imports: bool,
+
+ /// Do not move renamed fields to their new location.
+ #[clap(long)]
+ pub skip_renames: bool,
+
+ #[clap(short, long)]
+ /// Do not output to STDOUT.
+ pub silent: bool,
+}
+
/// Subset of options that we pass to 'create-window' IPC subcommand.
#[derive(Serialize, Deserialize, Args, Default, Clone, Debug, PartialEq, Eq)]
pub struct WindowOptions {
@@ -320,6 +318,16 @@ 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::*;
@@ -333,7 +341,7 @@ mod tests {
use clap::CommandFactory;
#[cfg(target_os = "linux")]
use clap_complete::Shell;
- use serde_yaml::mapping::Mapping;
+ use toml::Table;
#[test]
fn dynamic_title_ignoring_options_by_default() {
@@ -371,38 +379,38 @@ mod tests {
#[test]
fn valid_option_as_value() {
// Test with a single field.
- let value = option_as_value("field=true").unwrap();
+ let value: Value = toml::from_str("field=true").unwrap();
- let mut mapping = Mapping::new();
- mapping.insert(Value::String(String::from("field")), Value::Bool(true));
+ let mut table = Table::new();
+ table.insert(String::from("field"), Value::Boolean(true));
- assert_eq!(value, Value::Mapping(mapping));
+ assert_eq!(value, Value::Table(table));
// Test with nested fields
- let value = option_as_value("parent.field=true").unwrap();
+ let value: Value = toml::from_str("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));
+ let mut parent_table = Table::new();
+ parent_table.insert(String::from("field"), Value::Boolean(true));
+ let mut table = Table::new();
+ table.insert(String::from("parent"), Value::Table(parent_table));
- assert_eq!(value, Value::Mapping(mapping));
+ assert_eq!(value, Value::Table(table));
}
#[test]
fn invalid_option_as_value() {
- let value = option_as_value("}");
+ let value = toml::from_str::<Value>("}");
assert!(value.is_err());
}
#[test]
fn float_option_as_value() {
- let value = option_as_value("float=3.4").unwrap();
+ let value: Value = toml::from_str("float=3.4").unwrap();
- let mut expected = Mapping::new();
- expected.insert(Value::String(String::from("float")), Value::Number(3.4.into()));
+ let mut expected = Table::new();
+ expected.insert(String::from("float"), Value::Float(3.4));
- assert_eq!(value, Value::Mapping(expected));
+ assert_eq!(value, Value::Table(expected));
}
#[test]
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}"),
}
}
diff --git a/alacritty/src/display/content.rs b/alacritty/src/display/content.rs
index da211094..72f71e9b 100644
--- a/alacritty/src/display/content.rs
+++ b/alacritty/src/display/content.rs
@@ -314,7 +314,7 @@ impl RenderableCell {
_ => rgb.into(),
},
Color::Named(ansi) => {
- match (config.draw_bold_text_with_bright_colors, flags & Flags::DIM_BOLD) {
+ match (config.draw_bold_text_with_bright_colors(), flags & Flags::DIM_BOLD) {
// If no bright foreground is set, treat it like the BOLD flag doesn't exist.
(_, Flags::DIM_BOLD)
if ansi == NamedColor::Foreground
@@ -334,7 +334,7 @@ impl RenderableCell {
},
Color::Indexed(idx) => {
let idx = match (
- config.draw_bold_text_with_bright_colors,
+ config.draw_bold_text_with_bright_colors(),
flags & Flags::DIM_BOLD,
idx,
) {
diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs
index 29120b35..55f81502 100644
--- a/alacritty/src/main.rs
+++ b/alacritty/src/main.rs
@@ -42,6 +42,7 @@ mod logging;
#[cfg(target_os = "macos")]
mod macos;
mod message_bar;
+mod migrate;
#[cfg(windows)]
mod panic;
mod renderer;
@@ -54,9 +55,9 @@ mod gl {
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}
-use crate::cli::Options;
#[cfg(unix)]
-use crate::cli::{MessageOptions, Subcommands};
+use crate::cli::MessageOptions;
+use crate::cli::{Options, Subcommands};
use crate::config::{monitor, UiConfig};
use crate::event::{Event, Processor};
#[cfg(target_os = "macos")]
@@ -77,14 +78,14 @@ fn main() -> Result<(), Box<dyn Error>> {
// Load command line options.
let options = Options::new();
- #[cfg(unix)]
match options.subcommands {
- Some(Subcommands::Msg(options)) => msg(options),
- None => alacritty(options),
+ #[cfg(unix)]
+ Some(Subcommands::Msg(options)) => msg(options)?,
+ Some(Subcommands::Migrate(options)) => migrate::migrate(options),
+ None => alacritty(options)?,
}
- #[cfg(not(unix))]
- alacritty(options)
+ Ok(())
}
/// `msg` subcommand entrypoint.
diff --git a/alacritty/src/migrate.rs b/alacritty/src/migrate.rs
new file mode 100644
index 00000000..07a10ba4
--- /dev/null
+++ b/alacritty/src/migrate.rs
@@ -0,0 +1,249 @@
+//! Configuration file migration.
+
+use std::fs;
+use std::path::Path;
+
+use toml::map::Entry;
+use toml::{Table, Value};
+
+use crate::cli::MigrateOptions;
+use crate::config;
+
+/// Handle migration.
+pub fn migrate(options: MigrateOptions) {
+ // Find configuration file path.
+ let config_path = options
+ .config_file
+ .clone()
+ .or_else(|| config::installed_config("toml"))
+ .or_else(|| config::installed_config("yml"));
+
+ // Abort if system has no installed configuration.
+ let config_path = match config_path {
+ Some(config_path) => config_path,
+ None => {
+ eprintln!("No configuration file found");
+ std::process::exit(1);
+ },
+ };
+
+ // If we're doing a wet run, perform a dry run first for safety.
+ if !options.dry_run {
+ let mut options = options.clone();
+ options.silent = true;
+ options.dry_run = true;
+ if let Err(err) = migrate_config(&options, &config_path, config::IMPORT_RECURSION_LIMIT) {
+ eprintln!("Configuration file migration failed:");
+ eprintln!(" {config_path:?}: {err}");
+ std::process::exit(1);
+ }
+ }
+
+ // Migrate the root config.
+ match migrate_config(&options, &config_path, config::IMPORT_RECURSION_LIMIT) {
+ Ok(new_path) => {
+ if !options.silent {
+ println!("Successfully migrated {config_path:?} to {new_path:?}");
+ }
+ },
+ Err(err) => {
+ eprintln!("Configuration file migration failed:");
+ eprintln!(" {config_path:?}: {err}");
+ std::process::exit(1);
+ },
+ }
+}
+
+/// Migrate a specific configuration file.
+fn migrate_config(
+ options: &MigrateOptions,
+ path: &Path,
+ recursion_limit: usize,
+) -> Result<String, String> {
+ // Ensure configuration file has an extension.
+ let path_str = path.to_string_lossy();
+ let (prefix, suffix) = match path_str.rsplit_once('.') {
+ Some((prefix, suffix)) => (prefix, suffix),
+ None => return Err("missing file extension".to_string()),
+ };
+
+ // Abort if config is already toml.
+ if suffix == "toml" {
+ return Err("already in TOML format".to_string());
+ }
+
+ // Try to parse the configuration file.
+ let mut config = match config::deserialize_config(path) {
+ Ok(config) => config,
+ Err(err) => return Err(format!("parsing error: {err}")),
+ };
+
+ // Migrate config imports.
+ if !options.skip_imports {
+ migrate_imports(options, &mut config, recursion_limit)?;
+ }
+
+ // Migrate deprecated field names to their new location.
+ if !options.skip_renames {
+ migrate_renames(&mut config)?;
+ }
+
+ // Convert to TOML format.
+ let toml = toml::to_string(&config).map_err(|err| format!("conversion error: {err}"))?;
+ let new_path = format!("{prefix}.toml");
+
+ if options.dry_run && !options.silent {
+ // Output new content to STDOUT.
+ println!(
+ "\nv-----Start TOML for {path:?}-----v\n\n{toml}\n^-----End TOML for {path:?}-----^"
+ );
+ } else if !options.dry_run {
+ // Write the new toml configuration.
+ fs::write(&new_path, toml).map_err(|err| format!("filesystem error: {err}"))?;
+ }
+
+ Ok(new_path)
+}
+
+/// Migrate the imports of a config.
+fn migrate_imports(
+ options: &MigrateOptions,
+ config: &mut Value,
+ recursion_limit: usize,
+) -> Result<(), String> {
+ let imports = match config::imports(config, recursion_limit) {
+ Ok(imports) => imports,
+ Err(err) => return Err(format!("import error: {err}")),
+ };
+
+ // Migrate the individual imports.
+ let mut new_imports = Vec::new();
+ for import in imports {
+ let import = match import {
+ Ok(import) => import,
+ Err(err) => return Err(format!("import error: {err}")),
+ };
+ let new_path = migrate_config(options, &import, recursion_limit - 1)?;
+ new_imports.push(Value::String(new_path));
+ }
+
+ // Update the imports field.
+ if let Some(import) = config.get_mut("import") {
+ *import = Value::Array(new_imports);
+ }
+
+ Ok(())
+}
+
+/// Migrate deprecated fields.
+fn migrate_renames(config: &mut Value) -> Result<(), String> {
+ let config_table = match config.as_table_mut() {
+ Some(config_table) => config_table,
+ None => return Ok(()),
+ };
+
+ // draw_bold_text_with_bright_colors -> colors.draw_bold_text_with_bright_colors
+ move_value(config_table, &["draw_bold_text_with_bright_colors"], &[
+ "colors",
+ "draw_bold_text_with_bright_colors",
+ ])?;
+
+ // key_bindings -> keyboard.bindings
+ move_value(config_table, &["key_bindings"], &["keyboard", "bindings"])?;
+
+ // mouse_bindings -> keyboard.bindings
+ move_value(config_table, &["mouse_bindings"], &["mouse", "bindings"])?;
+
+ Ok(())
+}
+
+/// Move a toml value from one map to another.
+fn move_value(config_table: &mut Table, origin: &[&str], target: &[&str]) -> Result<(), String> {
+ if let Some(value) = remove_node(config_table, origin)? {
+ if !insert_node_if_empty(config_table, target, value)? {
+ return Err(format!(
+ "conflict: both `{}` and `{}` are set",
+ origin.join("."),
+ target.join(".")
+ ));
+ }
+ }
+
+ Ok(())
+}
+
+/// Remove a node from a tree of tables.
+fn remove_node(table: &mut Table, path: &[&str]) -> Result<Option<Value>, String> {
+ if path.len() == 1 {
+ Ok(table.remove(path[0]))
+ } else {
+ let next_table_value = match table.get_mut(path[0]) {
+ Some(next_table_value) => next_table_value,
+ None => return Ok(None),
+ };
+
+ let next_table = match next_table_value.as_table_mut() {
+ Some(next_table) => next_table,
+ None => return Err(format!("invalid `{}` table", path[0])),
+ };
+
+ remove_node(next_table, &path[1..])
+ }
+}
+
+/// Try to insert a node into a tree of tables.
+///
+/// Returns `false` if the node already exists.
+fn insert_node_if_empty(table: &mut Table, path: &[&str], node: Value) -> Result<bool, String> {
+ if path.len() == 1 {
+ match table.entry(path[0]) {
+ Entry::Vacant(vacant_entry) => {
+ vacant_entry.insert(node);
+ Ok(true)
+ },
+ Entry::Occupied(_) => Ok(false),
+ }
+ } else {
+ let next_table_value = table.entry(path[0]).or_insert_with(|| Value::Table(Table::new()));
+
+ let next_table = match next_table_value.as_table_mut() {
+ Some(next_table) => next_table,
+ None => return Err(format!("invalid `{}` table", path[0])),
+ };
+
+ insert_node_if_empty(next_table, &path[1..], node)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn move_values() {
+ let input = r#"
+root_value = 3
+
+[table]
+table_value = 5
+
+[preexisting]
+not_moved = 9
+ "#;
+
+ let mut value: Value = toml::from_str(input).unwrap();
+ let table = value.as_table_mut().unwrap();
+
+ move_value(table, &["root_value"], &["new_table", "root_value"]).unwrap();
+ move_value(table, &["table", "table_value"], &["preexisting", "subtable", "new_name"])
+ .unwrap();
+
+ let output = toml::to_string(table).unwrap();
+
+ assert_eq!(
+ output,
+ "[new_table]\nroot_value = 3\n\n[preexisting]\nnot_moved = \
+ 9\n\n[preexisting.subtable]\nnew_name = 5\n\n[table]\n"
+ );
+ }
+}
diff --git a/alacritty/src/window_context.rs b/alacritty/src/window_context.rs
index 885d71a4..a450c77d 100644
--- a/alacritty/src/window_context.rs
+++ b/alacritty/src/window_context.rs
@@ -70,7 +70,7 @@ pub struct WindowContext {
master_fd: RawFd,
#[cfg(not(windows))]
shell_pid: u32,
- ipc_config: Vec<(String, serde_yaml::Value)>,
+ ipc_config: Vec<(String, toml::Value)>,
config: Rc<UiConfig>,
}
@@ -379,8 +379,8 @@ impl WindowContext {
},
};
- // Try and parse value as yaml.
- match serde_yaml::from_str(value) {
+ // Try and parse value as toml.
+ match toml::from_str(value) {
Ok(value) => self.ipc_config.push((key.to_owned(), value)),
Err(err) => error!(
target: LOG_TARGET_IPC_CONFIG,