summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--Cargo.lock23
-rw-r--r--Cargo.toml1
-rw-r--r--alacritty.yml2
-rw-r--r--alacritty/Cargo.toml6
-rw-r--r--alacritty/src/cli.rs30
-rw-r--r--alacritty/src/config/bindings.rs18
-rw-r--r--alacritty/src/config/debug.rs41
-rw-r--r--alacritty/src/config/font.rs153
-rw-r--r--alacritty/src/config/mod.rs56
-rw-r--r--alacritty/src/config/mouse.rs71
-rw-r--r--alacritty/src/config/ui_config.rs129
-rw-r--r--alacritty/src/config/window.rs198
-rw-r--r--alacritty/src/cursor.rs4
-rw-r--r--alacritty/src/display.rs16
-rw-r--r--alacritty/src/event.rs21
-rw-r--r--alacritty/src/input.rs41
-rw-r--r--alacritty/src/logging.rs2
-rw-r--r--alacritty/src/main.rs2
-rw-r--r--alacritty/src/renderer/mod.rs24
-rw-r--r--alacritty/src/wayland_theme.rs6
-rw-r--r--alacritty/src/window.rs9
-rw-r--r--alacritty_config_derive/Cargo.toml21
l---------alacritty_config_derive/LICENSE-APACHE1
-rw-r--r--alacritty_config_derive/LICENSE-MIT23
-rw-r--r--alacritty_config_derive/src/de_enum.rs66
-rw-r--r--alacritty_config_derive/src/de_struct.rs226
-rw-r--r--alacritty_config_derive/src/lib.rs27
-rw-r--r--alacritty_config_derive/tests/config.rs155
-rw-r--r--alacritty_terminal/Cargo.toml4
-rw-r--r--alacritty_terminal/src/config/bell.rs72
-rw-r--r--alacritty_terminal/src/config/colors.rs237
-rw-r--r--alacritty_terminal/src/config/mod.rs224
-rw-r--r--alacritty_terminal/src/config/scrolling.rs78
-rw-r--r--alacritty_terminal/src/event_loop.rs2
-rw-r--r--alacritty_terminal/src/selection.rs14
-rw-r--r--alacritty_terminal/src/term/color.rs52
-rw-r--r--alacritty_terminal/src/term/mod.rs27
38 files changed, 1037 insertions, 1048 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1c005e62..ae6deaea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed
- Nonexistent config imports are ignored instead of raising an error
+- Value for disabling logging with `config.log_level` is `Off` instead of `None`
### Fixed
@@ -51,6 +52,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* `--dimensions`
* `--position`
- `live-shader-reload` feature
+- Config option `dynamic_title`, you should use `window.dynamic_title` instead
+- Config option `scrolling.faux_multiplier`, which was replaced by escape `CSI ? 1007 h/l`
## 0.6.0
diff --git a/Cargo.lock b/Cargo.lock
index 68d0782b..f3aa5acd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -25,6 +25,7 @@ dependencies = [
name = "alacritty"
version = "0.7.0-dev"
dependencies = [
+ "alacritty_config_derive",
"alacritty_terminal",
"bitflags",
"clap",
@@ -54,9 +55,22 @@ dependencies = [
]
[[package]]
+name = "alacritty_config_derive"
+version = "0.1.0"
+dependencies = [
+ "log",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_yaml",
+ "syn",
+]
+
+[[package]]
name = "alacritty_terminal"
version = "0.11.1-dev"
dependencies = [
+ "alacritty_config_derive",
"base64",
"bitflags",
"libc",
@@ -1077,6 +1091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
+ "serde",
]
[[package]]
@@ -1794,9 +1809,9 @@ dependencies = [
[[package]]
name = "serde_yaml"
-version = "0.8.13"
+version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5"
+checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7"
dependencies = [
"dtoa",
"linked-hash-map",
@@ -1942,9 +1957,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "syn"
-version = "1.0.46"
+version = "1.0.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ad5de3220ea04da322618ded2c42233d02baca219d6f160a3e9c87cda16c942"
+checksum = "8833e20724c24de12bbaba5ad230ea61c3eafb05b881c7c9d3cfe8638b187e68"
dependencies = [
"proc-macro2",
"quote",
diff --git a/Cargo.toml b/Cargo.toml
index 0198afd7..7a6dec80 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,6 +2,7 @@
members = [
"alacritty",
"alacritty_terminal",
+ "alacritty_config_derive",
]
[profile.release]
diff --git a/alacritty.yml b/alacritty.yml
index 72fbcf8c..89a1c65f 100644
--- a/alacritty.yml
+++ b/alacritty.yml
@@ -799,7 +799,7 @@
# Log level
#
# Values for `log_level`:
- # - None
+ # - Off
# - Error
# - Warn
# - Info
diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml
index b5ebefe2..6e833f34 100644
--- a/alacritty/Cargo.toml
+++ b/alacritty/Cargo.toml
@@ -13,9 +13,13 @@ path = "../alacritty_terminal"
version = "0.11.1-dev"
default-features = false
+[dependencies.alacritty_config_derive]
+path = "../alacritty_config_derive"
+version = "0.1.0"
+
[dependencies]
clap = "2"
-log = { version = "0.4", features = ["std"] }
+log = { version = "0.4", features = ["std", "serde"] }
time = "0.1.40"
fnv = "1"
serde = { version = "1", features = ["derive"] }
diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs
index cacffcdc..6dea3319 100644
--- a/alacritty/src/cli.rs
+++ b/alacritty/src/cli.rs
@@ -230,13 +230,17 @@ impl Options {
config.hold = self.hold;
- let dynamic_title = config.ui_config.dynamic_title() && self.title.is_none();
- config.ui_config.set_dynamic_title(dynamic_title);
-
- replace_if_some(&mut config.ui_config.window.title, self.title.clone());
- replace_if_some(&mut config.ui_config.window.class.instance, self.class_instance.clone());
- replace_if_some(&mut config.ui_config.window.class.general, self.class_general.clone());
+ if let Some(title) = self.title.clone() {
+ config.ui_config.window.title = title
+ }
+ if let Some(class_instance) = self.class_instance.clone() {
+ config.ui_config.window.class.instance = class_instance;
+ }
+ if let Some(class_general) = self.class_general.clone() {
+ config.ui_config.window.class.general = class_general;
+ }
+ config.ui_config.window.dynamic_title &= self.title.is_none();
config.ui_config.window.embed = self.embed.as_ref().and_then(|embed| embed.parse().ok());
config.ui_config.debug.print_events |= self.print_events;
config.ui_config.debug.log_level = max(config.ui_config.debug.log_level, self.log_level);
@@ -249,12 +253,6 @@ impl Options {
}
}
-fn replace_if_some<T>(option: &mut T, value: Option<T>) {
- if let Some(value) = value {
- *option = value;
- }
-}
-
/// Format an option in the format of `parent.field=value` to a serde Value.
fn option_as_value(option: &str) -> Result<Value, serde_yaml::Error> {
let mut yaml_text = String::with_capacity(option.len());
@@ -289,11 +287,11 @@ mod tests {
#[test]
fn dynamic_title_ignoring_options_by_default() {
let mut config = Config::default();
- let old_dynamic_title = config.ui_config.dynamic_title();
+ let old_dynamic_title = config.ui_config.window.dynamic_title;
Options::default().override_config(&mut config);
- assert_eq!(old_dynamic_title, config.ui_config.dynamic_title());
+ assert_eq!(old_dynamic_title, config.ui_config.window.dynamic_title);
}
#[test]
@@ -304,7 +302,7 @@ mod tests {
options.title = Some("foo".to_owned());
options.override_config(&mut config);
- assert!(!config.ui_config.dynamic_title());
+ assert!(!config.ui_config.window.dynamic_title);
}
#[test]
@@ -314,7 +312,7 @@ mod tests {
config.ui_config.window.title = "foo".to_owned();
Options::default().override_config(&mut config);
- assert!(config.ui_config.dynamic_title());
+ assert!(config.ui_config.window.dynamic_title);
}
#[test]
diff --git a/alacritty/src/config/bindings.rs b/alacritty/src/config/bindings.rs
index 80900733..9babd7f0 100644
--- a/alacritty/src/config/bindings.rs
+++ b/alacritty/src/config/bindings.rs
@@ -10,6 +10,8 @@ use serde::de::{self, MapAccess, Unexpected, Visitor};
use serde::{Deserialize, Deserializer};
use serde_yaml::Value as SerdeValue;
+use alacritty_config_derive::ConfigDeserialize;
+
use alacritty_terminal::config::Program;
use alacritty_terminal::term::TermMode;
use alacritty_terminal::vi_mode::ViMotion;
@@ -79,26 +81,26 @@ impl<T: Eq> Binding<T> {
}
}
-#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
+#[derive(ConfigDeserialize, Debug, Clone, PartialEq, Eq)]
pub enum Action {
/// Write an escape sequence.
- #[serde(skip)]
+ #[config(skip)]
Esc(String),
/// Run given command.
- #[serde(skip)]
+ #[config(skip)]
Command(Program),
/// Move vi mode cursor.
- #[serde(skip)]
+ #[config(skip)]
ViMotion(ViMotion),
/// Perform vi mode action.
- #[serde(skip)]
+ #[config(skip)]
ViAction(ViAction),
/// Perform search mode action.
- #[serde(skip)]
+ #[config(skip)]
SearchAction(SearchAction),
/// Paste contents of system clipboard.
@@ -227,7 +229,7 @@ impl Display for Action {
}
/// Vi mode specific actions.
-#[derive(Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub enum ViAction {
/// Toggle normal vi selection.
ToggleNormalSelection,
@@ -912,7 +914,7 @@ impl<'a> Deserialize<'a> for RawBinding {
where
E: de::Error,
{
- match value {
+ match value.to_ascii_lowercase().as_str() {
"key" => Ok(Field::Key),
"mods" => Ok(Field::Mods),
"mode" => Ok(Field::Mode),
diff --git a/alacritty/src/config/debug.rs b/alacritty/src/config/debug.rs
index 62de0500..f52cdf90 100644
--- a/alacritty/src/config/debug.rs
+++ b/alacritty/src/config/debug.rs
@@ -1,35 +1,29 @@
-use log::{error, LevelFilter};
-use serde::{Deserialize, Deserializer};
+use log::LevelFilter;
-use alacritty_terminal::config::{failure_default, LOG_TARGET_CONFIG};
+use alacritty_config_derive::ConfigDeserialize;
/// Debugging options.
-#[serde(default)]
-#[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(ConfigDeserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Debug {
- #[serde(default = "default_log_level", deserialize_with = "deserialize_log_level")]
pub log_level: LevelFilter,
- #[serde(deserialize_with = "failure_default")]
pub print_events: bool,
/// Keep the log file after quitting.
- #[serde(deserialize_with = "failure_default")]
pub persistent_logging: bool,
/// Should show render timer.
- #[serde(deserialize_with = "failure_default")]
pub render_timer: bool,
/// Record ref test.
- #[serde(skip)]
+ #[config(skip)]
pub ref_test: bool,
}
impl Default for Debug {
fn default() -> Self {
Self {
- log_level: default_log_level(),
+ log_level: LevelFilter::Warn,
print_events: Default::default(),
persistent_logging: Default::default(),
render_timer: Default::default(),
@@ -37,28 +31,3 @@ impl Default for Debug {
}
}
}
-
-fn default_log_level() -> LevelFilter {
- LevelFilter::Warn
-}
-
-fn deserialize_log_level<'a, D>(deserializer: D) -> Result<LevelFilter, D::Error>
-where
- D: Deserializer<'a>,
-{
- Ok(match failure_default::<D, String>(deserializer)?.to_lowercase().as_str() {
- "off" | "none" => LevelFilter::Off,
- "error" => LevelFilter::Error,
- "warn" => LevelFilter::Warn,
- "info" => LevelFilter::Info,
- "debug" => LevelFilter::Debug,
- "trace" => LevelFilter::Trace,
- level => {
- error!(
- target: LOG_TARGET_CONFIG,
- "Problem with config: invalid log level {}; using level Warn", level
- );
- default_log_level()
- },
- })
-}
diff --git a/alacritty/src/config/font.rs b/alacritty/src/config/font.rs
index 9982352f..3dc11671 100644
--- a/alacritty/src/config/font.rs
+++ b/alacritty/src/config/font.rs
@@ -1,14 +1,11 @@
use std::fmt;
-use crossfont::Size;
-use log::error;
-use serde::de::Visitor;
+use crossfont::Size as FontSize;
+use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer};
-use alacritty_terminal::config::{failure_default, LOG_TARGET_CONFIG};
+use alacritty_config_derive::ConfigDeserialize;
-#[cfg(target_os = "macos")]
-use crate::config::ui_config::DefaultTrueBool;
use crate::config::ui_config::Delta;
/// Font config.
@@ -17,62 +14,41 @@ use crate::config::ui_config::Delta;
/// field in this struct. It might be nice in the future to have defaults for
/// each value independently. Alternatively, maybe erroring when the user
/// doesn't provide complete config is Ok.
-#[serde(default)]
-#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Default, Debug, Clone, PartialEq, Eq)]
pub struct Font {
+ /// Extra spacing per character.
+ pub offset: Delta<i8>,
+
+ /// Glyph offset within character cell.
+ pub glyph_offset: Delta<i8>,
+
+ pub use_thin_strokes: bool,
+
/// Normal font face.
- #[serde(deserialize_with = "failure_default")]
normal: FontDescription,
/// Bold font face.
- #[serde(deserialize_with = "failure_default")]
bold: SecondaryFontDescription,
/// Italic font face.
- #[serde(deserialize_with = "failure_default")]
italic: SecondaryFontDescription,
/// Bold italic font face.
- #[serde(deserialize_with = "failure_default")]
bold_italic: SecondaryFontDescription,
/// Font size in points.
- #[serde(deserialize_with = "DeserializeSize::deserialize")]
- pub size: Size,
-
- /// Extra spacing per character.
- #[serde(deserialize_with = "failure_default")]
- pub offset: Delta<i8>,
-
- /// Glyph offset within character cell.
- #[serde(deserialize_with = "failure_default")]
- pub glyph_offset: Delta<i8>,
-
- #[cfg(target_os = "macos")]
- #[serde(deserialize_with = "failure_default")]
- use_thin_strokes: DefaultTrueBool,
-}
-
-impl Default for Font {
- fn default() -> Font {
- Font {
- size: default_font_size(),
- normal: Default::default(),
- bold: Default::default(),
- italic: Default::default(),
- bold_italic: Default::default(),
- glyph_offset: Default::default(),
- offset: Default::default(),
- #[cfg(target_os = "macos")]
- use_thin_strokes: Default::default(),
- }
- }
+ size: Size,
}
impl Font {
/// Get a font clone with a size modification.
- pub fn with_size(self, size: Size) -> Font {
- Font { size, ..self }
+ pub fn with_size(self, size: FontSize) -> Font {
+ Font { size: Size(size), ..self }
+ }
+
+ #[inline]
+ pub fn size(&self) -> FontSize {
+ self.size.0
}
/// Get normal font description.
@@ -94,29 +70,12 @@ impl Font {
pub fn bold_italic(&self) -> FontDescription {
self.bold_italic.desc(&self.normal)
}
-
- #[cfg(target_os = "macos")]
- pub fn use_thin_strokes(&self) -> bool {
- self.use_thin_strokes.0
- }
-
- #[cfg(not(target_os = "macos"))]
- pub fn use_thin_strokes(&self) -> bool {
- false
- }
-}
-
-fn default_font_size() -> Size {
- Size::new(11.)
}
/// Description of the normal font.
-#[serde(default)]
-#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Debug, Clone, PartialEq, Eq)]
pub struct FontDescription {
- #[serde(deserialize_with = "failure_default")]
pub family: String,
- #[serde(deserialize_with = "failure_default")]
pub style: Option<String>,
}
@@ -135,12 +94,9 @@ impl Default for FontDescription {
}
/// Description of the italic and bold font.
-#[serde(default)]
-#[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Debug, Default, Clone, PartialEq, Eq)]
pub struct SecondaryFontDescription {
- #[serde(deserialize_with = "failure_default")]
family: Option<String>,
- #[serde(deserialize_with = "failure_default")]
style: Option<String>,
}
@@ -153,66 +109,37 @@ impl SecondaryFontDescription {
}
}
-trait DeserializeSize: Sized {
- fn deserialize<'a, D>(_: D) -> ::std::result::Result<Self, D::Error>
- where
- D: serde::de::Deserializer<'a>;
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct Size(FontSize);
+
+impl Default for Size {
+ fn default() -> Self {
+ Self(FontSize::new(11.))
+ }
}
-impl DeserializeSize for Size {
- fn deserialize<'a, D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
+impl<'de> Deserialize<'de> for Size {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
- D: serde::de::Deserializer<'a>,
+ D: Deserializer<'de>,
{
- use std::marker::PhantomData;
-
- struct NumVisitor<__D> {
- _marker: PhantomData<__D>,
- }
-
- impl<'a, __D> Visitor<'a> for NumVisitor<__D>
- where
- __D: serde::de::Deserializer<'a>,
- {
- type Value = f64;
+ struct NumVisitor;
+ impl<'v> Visitor<'v> for NumVisitor {
+ type Value = Size;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("f64 or u64")
}
- fn visit_f64<E>(self, value: f64) -> ::std::result::Result<Self::Value, E>
- where
- E: ::serde::de::Error,
- {
- Ok(value)
+ fn visit_f64<E: de::Error>(self, value: f64) -> Result<Self::Value, E> {
+ Ok(Size(FontSize::new(value as f32)))
}
- fn visit_u64<E>(self, value: u64) -> ::std::result::Result<Self::Value, E>
- where
- E: ::serde::de::Error,
- {
- Ok(value as f64)
+ fn visit_u64<E: de::Error>(self, value: u64) -> Result<Self::Value, E> {
+ Ok(Size(FontSize::new(value as f32)))
}
}
- let value = serde_yaml::Value::deserialize(deserializer)?;
- let size = value
- .deserialize_any(NumVisitor::<D> { _marker: PhantomData })
- .map(|v| Size::new(v as _));
-
- // Use default font size as fallback.
- match size {
- Ok(size) => Ok(size),
- Err(err) => {
- let size = default_font_size();
- error!(
- target: LOG_TARGET_CONFIG,
- "Problem with config: {}; using size {}",
- err,
- size.as_f32_pts()
- );
- Ok(size)
- },
- }
+ deserializer.deserialize_any(NumVisitor)
}
}
diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs
index 19888add..0673ffd5 100644
--- a/alacritty/src/config/mod.rs
+++ b/alacritty/src/config/mod.rs
@@ -2,7 +2,7 @@ use std::fmt::{self, Display, Formatter};
use std::path::PathBuf;
use std::{env, fs, io};
-use log::{error, info, warn};
+use log::{error, info};
use serde::Deserialize;
use serde_yaml::mapping::Mapping;
use serde_yaml::Value;
@@ -68,7 +68,7 @@ impl Display for Error {
write!(f, "Unable to read $HOME environment variable: {}", err)
},
Error::Io(err) => write!(f, "Error reading config file: {}", err),
- Error::Yaml(err) => write!(f, "Problem with config: {}", err),
+ Error::Yaml(err) => write!(f, "Config error: {}", err),
}
}
}
@@ -157,8 +157,6 @@ fn read_config(path: &PathBuf, cli_config: Value) -> Result<Config> {
let mut config = Config::deserialize(config_value)?;
config.ui_config.config_paths = config_paths;
- print_deprecation_warnings(&config);
-
Ok(config)
}
@@ -287,56 +285,6 @@ fn installed_config() -> Option<PathBuf> {
dirs::config_dir().map(|path| path.join("alacritty\\alacritty.yml")).filter(|new| new.exists())
}
-fn print_deprecation_warnings(config: &Config) {
- if config.scrolling.faux_multiplier().is_some() {
- warn!(
- target: LOG_TARGET_CONFIG,
- "Config scrolling.faux_multiplier is deprecated; the alternate scroll escape can now \
- be used to disable it and `scrolling.multiplier` controls the number of scrolled \
- lines"
- );
- }
-
- if config.scrolling.auto_scroll.is_some() {
- warn!(
- target: LOG_TARGET_CONFIG,
- "Config scrolling.auto_scroll has been removed and is now always disabled, it can be \
- safely removed from the config"
- );
- }
-
- if config.tabspaces.is_some() {
- warn!(
- target: LOG_TARGET_CONFIG,
- "Config tabspaces has been removed and is now always 8, it can be safely removed from \
- the config"
- );
- }
-
- if config.visual_bell.is_some() {
- warn!(
- target: LOG_TARGET_CONFIG,
- "Config visual_bell has been deprecated; please use bell instead"
- )
- }
-
- if config.ui_config.dynamic_title.is_some() {
- warn!(
- target: LOG_TARGET_CONFIG,
- "Config dynamic_title is deprecated; please use window.dynamic_title instead",
- )
- }
-
- #[cfg(all(windows, not(feature = "winpty")))]
- if config.winpty_backend {
- warn!(
- target: LOG_TARGET_CONFIG,
- "Config winpty_backend is deprecated and requires a compilation flag; it should be \
- removed from the config",
- )
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
diff --git a/alacritty/src/config/mouse.rs b/alacritty/src/config/mouse.rs
index 1a5aec3d..f562ebe0 100644
--- a/alacritty/src/config/mouse.rs
+++ b/alacritty/src/config/mouse.rs
@@ -1,35 +1,26 @@
use std::time::Duration;
use glutin::event::ModifiersState;
-use log::error;
-use serde::{Deserialize, Deserializer};
-use alacritty_terminal::config::{failure_default, Program, LOG_TARGET_CONFIG};
+use alacritty_config_derive::ConfigDeserialize;
+use alacritty_terminal::config::Program;
use crate::config::bindings::ModsWrapper;
-#[serde(default)]
-#[derive(Default, Clone, Debug, Deserialize, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Default, Clone, Debug, PartialEq, Eq)]
pub struct Mouse {
- #[serde(deserialize_with = "failure_default")]
pub double_click: ClickHandler,
- #[serde(deserialize_with = "failure_default")]
pub triple_click: ClickHandler,
- #[serde(deserialize_with = "failure_default")]
pub hide_when_typing: bool,
- #[serde(deserialize_with = "failure_default")]
pub url: Url,
}
-#[serde(default)]
-#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
pub struct Url {
/// Program for opening links.
- #[serde(deserialize_with = "deserialize_launcher")]
pub launcher: Option<Program>,
/// Modifier used to open links.
- #[serde(deserialize_with = "failure_default")]
modifiers: ModsWrapper,
}
@@ -39,34 +30,6 @@ impl Url {
}
}
-fn deserialize_launcher<'a, D>(deserializer: D) -> std::result::Result<Option<Program>, D::Error>
-where
- D: Deserializer<'a>,
-{
- let default = Url::default().launcher;
-
- // Deserialize to generic value.
- let val = serde_yaml::Value::deserialize(deserializer)?;
-
- // Accept `None` to disable the launcher.
- if val.as_str().filter(|v| v.to_lowercase() == "none").is_some() {
- return Ok(None);
- }
-
- match <Option<Program>>::deserialize(val) {
- Ok(launcher) => Ok(launcher),
- Err(err) => {
- error!(
- target: LOG_TARGET_CONFIG,
- "Problem with config: {}; using {}",
- err,
- default.clone().unwrap().program()
- );
- Ok(default)
- },
- }
-}
-
impl Default for Url {
fn default() -> Url {
Url {
@@ -81,33 +44,19 @@ impl Default for Url {
}
}
-#[serde(default)]
-#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
pub struct ClickHandler {
- #[serde(deserialize_with = "deserialize_duration_ms")]
- pub threshold: Duration,
+ threshold: u16,
}
impl Default for ClickHandler {
fn default() -> Self {
- ClickHandler { threshold: default_threshold_ms() }
+ Self { threshold: 300 }
}
}
-fn default_threshold_ms() -> Duration {
- Duration::from_millis(300)
-}
-
-fn deserialize_duration_ms<'a, D>(deserializer: D) -> ::std::result::Result<Duration, D::Error>
-where
- D: Deserializer<'a>,
-{
- let value = serde_yaml::Value::deserialize(deserializer)?;
- match u64::deserialize(value) {
- Ok(threshold_ms) => Ok(Duration::from_millis(threshold_ms)),
- Err(err) => {
- error!(target: LOG_TARGET_CONFIG, "Problem with config: {}; using default value", err);
- Ok(default_threshold_ms())
- },
+impl ClickHandler {
+ pub fn threshold(&self) -> Duration {
+ Duration::from_millis(self.threshold as u64)
}
}
diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs
index 87b27b3e..25f9fb91 100644
--- a/alacritty/src/config/ui_config.rs
+++ b/alacritty/src/config/ui_config.rs
@@ -3,7 +3,8 @@ use std::path::PathBuf;
use log::error;
use serde::{Deserialize, Deserializer};
-use alacritty_terminal::config::{failure_default, Percentage, LOG_TARGET_CONFIG};
+use alacritty_config_derive::ConfigDeserialize;
+use alacritty_terminal::config::{Percentage, LOG_TARGET_CONFIG};
use crate::config::bindings::{self, Binding, KeyBinding, MouseBinding};
use crate::config::debug::Debug;
@@ -11,66 +12,52 @@ use crate::config::font::Font;
use crate::config::mouse::Mouse;
use crate::config::window::WindowConfig;
-#[derive(Debug, PartialEq, Deserialize)]
+#[derive(ConfigDeserialize, Debug, PartialEq)]
pub struct UIConfig {
/// Font configuration.
- #[serde(default, deserialize_with = "failure_default")]
pub font: Font,
/// Window configuration.
- #[serde(default, deserialize_with = "failure_default")]
pub window: WindowConfig,
- #[serde(default, deserialize_with = "failure_default")]
pub mouse: Mouse,
- /// Keybindings.
- #[serde(default = "default_key_bindings", deserialize_with = "deserialize_key_bindings")]
- pub key_bindings: Vec<KeyBinding>,
-
- /// Bindings for the mouse.
- #[serde(default = "default_mouse_bindings", deserialize_with = "deserialize_mouse_bindings")]
- pub mouse_bindings: Vec<MouseBinding>,
-
/// Debug options.
- #[serde(default, deserialize_with = "failure_default")]
pub debug: Debug,
/// Send escape sequences using the alt key.
- #[serde(default, deserialize_with = "failure_default")]
- alt_send_esc: DefaultTrueBool,
+ pub alt_send_esc: bool,
/// Live config reload.
- #[serde(default, deserialize_with = "failure_default")]
- live_config_reload: DefaultTrueBool,
-
- /// Background opacity from 0.0 to 1.0.
- #[serde(default, deserialize_with = "failure_default")]
- background_opacity: Percentage,
+ pub live_config_reload: bool,
/// Path where config was loaded from.
- #[serde(skip)]
+ #[config(skip)]
pub config_paths: Vec<PathBuf>,
- // TODO: DEPRECATED
- #[serde(default, deserialize_with = "failure_default")]
- pub dynamic_title: Option<bool>,
+ /// Keybindings.
+ key_bindings: KeyBindings,
+
+ /// Bindings for the mouse.
+ mouse_bindings: MouseBindings,
+
+ /// Background opacity from 0.0 to 1.0.
+ background_opacity: Percentage,
}
impl Default for UIConfig {
fn default() -> Self {
- UIConfig {
+ Self {
+ alt_send_esc: true,
+ live_config_reload: true,
font: Default::default(),
window: Default::default(),
mouse: Default::default(),
- key_bindings: default_key_bindings(),
- mouse_bindings: default_mouse_bindings(),
debug: Default::default(),
- alt_send_esc: Default::default(),
- background_opacity: Default::default(),
- live_config_reload: Default::default(),
- dynamic_title: Default::default(),
config_paths: Default::default(),
+ key_bindings: Default::default(),
+ mouse_bindings: Default::default(),
+ background_opacity: Default::default(),
}
}
}
@@ -82,48 +69,50 @@ impl UIConfig {
}
#[inline]
- pub fn dynamic_title(&self) -> bool {
- self.dynamic_title.unwrap_or_else(|| self.window.dynamic_title())
+ pub fn key_bindings(&self) -> &[KeyBinding] {
+ &self.key_bindings.0.as_slice()
}
#[inline]
- pub fn set_dynamic_title(&mut self, dynamic_title: bool) {
- self.window.set_dynamic_title(dynamic_title);
+ pub fn mouse_bindings(&self) -> &[MouseBinding] {
+ self.mouse_bindings.0.as_slice()
}
+}
- /// Live config reload.
- #[inline]
- pub fn live_config_reload(&self) -> bool {
- self.live_config_reload.0
- }
+#[derive(Debug, PartialEq)]
+struct KeyBindings(Vec<KeyBinding>);
- /// Send escape sequences using the alt key.
- #[inline]
- pub fn alt_send_esc(&self) -> bool {
- self.alt_send_esc.0
+impl Default for KeyBindings {
+ fn default() -> Self {
+ Self(bindings::default_key_bindings())
}
}
-fn default_key_bindings() -> Vec<KeyBinding> {
- bindings::default_key_bindings()
+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)?))
+ }
}
-fn default_mouse_bindings() -> Vec<MouseBinding> {
- bindings::default_mouse_bindings()
-}
+#[derive(Debug, PartialEq)]
+struct MouseBindings(Vec<MouseBinding>);
-fn deserialize_key_bindings<'a, D>(deserializer: D) -> Result<Vec<KeyBinding>, D::Error>
-where
- D: Deserializer<'a>,
-{
- deserialize_bindings(deserializer, bindings::default_key_bindings())
+impl Default for MouseBindings {
+ fn default() -> Self {
+ Self(bindings::default_mouse_bindings())
+ }
}
-fn deserialize_mouse_bindings<'a, D>(deserializer: D) -> Result<Vec<MouseBinding>, D::Error>
-where
- D: Deserializer<'a>,
-{
- deserialize_bindings(deserializer, 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(deserialize_bindings(deserializer, Self::default().0)?))
+ }
}
fn deserialize_bindings<'a, D, T>(
@@ -143,7 +132,7 @@ where
match Binding::<T>::deserialize(value) {
Ok(binding) => bindings.push(binding),
Err(err) => {
- error!(target: LOG_TARGET_CONFIG, "Problem with config: {}; ignoring binding", err);
+ error!(target: LOG_TARGET_CONFIG, "Config error: {}; ignoring binding", err);
},
}
}
@@ -158,23 +147,11 @@ where
Ok(bindings)
}
-#[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq)]
-pub struct DefaultTrueBool(pub bool);
-
-impl Default for DefaultTrueBool {
- fn default() -> Self {
- DefaultTrueBool(true)
- }
-}
-
/// A delta for a point in a 2 dimensional plane.
-#[serde(default, bound(deserialize = "T: Deserialize<'de> + Default"))]
-#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)]
-pub struct Delta<T: Default + PartialEq + Eq> {
+#[derive(ConfigDeserialize, Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub struct Delta<T: Default> {
/// Horizontal change.
- #[serde(deserialize_with = "failure_default")]
pub x: T,
/// Vertical change.
- #[serde(deserialize_with = "failure_default")]
pub y: T,
}
diff --git a/alacritty/src/config/window.rs b/alacritty/src/config/window.rs
index ce36d23c..2d501b91 100644
--- a/alacritty/src/config/window.rs
+++ b/alacritty/src/config/window.rs
@@ -1,82 +1,77 @@
+use std::fmt::{self, Formatter};
use std::os::raw::c_ulong;
use glutin::window::Fullscreen;
use log::error;
+use serde::de::{self, MapAccess, Visitor};
use serde::{Deserialize, Deserializer};
-use serde_yaml::Value;
-use alacritty_terminal::config::{failure_default, option_explicit_none, LOG_TARGET_CONFIG};
+use alacritty_config_derive::ConfigDeserialize;
+use alacritty_terminal::config::LOG_TARGET_CONFIG;
use alacritty_terminal::index::{Column, Line};
-use crate::config::ui_config::{DefaultTrueBool, Delta};
+use crate::config::ui_config::Delta;
/// Default Alacritty name, used for window title and class.
pub const DEFAULT_NAME: &str = "Alacritty";
-#[serde(default)]
-#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Debug, Clone, PartialEq, Eq)]
pub struct WindowConfig {
/// Initial position.
- #[serde(deserialize_with = "failure_default")]
pub position: Option<Delta<i32>>,
/// Draw the window with title bar / borders.
- #[serde(deserialize_with = "failure_default")]
pub decorations: Decorations,
/// Startup mode.
- #[serde(deserialize_with = "failure_default")]
pub startup_mode: StartupMode,
- /// Window title.
- #[serde(default = "default_title")]
- pub title: String,
-
- /// Window class.
- #[serde(deserialize_with = "deserialize_class")]
- pub class: Class,
-
/// XEmbed parent.
- #[serde(skip)]
+ #[config(skip)]
pub embed: Option<c_ulong>,
/// GTK theme variant.
- #[serde(deserialize_with = "option_explicit_none")]
pub gtk_theme_variant: Option<String>,
/// Spread out additional padding evenly.
- #[serde(deserialize_with = "failure_default")]
pub dynamic_padding: bool,
+ /// Use dynamic title.
+ pub dynamic_title: bool,
+
+ /// Window title.
+ pub title: String,
+
+ /// Window class.
+ pub class: Class,
+
/// Pixel padding.
- #[serde(deserialize_with = "failure_default")]
padding: Delta<u8>,
- /// Use dynamic title.
- #[serde(default, deserialize_with = "failure_default")]
- dynamic_title: DefaultTrueBool,
-
/// Initial dimensions.
- #[serde(deserialize_with = "failure_default")]
dimensions: Dimensions,
}
-pub fn default_title() -> String {
- DEFAULT_NAME.to_string()
+impl Default for WindowConfig {
+ fn default() -> Self {
+ Self {
+ dynamic_title: true,
+ title: DEFAULT_NAME.into(),
+ position: Default::default(),
+ decorations: Default::default(),
+ startup_mode: Default::default(),
+ embed: Default::default(),
+ gtk_theme_variant: Default::default(),
+ dynamic_padding: Default::default(),
+ class: Default::default(),
+ padding: Default::default(),
+ dimensions: Default::default(),
+ }
+ }
}
impl WindowConfig {
#[inline]
- pub fn dynamic_title(&self) -> bool {
- self.dynamic_title.0
- }
-
- #[inline]
- pub fn set_dynamic_title(&mut self, dynamic_title: bool) {
- self.dynamic_title.0 = dynamic_title;
- }
-
- #[inline]
pub fn dimensions(&self) -> Option<Dimensions> {
if self.dimensions.columns.0 != 0
&& self.dimensions.lines.0 != 0
@@ -110,25 +105,7 @@ impl WindowConfig {
}
}
-impl Default for WindowConfig {
- fn default() -> WindowConfig {
- WindowConfig {
- dimensions: Default::default(),
- position: Default::default(),
- padding: Default::default(),
- decorations: Default::default(),
- dynamic_padding: Default::default(),
- startup_mode: Default::default(),
- class: Default::default(),
- embed: Default::default(),
- gtk_theme_variant: Default::default(),
- title: default_title(),
- dynamic_title: Default::default(),
- }
- }
-}
-
-#[derive(Debug, Deserialize, Copy, Clone, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub enum StartupMode {
Windowed,
Maximized,
@@ -143,17 +120,13 @@ impl Default for StartupMode {
}
}
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
+#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub enum Decorations {
- #[serde(rename = "full")]
Full,
#[cfg(target_os = "macos")]
- #[serde(rename = "transparent")]
Transparent,
#[cfg(target_os = "macos")]
- #[serde(rename = "buttonless")]
Buttonless,
- #[serde(rename = "none")]
None,
}
@@ -166,71 +139,82 @@ impl Default for Decorations {
/// Window Dimensions.
///
/// Newtype to avoid passing values incorrectly.
-#[serde(default)]
-#[derive(Default, Debug, Copy, Clone, Deserialize, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Default, Debug, Copy, Clone, PartialEq, Eq)]
pub struct Dimensions {
/// Window width in character columns.
- #[serde(deserialize_with = "failure_default")]
pub columns: Column,
/// Window Height in character lines.
- #[serde(deserialize_with = "failure_default")]
pub lines: Line,
}
/// Window class hint.
-#[serde(default)]
-#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Class {
- #[serde(deserialize_with = "deserialize_class_resource")]
pub instance: String,
-
- #[serde(deserialize_with = "deserialize_class_resource")]
pub general: String,
}
impl Default for Class {
fn default() -> Self {
- Class { instance: DEFAULT_NAME.into(), general: DEFAULT_NAME.into() }
- }
-}
-
-fn deserialize_class_resource<'a, D>(deserializer: D) -> Result<String, D::Error>
-where
- D: Deserializer<'a>,
-{
- let value = Value::deserialize(deserializer)?;
- match String::deserialize(value) {
- Ok(value) => Ok(value),
- Err(err) => {
- error!(
- target: LOG_TARGET_CONFIG,
- "Problem with config: {}, using default value {}", err, DEFAULT_NAME,
- );
-
- Ok(DEFAULT_NAME.into())
- },
+ Self { instance: DEFAULT_NAME.into(), general: DEFAULT_NAME.into() }
}
}
-fn deserialize_class<'a, D>(deserializer: D) -> Result<Class, D::Error>
-where
- D: Deserializer<'a>,
-{
- let value = Value::deserialize(deserializer)?;
-
- if let Value::String(instance) = value {
- return Ok(Class { instance, general: DEFAULT_NAME.into() });
- }
+impl<'de> Deserialize<'de> for Class {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ struct ClassVisitor;
+ impl<'a> Visitor<'a> for ClassVisitor {
+ type Value = Class;
+
+ fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_str("a mapping")
+ }
+
+ fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ Ok(Self::Value { instance: value.into(), ..Self::Value::default() })
+ }
+
+ fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
+ where
+ M: MapAccess<'a>,
+ {
+ let mut class = Self::Value::default();
+
+ while let Some((key, value)) = map.next_entry::<String, serde_yaml::Value>()? {
+ match key.as_str() {
+ "instance" => match String::deserialize(value) {
+ Ok(instance) => class.instance = instance,
+ Err(err) => {
+ error!(
+ target: LOG_TARGET_CONFIG,
+ "Config error: class.instance: {}", err
+ );
+ },
+ },
+ "general" => match String::deserialize(value) {
+ Ok(general) => class.general = general,
+ Err(err) => {
+ error!(
+ target: LOG_TARGET_CONFIG,
+ "Config error: class.instance: {}", err
+ );
+ },
+ },
+ _ => (),
+ }
+ }
+
+ Ok(class)
+ }
+ }
- match Class::deserialize(value) {
- Ok(value) => Ok(value),
- Err(err) => {
- error!(
- target: LOG_TARGET_CONFIG,
- "Problem with config: {}; using class {}", err, DEFAULT_NAME
- );
- Ok(Class::default())
- },
+ deserializer.deserialize_any(ClassVisitor)
}
}
diff --git a/alacritty/src/cursor.rs b/alacritty/src/cursor.rs
index edf76bf3..94808029 100644
--- a/alacritty/src/cursor.rs
+++ b/alacritty/src/cursor.rs
@@ -10,7 +10,7 @@ pub fn get_cursor_glyph(
offset_x: i8,
offset_y: i8,
is_wide: bool,
- cursor_thickness: f64,
+ cursor_thickness: f32,
) -> RasterizedGlyph {
// Calculate the cell metrics.
//
@@ -18,7 +18,7 @@ pub fn get_cursor_glyph(
// https://github.com/rust-lang/rust/commit/14d608f1d8a0b84da5f3bccecb3efb3d35f980dc
let height = (metrics.line_height + f64::from(offset_y)).max(1.) as usize;
let mut width = (metrics.average_advance + f64::from(offset_x)).max(1.) as usize;
- let line_width = (cursor_thickness * width as f64).round().max(1.) as usize;
+ let line_width = (cursor_thickness * width as f32).round().max(1.) as usize;
// Double the cursor width if it's above a double-width glyph.
if is_wide {
diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs
index 451874c8..1fe1d2de 100644
--- a/alacritty/src/display.rs
+++ b/alacritty/src/display.rs
@@ -20,8 +20,6 @@ use unicode_width::UnicodeWidthChar;
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
use wayland_client::{Display as WaylandDisplay, EventQueue};
-#[cfg(target_os = "macos")]
-use crossfont::set_font_smoothing;
use crossfont::{self, Rasterize, Rasterizer};
use alacritty_terminal::event::{EventListener, OnResize};
@@ -254,7 +252,7 @@ impl Display {
// Set subpixel anti-aliasing.
#[cfg(target_os = "macos")]
- set_font_smoothing(config.ui_config.font.use_thin_strokes());
+ crossfont::set_font_smoothing(config.ui_config.font.use_thin_strokes);
#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
let is_x11 = event_loop.is_x11();
@@ -313,7 +311,7 @@ impl Display {
config: &Config,
) -> Result<(GlyphCache, f32, f32), Error> {
let font = config.ui_config.font.clone();
- let rasterizer = Rasterizer::new(dpr as f32, config.ui_config.font.use_thin_strokes())?;
+ let rasterizer = Rasterizer::new(dpr as f32, config.ui_config.font.use_thin_strokes)?;
// Initialize glyph cache.
let glyph_cache = {
@@ -491,8 +489,8 @@ impl Display {
.map_or(false, |viewport_match| viewport_match.contains(&cell_point))
{
let colors = config.colors.search.focused_match;
- let match_fg = colors.foreground().color(cell.fg, cell.bg);
- cell.bg = colors.background().color(cell.fg, cell.bg);
+ let match_fg = colors.foreground.color(cell.fg, cell.bg);
+ cell.bg = colors.background.color(cell.fg, cell.bg);
cell.fg = match_fg;
cell.bg_alpha = 1.0;
}
@@ -558,8 +556,8 @@ impl Display {
let y = size_info.cell_height().mul_add(start_line.0 as f32, size_info.padding_y());
let color = match message.ty() {
- MessageType::Error => config.colors.normal().red,
- MessageType::Warning => config.colors.normal().yellow,
+ MessageType::Error => config.colors.normal.red,
+ MessageType::Warning => config.colors.normal.yellow,
};
let message_bar_rect =
@@ -680,7 +678,7 @@ impl Display {
let timing = format!("{:.3} usec", self.meter.average());
let fg = config.colors.primary.background;
- let bg = config.colors.normal().red;
+ let bg = config.colors.normal.red;
self.renderer.with_api(&config.ui_config, config.cursor, &size_info, |mut api| {
api.render_string(glyph_cache, size_info.screen_lines() - 2, &timing[..], fg, Some(bg));
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs
index 4369a689..87b1d5c8 100644
--- a/alacritty/src/event.rs
+++ b/alacritty/src/event.rs
@@ -4,6 +4,7 @@ use std::borrow::Cow;
use std::cmp::{max, min};
use std::collections::VecDeque;
use std::env;
+use std::f32;
use std::fmt::Debug;
#[cfg(not(any(target_os = "macos", windows)))]
use std::fs;
@@ -26,8 +27,6 @@ use glutin::platform::unix::EventLoopWindowTargetExtUnix;
use log::info;
use serde_json as json;
-#[cfg(target_os = "macos")]
-use crossfont::set_font_smoothing;
use crossfont::{self, Size};
use alacritty_terminal::config::LOG_TARGET_CONFIG;
@@ -396,7 +395,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
}
fn reset_font_size(&mut self) {
- *self.font_size = self.config.ui_config.font.size;
+ *self.font_size = self.config.ui_config.font.size();
self.display_update_pending.set_font(self.config.ui_config.font.clone());
self.terminal.dirty = true;
}
@@ -874,7 +873,7 @@ impl<N: Notify + OnResize> Processor<N> {
received_count: 0,
suppress_chars: false,
modifiers: Default::default(),
- font_size: config.ui_config.font.size,
+ font_size: config.ui_config.font.size(),
config,
message_buffer,
display,
@@ -1080,13 +1079,13 @@ impl<N: Notify + OnResize> Processor<N> {
Event::TerminalEvent(event) => match event {
TerminalEvent::Title(title) => {
let ui_config = &processor.ctx.config.ui_config;
- if ui_config.dynamic_title() {
+ if ui_config.window.dynamic_title {
processor.ctx.window.set_title(&title);
}
},
TerminalEvent::ResetTitle => {
let ui_config = &processor.ctx.config.ui_config;
- if ui_config.dynamic_title() {
+ if ui_config.window.dynamic_title {
processor.ctx.window.set_title(&ui_config.window.title);
}
},
@@ -1241,15 +1240,15 @@ impl<N: Notify + OnResize> Processor<N> {
// Reload cursor if its thickness has changed.
if (processor.ctx.config.cursor.thickness() - config.cursor.thickness()).abs()
- > std::f64::EPSILON
+ > f32::EPSILON
{
processor.ctx.display_update_pending.set_cursor_dirty();
}
if processor.ctx.config.ui_config.font != config.ui_config.font {
// Do not update font size if it has been changed at runtime.
- if *processor.ctx.font_size == processor.ctx.config.ui_config.font.size {
- *processor.ctx.font_size = config.ui_config.font.size;
+ if *processor.ctx.font_size == processor.ctx.config.ui_config.font.size() {
+ *processor.ctx.font_size = config.ui_config.font.size();
}
let font = config.ui_config.font.clone().with_size(*processor.ctx.font_size);
@@ -1265,7 +1264,7 @@ impl<N: Notify + OnResize> Processor<N> {
}
// Live title reload.
- if !config.ui_config.dynamic_title()
+ if !config.ui_config.window.dynamic_title
|| processor.ctx.config.ui_config.window.title != config.ui_config.window.title
{
processor.ctx.window.set_title(&config.ui_config.window.title);
@@ -1278,7 +1277,7 @@ impl<N: Notify + OnResize> Processor<N> {
// Set subpixel anti-aliasing.
#[cfg(target_os = "macos")]
- set_font_smoothing(config.ui_config.font.use_thin_strokes());
+ crossfont::set_font_smoothing(config.ui_config.font.use_thin_strokes);
*processor.ctx.config = config;
diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs
index 313c7051..4f66721c 100644
--- a/alacritty/src/input.rs
+++ b/alacritty/src/input.rs
@@ -561,10 +561,10 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
self.ctx.mouse_mut().last_click_button = button;
ClickState::Click
},
- ClickState::Click if elapsed < mouse_config.double_click.threshold => {
+ ClickState::Click if elapsed < mouse_config.double_click.threshold() => {
ClickState::DoubleClick
},
- ClickState::DoubleClick if elapsed < mouse_config.triple_click.threshold => {
+ ClickState::DoubleClick if elapsed < mouse_config.triple_click.threshold() => {
ClickState::TripleClick
},
_ => ClickState::Click,
@@ -714,13 +714,7 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
.contains(TermMode::ALT_SCREEN | TermMode::ALTERNATE_SCROLL)
&& !self.ctx.modifiers().shift()
{
- let multiplier = f64::from(
- self.ctx
- .config()
- .scrolling
- .faux_multiplier()
- .unwrap_or_else(|| self.ctx.config().scrolling.multiplier()),
- );
+ let multiplier = f64::from(self.ctx.config().scrolling.multiplier);
self.ctx.mouse_mut().scroll_px += new_scroll_px * multiplier;
let cmd = if new_scroll_px > 0. { b'A' } else { b'B' };
@@ -734,7 +728,7 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
}
self.ctx.write_to_pty(content);
} else {
- let multiplier = f64::from(self.ctx.config().scrolling.multiplier());
+ let multiplier = f64::from(self.ctx.config().scrolling.multiplier);
self.ctx.mouse_mut().scroll_px += new_scroll_px * multiplier;
let lines = self.ctx.mouse().scroll_px / height;
@@ -878,7 +872,7 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
c.encode_utf8(&mut bytes[..]);
}
- if self.ctx.config().ui_config.alt_send_esc()
+ if self.ctx.config().ui_config.alt_send_esc
&& *self.ctx.received_count() == 0
&& self.ctx.modifiers().alt()
&& utf8_len == 1
@@ -900,8 +894,8 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
let mods = *self.ctx.modifiers();
let mut suppress_chars = None;
- for i in 0..self.ctx.config().ui_config.key_bindings.len() {
- let binding = &self.ctx.config().ui_config.key_bindings[i];
+ for i in 0..self.ctx.config().ui_config.key_bindings().len() {
+ let binding = &self.ctx.config().ui_config.key_bindings()[i];
let key = match (binding.trigger, input.virtual_keycode) {
(Key::Scancode(_), _) => Key::Scancode(input.scancode),
@@ -932,8 +926,8 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
let mouse_mode = self.ctx.mouse_mode();
let mods = *self.ctx.modifiers();
- for i in 0..self.ctx.config().ui_config.mouse_bindings.len() {
- let mut binding = self.ctx.config().ui_config.mouse_bindings[i].clone();
+ for i in 0..self.ctx.config().ui_config.mouse_bindings().len() {
+ let mut binding = self.ctx.config().ui_config.mouse_bindings()[i].clone();
// Require shift for all modifiers when mouse mode is active.
if mouse_mode {
@@ -1072,7 +1066,6 @@ mod tests {
use alacritty_terminal::event::Event as TerminalEvent;
use alacritty_terminal::selection::Selection;
- use crate::config::ClickHandler;
use crate::message_bar::MessageBuffer;
const KEY: VirtualKeyCode = VirtualKeyCode::Key0;
@@ -1251,18 +1244,8 @@ mod tests {
} => {
#[test]
fn $name() {
- let mut cfg = Config::default();
- cfg.ui_config.mouse = crate::config::Mouse {
- double_click: ClickHandler {
- threshold: Duration::from_millis(1000),
- },
- triple_click: ClickHandler {
- threshold: Duration::from_millis(1000),
- },
- hide_when_typing: false,
- url: Default::default(),
- };
-
+ let mut clipboard = Clipboard::new_nop();
+ let cfg = Config::default();
let size = SizeInfo::new(
21.0,
51.0,
@@ -1273,8 +1256,6 @@ mod tests {
false,
);
- let mut clipboard = Clipboard::new_nop();
-
let mut terminal = Term::new(&cfg, size, MockEventProxy);
let mut mouse = Mouse::default();
diff --git a/alacritty/src/logging.rs b/alacritty/src/logging.rs
index 2110d8e1..36d20fa3 100644
--- a/alacritty/src/logging.rs
+++ b/alacritty/src/logging.rs
@@ -23,7 +23,7 @@ use crate::message_bar::{Message, MessageType};
const ALACRITTY_LOG_ENV: &str = "ALACRITTY_LOG";
/// List of targets which will be logged by Alacritty.
const ALLOWED_TARGETS: [&str; 4] =
- ["alacritty_terminal", "alacritty_config", "alacritty", "crossfont"];
+ ["alacritty_terminal", "alacritty_config_derive", "alacritty", "crossfont"];
pub fn initialize(
options: &Options,
diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs
index 3b517efc..0e27c893 100644
--- a/alacritty/src/main.rs
+++ b/alacritty/src/main.rs
@@ -186,7 +186,7 @@ fn run(
//
// The monitor watches the config file for changes and reloads it. Pending
// config changes are processed in the main loop.
- if config.ui_config.live_config_reload() {
+ if config.ui_config.live_config_reload {
monitor::watch(config.ui_config.config_paths.clone(), event_proxy);
}
diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs
index 3320a860..cc2515be 100644
--- a/alacritty/src/renderer/mod.rs
+++ b/alacritty/src/renderer/mod.rs
@@ -156,15 +156,15 @@ impl GlyphCache {
// Need to load at least one glyph for the face before calling metrics.
// The glyph requested here ('m' at the time of writing) has no special
// meaning.
- rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?;
+ rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
- let metrics = rasterizer.metrics(regular, font.size)?;
+ let metrics = rasterizer.metrics(regular, font.size())?;
let mut cache = Self {
cache: HashMap::default(),
cursor_cache: HashMap::default(),
rasterizer,
- font_size: font.size,
+ font_size: font.size(),
font_key: regular,
bold_key: bold,
italic_key: italic,
@@ -190,7 +190,7 @@ impl GlyphCache {
font: &Font,
rasterizer: &mut Rasterizer,
) -> Result<(FontKey, FontKey, FontKey, FontKey), crossfont::Error> {
- let size = font.size;
+ let size = font.size();
// Load regular font.
let regular_desc = Self::make_desc(&font.normal(), Slant::Normal, Weight::Normal);
@@ -291,12 +291,12 @@ impl GlyphCache {
let (regular, bold, italic, bold_italic) =
Self::compute_font_keys(font, &mut self.rasterizer)?;
- self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?;
- let metrics = self.rasterizer.metrics(regular, font.size)?;
+ self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
+ let metrics = self.rasterizer.metrics(regular, font.size())?;
- info!("Font size changed to {:?} with DPR of {}", font.size, dpr);
+ info!("Font size changed to {:?} with DPR of {}", font.size(), dpr);
- self.font_size = font.size;
+ self.font_size = font.size();
self.font_key = regular;
self.bold_key = bold;
self.italic_key = italic;
@@ -322,12 +322,12 @@ impl GlyphCache {
/// Calculate font metrics without access to a glyph cache.
pub fn static_metrics(font: Font, dpr: f64) -> Result<crossfont::Metrics, crossfont::Error> {
- let mut rasterizer = crossfont::Rasterizer::new(dpr as f32, font.use_thin_strokes())?;
+ let mut rasterizer = crossfont::Rasterizer::new(dpr as f32, font.use_thin_strokes)?;
let regular_desc = GlyphCache::make_desc(&font.normal(), Slant::Normal, Weight::Normal);
- let regular = Self::load_regular_font(&mut rasterizer, &regular_desc, font.size)?;
- rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?;
+ let regular = Self::load_regular_font(&mut rasterizer, &regular_desc, font.size())?;
+ rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
- rasterizer.metrics(regular, font.size)
+ rasterizer.metrics(regular, font.size())
}
}
diff --git a/alacritty/src/wayland_theme.rs b/alacritty/src/wayland_theme.rs
index b9c4381e..5d3bd922 100644
--- a/alacritty/src/wayland_theme.rs
+++ b/alacritty/src/wayland_theme.rs
@@ -17,9 +17,9 @@ pub struct AlacrittyWaylandTheme {
impl AlacrittyWaylandTheme {
pub fn new(colors: &Colors) -> Self {
- let hovered_close_icon = colors.normal().red.into_rgba();
- let hovered_maximize_icon = colors.normal().green.into_rgba();
- let hovered_minimize_icon = colors.normal().yellow.into_rgba();
+ let hovered_close_icon = colors.normal.red.into_rgba();
+ let hovered_maximize_icon = colors.normal.green.into_rgba();
+ let hovered_minimize_icon = colors.normal.yellow.into_rgba();
let foreground = colors.search_bar_foreground().into_rgba();
let background = colors.search_bar_background().into_rgba();
diff --git a/alacritty/src/window.rs b/alacritty/src/window.rs
index 1b9e7731..f4a38183 100644
--- a/alacritty/src/window.rs
+++ b/alacritty/src/window.rs
@@ -271,8 +271,6 @@ impl Window {
Icon::from_rgba(buf, info.width, info.height)
};
- let class = &window_config.class;
-
let builder = WindowBuilder::new()
.with_title(title)
.with_visible(false)
@@ -285,10 +283,13 @@ impl Window {
let builder = builder.with_window_icon(icon.ok());
#[cfg(feature = "wayland")]
- let builder = builder.with_app_id(class.instance.clone());
+ let builder = builder.with_app_id(window_config.class.instance.to_owned());
#[cfg(feature = "x11")]
- let builder = builder.with_class(class.instance.clone(), class.general.clone());
+ let builder = builder.with_class(
+ window_config.class.instance.to_owned(),
+ window_config.class.general.to_owned(),
+ );
#[cfg(feature = "x11")]
let builder = match &window_config.gtk_theme_variant {
diff --git a/alacritty_config_derive/Cargo.toml b/alacritty_config_derive/Cargo.toml
new file mode 100644
index 00000000..8d6d9c4b
--- /dev/null
+++ b/alacritty_config_derive/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "alacritty_config_derive"
+version = "0.1.0"
+authors = ["Christian Duerr <contact@christianduerr.com>"]
+license = "MIT/Apache-2.0"
+description = "Failure resistant deserialization derive"
+homepage = "https://github.com/alacritty/alacritty"
+edition = "2018"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+syn = { version = "1.0.53", features = ["derive", "parsing", "proc-macro", "printing"], default-features = false }
+proc-macro2 = "1.0.24"
+quote = "1.0.7"
+
+[dev-dependencies]
+serde_yaml = "0.8.14"
+serde = "1.0.117"
+log = "0.4.11"
diff --git a/alacritty_config_derive/LICENSE-APACHE b/alacritty_config_derive/LICENSE-APACHE
new file mode 120000
index 00000000..965b606f
--- /dev/null
+++ b/alacritty_config_derive/LICENSE-APACHE
@@ -0,0 +1 @@
+../LICENSE-APACHE \ No newline at end of file
diff --git a/alacritty_config_derive/LICENSE-MIT b/alacritty_config_derive/LICENSE-MIT
new file mode 100644
index 00000000..31aa7938
--- /dev/null
+++ b/alacritty_config_derive/LICENSE-MIT
@@ -0,0 +1,23 @@
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/alacritty_config_derive/src/de_enum.rs b/alacritty_config_derive/src/de_enum.rs
new file mode 100644
index 00000000..98247c0c
--- /dev/null
+++ b/alacritty_config_derive/src/de_enum.rs
@@ -0,0 +1,66 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use syn::{DataEnum, Ident};
+
+pub fn derive_deserialize(ident: Ident, data_enum: DataEnum) -> TokenStream {
+ let visitor = format_ident!("{}Visitor", ident);
+
+ // Create match arm streams and get a list with all available values.
+ let mut match_arms_stream = TokenStream2::new();
+ let mut available_values = String::from("one of ");
+ for variant in data_enum.variants.iter().filter(|variant| {
+ // Skip deserialization for `#[config(skip)]` fields.
+ variant.attrs.iter().all(|attr| {
+ !crate::path_ends_with(&attr.path, "config") || attr.tokens.to_string() != "(skip)"
+ })
+ }) {
+ let variant_ident = &variant.ident;
+ let variant_str = variant_ident.to_string();
+ available_values = format!("{}`{}`, ", available_values, variant_str);
+
+ let literal = variant_str.to_lowercase();
+
+ match_arms_stream.extend(quote! {
+ #literal => Ok(#ident :: #variant_ident),
+ });
+ }
+
+ // Remove trailing `, ` from the last enum variant.
+ available_values.truncate(available_values.len().saturating_sub(2));
+
+ // Generate deserialization impl.
+ let tokens = quote! {
+ struct #visitor;
+ impl<'de> serde::de::Visitor<'de> for #visitor {
+ type Value = #ident;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+ formatter.write_str(#available_values)
+ }
+
+ fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ match s.to_lowercase().as_str() {
+ #match_arms_stream
+ _ => Err(E::custom(
+ &format!("unknown variant `{}`, expected {}", s, #available_values)
+ )),
+ }
+ }
+ }
+
+ impl<'de> serde::Deserialize<'de> for #ident {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ deserializer.deserialize_str(#visitor)
+ }
+ }
+ };
+
+ tokens.into()
+}
diff --git a/alacritty_config_derive/src/de_struct.rs b/alacritty_config_derive/src/de_struct.rs
new file mode 100644
index 00000000..1325cae3
--- /dev/null
+++ b/alacritty_config_derive/src/de_struct.rs
@@ -0,0 +1,226 @@
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokenStream2;
+use quote::{format_ident, quote};
+use syn::parse::{self, Parse, ParseStream};
+use syn::punctuated::Punctuated;
+use syn::spanned::Spanned;
+use syn::{Error, Field, GenericParam, Generics, Ident, LitStr, Token, Type, TypeParam};
+
+/// Error message when attempting to flatten multiple fields.
+const MULTIPLE_FLATTEN_ERROR: &str = "At most one instance of #[config(flatten)] is supported";
+/// Use this crate's name as log target.
+const LOG_TARGET: &str = env!("CARGO_PKG_NAME");
+
+pub fn derive_deserialize<T>(
+ ident: Ident,
+ generics: Generics,
+ fields: Punctuated<Field, T>,
+) -> TokenStream {
+ // Create all necessary tokens for the implementation.
+ let GenericsStreams { unconstrained, constrained, phantoms } =
+ generics_streams(generics.params);
+ let FieldStreams { flatten, match_assignments } = fields_deserializer(&fields);
+ let visitor = format_ident!("{}Visitor", ident);
+
+ // Generate deserialization impl.
+ let tokens = quote! {
+ #[derive(Default)]
+ #[allow(non_snake_case)]
+ struct #visitor < #unconstrained > {
+ #phantoms
+ }
+
+ impl<'de, #constrained> serde::de::Visitor<'de> for #visitor < #unconstrained > {
+ type Value = #ident < #unconstrained >;
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+ formatter.write_str("a mapping")
+ }
+
+ fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
+ where
+ M: serde::de::MapAccess<'de>,
+ {
+ let mut config = Self::Value::default();
+
+ // NOTE: This could be used to print unused keys.
+ let mut unused = serde_yaml::Mapping::new();
+
+ while let Some((key, value)) = map.next_entry::<String, serde_yaml::Value>()? {
+ match key.as_str() {
+ #match_assignments
+ _ => {
+ unused.insert(serde_yaml::Value::String(key), value);
+ },
+ }
+ }
+
+ #flatten
+
+ Ok(config)
+ }
+ }
+
+ impl<'de, #constrained> serde::Deserialize<'de> for #ident < #unconstrained > {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ deserializer.deserialize_map(#visitor :: default())
+ }
+ }
+ };
+
+ tokens.into()
+}
+
+// Token streams created from the fields in the struct.
+#[derive(Default)]
+struct FieldStreams {
+ match_assignments: TokenStream2,
+ flatten: TokenStream2,
+}
+
+/// Create the deserializers for match arms and flattened fields.
+fn fields_deserializer<T>(fields: &Punctuated<Field, T>) -> FieldStreams {
+ let mut field_streams = FieldStreams::default();
+
+ // Create the deserialization stream for each field.
+ for field in fields.iter() {
+ if let Err(err) = field_deserializer(&mut field_streams, field) {
+ field_streams.flatten = err.to_compile_error();
+ return field_streams;
+ }
+ }
+
+ field_streams
+}
+
+/// Append a single field deserializer to the stream.
+fn field_deserializer(field_streams: &mut FieldStreams, field: &Field) -> Result<(), Error> {
+ let ident = field.ident.as_ref().expect("unreachable tuple struct");
+ let literal = ident.to_string();
+ let mut literals = vec![literal.clone()];
+
+ // Create default stream for deserializing fields.
+ let mut match_assignment_stream = quote! {
+ match serde::Deserialize::deserialize(value) {
+ Ok(value) => config.#ident = value,
+ Err(err) => {
+ log::error!(target: #LOG_TARGET, "Config error: {}: {}", #literal, err);
+ },
+ }
+ };
+
+ // Iterate over all #[config(...)] attributes.
+ for attr in field.attrs.iter().filter(|attr| crate::path_ends_with(&attr.path, "config")) {
+ let parsed = match attr.parse_args::<Attr>() {
+ Ok(parsed) => parsed,
+ Err(_) => continue,
+ };
+
+ match parsed.ident.as_str() {
+ // Skip deserialization for `#[config(skip)]` fields.
+ "skip" => return Ok(()),
+ "flatten" => {
+ // NOTE: Currently only a single instance of flatten is supported per struct
+ // for complexity reasons.
+ if !field_streams.flatten.is_empty() {
+ return Err(Error::new(attr.span(), MULTIPLE_FLATTEN_ERROR));
+ }
+
+ // Create the tokens to deserialize the flattened struct from the unused fields.
+ field_streams.flatten.extend(quote! {
+ let unused = serde_yaml::Value::Mapping(unused);
+ config.#ident = serde::Deserialize::deserialize(unused).unwrap_or_default();
+ });
+ },
+ "deprecated" => {
+ // Construct deprecation message and append optional attribute override.
+ let mut message = format!("Config warning: {} is deprecated", literal);
+ if let Some(warning) = parsed.param {
+ message = format!("{}; {}", message, warning.value());
+ }
+
+ // Append stream to log deprecation warning.
+ match_assignment_stream.extend(quote! {
+ log::warn!(target: #LOG_TARGET, #message);
+ });
+ },
+ // Add aliases to match pattern.
+ "alias" => {
+ if let Some(alias) = parsed.param {
+ literals.push(alias.value());
+ }
+ },
+ _ => (),
+ }
+ }
+
+ // Create token stream for deserializing "none" string into `Option<T>`.
+ if let Type::Path(type_path) = &field.ty {
+ if crate::path_ends_with(&type_path.path, "Option") {
+ match_assignment_stream = quote! {
+ if value.as_str().map_or(false, |s| s.eq_ignore_ascii_case("none")) {
+ config.#ident = None;
+ continue;
+ }
+ #match_assignment_stream
+ };
+ }
+ }
+
+ // Create the token stream for deserialization and error handling.
+ field_streams.match_assignments.extend(quote! {
+ #(#literals)|* => { #match_assignment_stream },
+ });
+
+ Ok(())
+}
+
+/// Field attribute.
+struct Attr {
+ ident: String,
+ param: Option<LitStr>,
+}
+
+impl Parse for Attr {
+ fn parse(input: ParseStream) -> parse::Result<Self> {
+ let ident = input.parse::<Ident>()?.to_string();
+ let param = input.parse::<Token![=]>().and_then(|_| input.parse()).ok();
+ Ok(Self { ident, param })
+ }
+}
+
+/// Storage for all necessary generics information.
+#[derive(Default)]
+struct GenericsStreams {
+ unconstrained: TokenStream2,
+ constrained: TokenStream2,
+ phantoms: TokenStream2,
+}
+
+/// Create the necessary generics annotations.
+///
+/// This will create three different token streams, which might look like this:
+/// - unconstrained: `T`
+/// - constrained: `T: Default + Deserialize<'de>`
+/// - phantoms: `T: PhantomData<T>,`
+fn generics_streams<T>(params: Punctuated<GenericParam, T>) -> GenericsStreams {
+ let mut generics = GenericsStreams::default();
+
+ for generic in params {
+ // NOTE: Lifetimes and const params are not supported.
+ if let GenericParam::Type(TypeParam { ident, .. }) = generic {
+ generics.unconstrained.extend(quote!( #ident , ));
+ generics.constrained.extend(quote! {
+ #ident : Default + serde::Deserialize<'de> ,
+ });
+ generics.phantoms.extend(quote! {
+ #ident : std::marker::PhantomData < #ident >,
+ });
+ }
+ }
+
+ generics
+}
diff --git a/alacritty_config_derive/src/lib.rs b/alacritty_config_derive/src/lib.rs
new file mode 100644
index 00000000..8601d5cb
--- /dev/null
+++ b/alacritty_config_derive/src/lib.rs
@@ -0,0 +1,27 @@
+use proc_macro::TokenStream;
+use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Error, Fields, Path};
+
+mod de_enum;
+mod de_struct;
+
+/// Error if the derive was used on an unsupported type.
+const UNSUPPORTED_ERROR: &str = "ConfigDeserialize must be used on a struct with fields";
+
+#[proc_macro_derive(ConfigDeserialize, attributes(config))]
+pub fn derive_config_deserialize(input: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(input as DeriveInput);
+
+ match input.data {
+ Data::Struct(DataStruct { fields: Fields::Named(fields), .. }) => {
+ de_struct::derive_deserialize(input.ident, input.generics, fields.named)
+ },
+ Data::Enum(data_enum) => de_enum::derive_deserialize(input.ident, data_enum),
+ _ => Error::new(input.ident.span(), UNSUPPORTED_ERROR).to_compile_error().into(),
+ }
+}
+
+/// Verify that a token path ends with a specific segment.
+pub(crate) fn path_ends_with(path: &Path, segment: &str) -> bool {
+ let segments = path.segments.iter();
+ segments.last().map_or(false, |s| s.ident == segment)
+}
diff --git a/alacritty_config_derive/tests/config.rs b/alacritty_config_derive/tests/config.rs
new file mode 100644
index 00000000..03abf893
--- /dev/null
+++ b/alacritty_config_derive/tests/config.rs
@@ -0,0 +1,155 @@
+use std::sync::{Arc, Mutex};
+
+use log::{Level, Log, Metadata, Record};
+
+use alacritty_config_derive::ConfigDeserialize;
+
+#[derive(ConfigDeserialize, Debug, PartialEq, Eq)]
+enum TestEnum {
+ One,
+ Two,
+ Three,
+ #[config(skip)]
+ Nine(String),
+}
+
+impl Default for TestEnum {
+ fn default() -> Self {
+ Self::Nine(String::from("nine"))
+ }
+}
+
+#[derive(ConfigDeserialize)]
+struct Test {
+ #[config(alias = "noalias")]
+ #[config(deprecated = "use field2 instead")]
+ field1: usize,
+ #[config(deprecated = "shouldn't be hit")]
+ field2: String,
+ field3: Option<u8>,
+ #[doc("aaa")]
+ nesting: Test2<usize>,
+ #[config(flatten)]
+ flatten: Test3,
+ enom_small: TestEnum,
+ enom_big: TestEnum,
+ #[config(deprecated)]
+ enom_error: TestEnum,
+}
+
+impl Default for Test {
+ fn default() -> Self {
+ Self {
+ field1: 13,
+ field2: String::from("field2"),
+ field3: Some(23),
+ nesting: Test2::default(),
+ flatten: Test3::default(),
+ enom_small: TestEnum::default(),
+ enom_big: TestEnum::default(),
+ enom_error: TestEnum::default(),
+ }
+ }
+}
+
+#[derive(ConfigDeserialize, Default)]
+struct Test2<T: Default> {
+ field1: T,
+ field2: Option<usize>,
+ #[config(skip)]
+ field3: usize,
+ #[config(alias = "aliased")]
+ field4: u8,
+}
+
+#[derive(ConfigDeserialize, Default)]
+struct Test3 {
+ flatty: usize,
+}
+
+#[test]
+fn config_deserialize() {
+ let logger = unsafe {
+ LOGGER = Some(Logger::default());
+ LOGGER.as_mut().unwrap()
+ };
+
+ log::set_logger(logger).unwrap();
+ log::set_max_level(log::LevelFilter::Warn);
+
+ let test: Test = serde_yaml::from_str(
+ r#"
+ field1: 3
+ field3: 32
+ nesting:
+ field1: "testing"
+ field2: None
+ field3: 99
+ aliased: 8
+ flatty: 123
+ enom_small: "one"
+ enom_big: "THREE"
+ enom_error: "HugaBuga"
+ "#,
+ )
+ .unwrap();
+
+ // Verify fields were deserialized correctly.
+ assert_eq!(test.field1, 3);
+ assert_eq!(test.field2, Test::default().field2);
+ assert_eq!(test.field3, Some(32));
+ assert_eq!(test.enom_small, TestEnum::One);
+ assert_eq!(test.enom_big, TestEnum::Three);
+ assert_eq!(test.enom_error, Test::default().enom_error);
+ assert_eq!(test.nesting.field1, Test::default().nesting.field1);
+ assert_eq!(test.nesting.field2, None);
+ assert_eq!(test.nesting.field3, Test::default().nesting.field3);
+ assert_eq!(test.nesting.field4, 8);
+ assert_eq!(test.flatten.flatty, 123);
+
+ // Verify all log messages are correct.
+ let error_logs = logger.error_logs.lock().unwrap();
+ assert_eq!(error_logs.as_slice(), [
+ "Config error: field1: invalid type: string \"testing\", expected usize",
+ "Config error: enom_error: unknown variant `HugaBuga`, expected one of `One`, `Two`, \
+ `Three`",
+ ]);
+ let warn_logs = logger.warn_logs.lock().unwrap();
+ assert_eq!(warn_logs.as_slice(), [
+ "Config warning: field1 is deprecated; use field2 instead",
+ "Config warning: enom_error is deprecated",
+ ]);
+}
+
+static mut LOGGER: Option<Logger> = None;
+
+/// Logger storing all messages for later validation.
+#[derive(Default)]
+struct Logger {
+ error_logs: Arc<Mutex<Vec<String>>>,
+ warn_logs: Arc<Mutex<Vec<String>>>,
+}
+
+impl Log for Logger {
+ fn log(&self, record: &Record) {
+ assert_eq!(record.target(), env!("CARGO_PKG_NAME"));
+
+ match record.level() {
+ Level::Error => {
+ let mut error_logs = self.error_logs.lock().unwrap();
+ error_logs.push(record.args().to_string());
+ },
+ Level::Warn => {
+ let mut warn_logs = self.warn_logs.lock().unwrap();
+ warn_logs.push(record.args().to_string());
+ },
+ _ => unreachable!(),
+ }
+ }
+
+ fn enabled(&self, _metadata: &Metadata) -> bool {
+ true
+ }
+
+ fn flush(&self) {}
+}
diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml
index 64404e64..9bdccf7b 100644
--- a/alacritty_terminal/Cargo.toml
+++ b/alacritty_terminal/Cargo.toml
@@ -8,6 +8,10 @@ readme = "../README.md"
homepage = "https://github.com/alacritty/alacritty"
edition = "2018"
+[dependencies.alacritty_config_derive]
+path = "../alacritty_config_derive"
+version = "0.1.0"
+
[dependencies]
libc = "0.2"
bitflags = "1"
diff --git a/alacritty_terminal/src/config/bell.rs b/alacritty_terminal/src/config/bell.rs
index 97010f31..825a7b1f 100644
--- a/alacritty_terminal/src/config/bell.rs
+++ b/alacritty_terminal/src/config/bell.rs
@@ -1,95 +1,45 @@
use std::time::Duration;
-use log::error;
-use serde::{Deserialize, Deserializer};
-use serde_yaml::Value;
+use alacritty_config_derive::ConfigDeserialize;
-use crate::config::{failure_default, Program, LOG_TARGET_CONFIG};
+use crate::config::Program;
use crate::term::color::Rgb;
-const DEFAULT_BELL_COLOR: Rgb = Rgb { r: 255, g: 255, b: 255 };
-
-#[serde(default)]
-#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
pub struct BellConfig {
/// Visual bell animation function.
- #[serde(deserialize_with = "failure_default")]
pub animation: BellAnimation,
- /// Visual bell duration in milliseconds.
- #[serde(deserialize_with = "failure_default")]
- duration: u16,
+ /// Command to run on bell.
+ pub command: Option<Program>,
/// Visual bell flash color.
- #[serde(deserialize_with = "deserialize_bell_color")]
pub color: Rgb,
- /// Command to run on bell.
- #[serde(deserialize_with = "deserialize_bell_command")]
- pub command: Option<Program>,
+ /// Visual bell duration in milliseconds.
+ duration: u16,
}
impl Default for BellConfig {
fn default() -> Self {
Self {
+ color: Rgb { r: 255, g: 255, b: 255 },
animation: Default::default(),
- duration: Default::default(),
command: Default::default(),
- color: DEFAULT_BELL_COLOR,
+ duration: Default::default(),
}
}
}
impl BellConfig {
- /// Visual bell duration in milliseconds.
- #[inline]
pub fn duration(&self) -> Duration {
- Duration::from_millis(u64::from(self.duration))
- }
-}
-
-fn deserialize_bell_color<'a, D>(deserializer: D) -> Result<Rgb, D::Error>
-where
- D: Deserializer<'a>,
-{
- let value = Value::deserialize(deserializer)?;
- match Rgb::deserialize(value) {
- Ok(value) => Ok(value),
- Err(err) => {
- error!(
- target: LOG_TARGET_CONFIG,
- "Problem with config: {}, using default color value {}", err, DEFAULT_BELL_COLOR
- );
-
- Ok(DEFAULT_BELL_COLOR)
- },
- }
-}
-
-fn deserialize_bell_command<'a, D>(deserializer: D) -> Result<Option<Program>, D::Error>
-where
- D: Deserializer<'a>,
-{
- // Deserialize to generic value.
- let val = Value::deserialize(deserializer)?;
-
- // Accept `None` to disable the bell command.
- if val.as_str().filter(|v| v.to_lowercase() == "none").is_some() {
- return Ok(None);
- }
-
- match Program::deserialize(val) {
- Ok(command) => Ok(Some(command)),
- Err(err) => {
- error!(target: LOG_TARGET_CONFIG, "Problem with config: {}; ignoring field", err);
- Ok(None)
- },
+ Duration::from_millis(self.duration as u64)
}
}
/// `VisualBellAnimations` are modeled after a subset of CSS transitions and Robert
/// Penner's Easing Functions.
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Clone, Copy, Debug, PartialEq, Eq)]
pub enum BellAnimation {
// CSS animation.
Ease,
diff --git a/alacritty_terminal/src/config/colors.rs b/alacritty_terminal/src/config/colors.rs
index a292fde4..df52f19f 100644
--- a/alacritty_terminal/src/config/colors.rs
+++ b/alacritty_terminal/src/config/colors.rs
@@ -1,42 +1,24 @@
-use log::error;
+use serde::de::Error as SerdeError;
use serde::{Deserialize, Deserializer};
-use serde_yaml::Value;
-use crate::config::{failure_default, LOG_TARGET_CONFIG};
+use alacritty_config_derive::ConfigDeserialize;
+
use crate::term::color::{CellRgb, Rgb};
-#[serde(default)]
-#[derive(Deserialize, Clone, Debug, Default, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Clone, Debug, Default, PartialEq, Eq)]
pub struct Colors {
- #[serde(deserialize_with = "failure_default")]
pub primary: PrimaryColors,
- #[serde(deserialize_with = "failure_default")]
- pub cursor: CursorColors,
- #[serde(deserialize_with = "failure_default")]
- pub vi_mode_cursor: CursorColors,
- #[serde(deserialize_with = "failure_default")]
+ pub cursor: InvertedCellColors,
+ pub vi_mode_cursor: InvertedCellColors,
pub selection: InvertedCellColors,
- #[serde(deserialize_with = "failure_default")]
- normal: NormalColors,
- #[serde(deserialize_with = "failure_default")]
- bright: BrightColors,
- #[serde(deserialize_with = "failure_default")]
- pub dim: Option<AnsiColors>,
- #[serde(deserialize_with = "failure_default")]
+ pub normal: NormalColors,
+ pub bright: BrightColors,
+ pub dim: Option<DimColors>,
pub indexed_colors: Vec<IndexedColor>,
- #[serde(deserialize_with = "failure_default")]
pub search: SearchColors,
}
impl Colors {
- pub fn normal(&self) -> &AnsiColors {
- &self.normal.0
- }
-
- pub fn bright(&self) -> &AnsiColors {
- &self.bright.0
- }
-
pub fn search_bar_foreground(&self) -> Rgb {
self.search.bar.foreground.unwrap_or(self.primary.background)
}
@@ -46,158 +28,88 @@ impl Colors {
}
}
-#[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq)]
-struct DefaultForegroundCellRgb(CellRgb);
-
-impl Default for DefaultForegroundCellRgb {
- fn default() -> Self {
- Self(CellRgb::CellForeground)
- }
-}
-
-#[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq)]
-struct DefaultBackgroundCellRgb(CellRgb);
-
-impl Default for DefaultBackgroundCellRgb {
- fn default() -> Self {
- Self(CellRgb::CellBackground)
- }
-}
-
-#[serde(default)]
-#[derive(Deserialize, Clone, Default, Debug, PartialEq, Eq)]
+#[derive(Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq)]
pub struct IndexedColor {
- #[serde(deserialize_with = "deserialize_color_index")]
- pub index: u8,
- #[serde(deserialize_with = "failure_default")]
pub color: Rgb,
-}
-fn deserialize_color_index<'a, D>(deserializer: D) -> Result<u8, D::Error>
-where
- D: Deserializer<'a>,
-{
- let value = Value::deserialize(deserializer)?;
- match u8::deserialize(value) {
- Ok(index) => {
- if index < 16 {
- error!(
- target: LOG_TARGET_CONFIG,
- "Problem with config: indexed_color's index is {}, but a value bigger than 15 \
- was expected; ignoring setting",
- index
- );
-
- // Return value out of range to ignore this color.
- Ok(0)
- } else {
- Ok(index)
- }
- },
- Err(err) => {
- error!(target: LOG_TARGET_CONFIG, "Problem with config: {}; ignoring setting", err);
+ index: ColorIndex,
+}
- // Return value out of range to ignore this color.
- Ok(0)
- },
+impl IndexedColor {
+ #[inline]
+ pub fn index(&self) -> u8 {
+ self.index.0
}
}
-#[serde(default)]
-#[derive(Deserialize, Debug, Copy, Clone, Default, PartialEq, Eq)]
-pub struct CursorColors {
- #[serde(deserialize_with = "failure_default")]
- text: DefaultBackgroundCellRgb,
- #[serde(deserialize_with = "failure_default")]
- cursor: DefaultForegroundCellRgb,
-}
+#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
+struct ColorIndex(u8);
-impl CursorColors {
- pub fn text(self) -> CellRgb {
- self.text.0
- }
+impl<'de> Deserialize<'de> for ColorIndex {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let index = u8::deserialize(deserializer)?;
- pub fn cursor(self) -> CellRgb {
- self.cursor.0
+ if index < 16 {
+ Err(SerdeError::custom(
+ "Config error: indexed_color's index is {}, but a value bigger than 15 was \
+ expected; ignoring setting",
+ ))
+ } else {
+ Ok(Self(index))
+ }
}
}
-#[serde(default)]
-#[derive(Deserialize, Debug, Copy, Clone, Default, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub struct InvertedCellColors {
- #[serde(deserialize_with = "failure_default", alias = "text")]
- foreground: DefaultBackgroundCellRgb,
- #[serde(deserialize_with = "failure_default")]
- background: DefaultForegroundCellRgb,
+ #[config(alias = "text")]
+ pub foreground: CellRgb,
+ #[config(alias = "cursor")]
+ pub background: CellRgb,
}
-impl InvertedCellColors {
- pub fn foreground(self) -> CellRgb {
- self.foreground.0
- }
-
- pub fn background(self) -> CellRgb {
- self.background.0
+impl Default for InvertedCellColors {
+ fn default() -> Self {
+ Self { foreground: CellRgb::CellBackground, background: CellRgb::CellForeground }
}
}
-#[serde(default)]
-#[derive(Deserialize, Debug, Copy, Clone, Default, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Debug, Copy, Clone, Default, PartialEq, Eq)]
pub struct SearchColors {
- #[serde(deserialize_with = "failure_default")]
- pub matches: MatchColors,
- #[serde(deserialize_with = "failure_default")]
pub focused_match: InvertedCellColors,
- #[serde(deserialize_with = "failure_default")]
+ pub matches: MatchColors,
bar: BarColors,
}
-#[serde(default)]
-#[derive(Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub struct MatchColors {
- #[serde(deserialize_with = "failure_default")]
pub foreground: CellRgb,
- #[serde(deserialize_with = "deserialize_match_background")]
pub background: CellRgb,
}
impl Default for MatchColors {
fn default() -> Self {
- Self { foreground: CellRgb::default(), background: default_match_background() }
+ Self {
+ background: CellRgb::Rgb(Rgb { r: 0xff, g: 0xff, b: 0xff }),
+ foreground: CellRgb::Rgb(Rgb { r: 0x00, g: 0x00, b: 0x00 }),
+ }
}
}
-fn deserialize_match_background<'a, D>(deserializer: D) -> Result<CellRgb, D::Error>
-where
- D: Deserializer<'a>,
-{
- let value = Value::deserialize(deserializer)?;
- Ok(CellRgb::deserialize(value).unwrap_or_else(|_| default_match_background()))
-}
-
-fn default_match_background() -> CellRgb {
- CellRgb::Rgb(Rgb { r: 0xff, g: 0xff, b: 0xff })
-}
-
-#[serde(default)]
-#[derive(Deserialize, Debug, Copy, Clone, Default, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Debug, Copy, Clone, Default, PartialEq, Eq)]
pub struct BarColors {
- #[serde(deserialize_with = "failure_default")]
foreground: Option<Rgb>,
- #[serde(deserialize_with = "failure_default")]
background: Option<Rgb>,
}
-#[serde(default)]
-#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
pub struct PrimaryColors {
- #[serde(deserialize_with = "failure_default")]
- pub background: Rgb,
- #[serde(deserialize_with = "failure_default")]
pub foreground: Rgb,
- #[serde(deserialize_with = "failure_default")]
+ pub background: Rgb,
pub bright_foreground: Option<Rgb>,
- #[serde(deserialize_with = "failure_default")]
pub dim_foreground: Option<Rgb>,
}
@@ -212,33 +124,21 @@ impl Default for PrimaryColors {
}
}
-/// The 8-colors sections of config.
-#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
-pub struct AnsiColors {
- #[serde(deserialize_with = "failure_default")]
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
+pub struct NormalColors {
pub black: Rgb,
- #[serde(deserialize_with = "failure_default")]
pub red: Rgb,
- #[serde(deserialize_with = "failure_default")]
pub green: Rgb,
- #[serde(deserialize_with = "failure_default")]
pub yellow: Rgb,
- #[serde(deserialize_with = "failure_default")]
pub blue: Rgb,
- #[serde(deserialize_with = "failure_default")]
pub magenta: Rgb,
- #[serde(deserialize_with = "failure_default")]
pub cyan: Rgb,
- #[serde(deserialize_with = "failure_default")]
pub white: Rgb,
}
-#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
-struct NormalColors(AnsiColors);
-
impl Default for NormalColors {
fn default() -> Self {
- NormalColors(AnsiColors {
+ NormalColors {
black: Rgb { r: 0x1d, g: 0x1f, b: 0x21 },
red: Rgb { r: 0xcc, g: 0x66, b: 0x66 },
green: Rgb { r: 0xb5, g: 0xbd, b: 0x68 },
@@ -247,16 +147,25 @@ impl Default for NormalColors {
magenta: Rgb { r: 0xb2, g: 0x94, b: 0xbb },
cyan: Rgb { r: 0x8a, g: 0xbe, b: 0xb7 },
white: Rgb { r: 0xc5, g: 0xc8, b: 0xc6 },
- })
+ }
}
}
-#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
-struct BrightColors(AnsiColors);
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
+pub struct BrightColors {
+ pub black: Rgb,
+ pub red: Rgb,
+ pub green: Rgb,
+ pub yellow: Rgb,
+ pub blue: Rgb,
+ pub magenta: Rgb,
+ pub cyan: Rgb,
+ pub white: Rgb,
+}
impl Default for BrightColors {
fn default() -> Self {
- BrightColors(AnsiColors {
+ BrightColors {
black: Rgb { r: 0x66, g: 0x66, b: 0x66 },
red: Rgb { r: 0xd5, g: 0x4e, b: 0x53 },
green: Rgb { r: 0xb9, g: 0xca, b: 0x4a },
@@ -265,6 +174,18 @@ impl Default for BrightColors {
magenta: Rgb { r: 0xc3, g: 0x97, b: 0xd8 },
cyan: Rgb { r: 0x70, g: 0xc0, b: 0xb1 },
white: Rgb { r: 0xea, g: 0xea, b: 0xea },
- })
+ }
}
}
+
+#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
+pub struct DimColors {
+ pub black: Rgb,
+ pub red: Rgb,
+ pub green: Rgb,
+ pub yellow: Rgb,
+ pub blue: Rgb,
+ pub magenta: Rgb,
+ pub cyan: Rgb,
+ pub white: Rgb,
+}
diff --git a/alacritty_terminal/src/config/mod.rs b/alacritty_terminal/src/config/mod.rs
index f3221920..cbe56057 100644
--- a/alacritty_terminal/src/config/mod.rs
+++ b/alacritty_terminal/src/config/mod.rs
@@ -1,11 +1,10 @@
use std::cmp::max;
use std::collections::HashMap;
-use std::fmt::Display;
use std::path::PathBuf;
-use log::error;
-use serde::{Deserialize, Deserializer};
-use serde_yaml::Value;
+use serde::Deserialize;
+
+use alacritty_config_derive::ConfigDeserialize;
mod bell;
mod colors;
@@ -17,132 +16,103 @@ pub use crate::config::bell::{BellAnimation, BellConfig};
pub use crate::config::colors::Colors;
pub use crate::config::scrolling::Scrolling;
-pub const LOG_TARGET_CONFIG: &str = "alacritty_config";
-const DEFAULT_CURSOR_THICKNESS: f32 = 0.15;
-const MAX_SCROLLBACK_LINES: u32 = 100_000;
+pub const LOG_TARGET_CONFIG: &str = "alacritty_config_derive";
const MIN_BLINK_INTERVAL: u64 = 10;
pub type MockConfig = Config<HashMap<String, serde_yaml::Value>>;
/// Top-level config type.
-#[derive(Debug, PartialEq, Default, Deserialize)]
+#[derive(ConfigDeserialize, Debug, PartialEq, Default)]
pub struct Config<T> {
/// TERM env variable.
- #[serde(default, deserialize_with = "failure_default")]
pub env: HashMap<String, String>,
/// Should draw bold text with brighter colors instead of bold font.
- #[serde(default, deserialize_with = "failure_default")]
- draw_bold_text_with_bright_colors: bool,
+ pub draw_bold_text_with_bright_colors: bool,
- #[serde(default, deserialize_with = "failure_default")]
pub colors: Colors,
- #[serde(default, deserialize_with = "failure_default")]
pub selection: Selection,
/// Path to a shell program to run on startup.
- #[serde(default, deserialize_with = "failure_default")]
pub shell: Option<Program>,
- /// Bell configuration.
- #[serde(default, deserialize_with = "failure_default")]
- bell: BellConfig,
-
/// How much scrolling history to keep.
- #[serde(default, deserialize_with = "failure_default")]
pub scrolling: Scrolling,
/// Cursor configuration.
- #[serde(default, deserialize_with = "failure_default")]
pub cursor: Cursor,
/// Shell startup directory.
- #[serde(default, deserialize_with = "option_explicit_none")]
pub working_directory: Option<PathBuf>,
/// Additional configuration options not directly required by the terminal.
- #[serde(flatten)]
+ #[config(flatten)]
pub ui_config: T,
/// Remain open after child process exits.
- #[serde(skip)]
+ #[config(skip)]
pub hold: bool,
- // TODO: DEPRECATED
+ /// Bell configuration.
+ bell: BellConfig,
+
#[cfg(windows)]
- #[serde(default, deserialize_with = "failure_default")]
+ #[config(deprecated = "recompile with winpty feature or remove this setting")]
pub winpty_backend: bool,
- // TODO: DEPRECATED
- #[serde(default, deserialize_with = "failure_default")]
+ #[config(deprecated = "use `bell` instead")]
pub visual_bell: Option<BellConfig>,
-
- // TODO: REMOVED
- #[serde(default, deserialize_with = "failure_default")]
- pub tabspaces: Option<usize>,
}
impl<T> Config<T> {
#[inline]
- pub fn draw_bold_text_with_bright_colors(&self) -> bool {
- self.draw_bold_text_with_bright_colors
- }
-
- #[inline]
pub fn bell(&self) -> &BellConfig {
self.visual_bell.as_ref().unwrap_or(&self.bell)
}
}
-#[serde(default)]
-#[derive(Deserialize, Default, Clone, Debug, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
pub struct Selection {
- #[serde(deserialize_with = "failure_default")]
- semantic_escape_chars: EscapeChars,
- #[serde(deserialize_with = "failure_default")]
+ pub semantic_escape_chars: String,
pub save_to_clipboard: bool,
}
-impl Selection {
- pub fn semantic_escape_chars(&self) -> &str {
- &self.semantic_escape_chars.0
- }
-}
-
-#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
-struct EscapeChars(String);
-
-impl Default for EscapeChars {
+impl Default for Selection {
fn default() -> Self {
- EscapeChars(String::from(",│`|:\"' ()[]{}<>\t"))
+ Self {
+ semantic_escape_chars: String::from(",│`|:\"' ()[]{}<>\t"),
+ save_to_clipboard: Default::default(),
+ }
}
}
-#[serde(default)]
-#[derive(Deserialize, Copy, Clone, Debug, PartialEq)]
+#[derive(ConfigDeserialize, Copy, Clone, Debug, PartialEq)]
pub struct Cursor {
- #[serde(deserialize_with = "failure_default")]
pub style: ConfigCursorStyle,
- #[serde(deserialize_with = "option_explicit_none")]
pub vi_mode_style: Option<ConfigCursorStyle>,
- #[serde(deserialize_with = "failure_default")]
- blink_interval: BlinkInterval,
- #[serde(deserialize_with = "deserialize_cursor_thickness")]
+ pub unfocused_hollow: bool,
+
thickness: Percentage,
- #[serde(deserialize_with = "failure_default")]
- unfocused_hollow: DefaultTrueBool,
+ blink_interval: u64,
}
-impl Cursor {
- #[inline]
- pub fn unfocused_hollow(self) -> bool {
- self.unfocused_hollow.0
+impl Default for Cursor {
+ fn default() -> Self {
+ Self {
+ thickness: Percentage(0.15),
+ unfocused_hollow: true,
+ blink_interval: 750,
+ style: Default::default(),
+ vi_mode_style: Default::default(),
+ }
}
+}
+impl Cursor {
#[inline]
- pub fn thickness(self) -> f64 {
- self.thickness.0 as f64
+ pub fn thickness(self) -> f32 {
+ self.thickness.as_f32()
}
#[inline]
@@ -157,48 +127,7 @@ impl Cursor {
#[inline]
pub fn blink_interval(self) -> u64 {
- max(self.blink_interval.0, MIN_BLINK_INTERVAL)
- }
-}
-
-impl Default for Cursor {
- fn default() -> Self {
- Self {
- style: Default::default(),
- vi_mode_style: Default::default(),
- thickness: Percentage::new(DEFAULT_CURSOR_THICKNESS),
- unfocused_hollow: Default::default(),
- blink_interval: Default::default(),
- }
- }
-}
-
-#[derive(Deserialize, Copy, Clone, Debug, PartialEq)]
-struct BlinkInterval(u64);
-
-impl Default for BlinkInterval {
- fn default() -> Self {
- BlinkInterval(750)
- }
-}
-
-fn deserialize_cursor_thickness<'a, D>(deserializer: D) -> Result<Percentage, D::Error>
-where
- D: Deserializer<'a>,
-{
- let value = Value::deserialize(deserializer)?;
- match Percentage::deserialize(value) {
- Ok(value) => Ok(value),
- Err(err) => {
- error!(
- target: LOG_TARGET_CONFIG,
- "Problem with config: {}, using default thickness value {}",
- err,
- DEFAULT_CURSOR_THICKNESS
- );
-
- Ok(Percentage::new(DEFAULT_CURSOR_THICKNESS))
- },
+ max(self.blink_interval, MIN_BLINK_INTERVAL)
}
}
@@ -206,17 +135,12 @@ where
#[derive(Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub enum ConfigCursorStyle {
Shape(CursorShape),
- WithBlinking {
- #[serde(default, deserialize_with = "failure_default")]
- shape: CursorShape,
- #[serde(default, deserialize_with = "failure_default")]
- blinking: CursorBlinking,
- },
+ WithBlinking { shape: CursorShape, blinking: CursorBlinking },
}
impl Default for ConfigCursorStyle {
fn default() -> Self {
- Self::WithBlinking { shape: CursorShape::default(), blinking: CursorBlinking::default() }
+ Self::Shape(CursorShape::default())
}
}
@@ -241,7 +165,7 @@ impl From<ConfigCursorStyle> for CursorStyle {
}
}
-#[derive(Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub enum CursorBlinking {
Never,
Off,
@@ -275,11 +199,7 @@ impl Into<bool> for CursorBlinking {
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum Program {
Just(String),
- WithArgs {
- program: String,
- #[serde(default, deserialize_with = "failure_default")]
- args: Vec<String>,
- },
+ WithArgs { program: String, args: Vec<String> },
}
impl Program {
@@ -299,9 +219,15 @@ impl Program {
}
/// Wrapper around f32 that represents a percentage value between 0.0 and 1.0.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Deserialize, Clone, Copy, Debug, PartialEq)]
pub struct Percentage(f32);
+impl Default for Percentage {
+ fn default() -> Self {
+ Percentage(1.0)
+ }
+}
+
impl Percentage {
pub fn new(value: f32) -> Self {
Percentage(if value < 0.0 {
@@ -317,55 +243,3 @@ impl Percentage {
self.0
}
}
-
-impl Default for Percentage {
- fn default() -> Self {
- Percentage(1.0)
- }
-}
-
-impl<'a> Deserialize<'a> for Percentage {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'a>,
- {
- Ok(Percentage::new(f32::deserialize(deserializer)?))
- }
-}
-
-#[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq)]
-struct DefaultTrueBool(bool);
-
-impl Default for DefaultTrueBool {
- fn default() -> Self {
- DefaultTrueBool(true)
- }
-}
-
-fn fallback_default<T, E>(err: E) -> T
-where
- T: Default,
- E: Display,
-{
- error!(target: LOG_TARGET_CONFIG, "Problem with config: {}; using default value", err);
- T::default()
-}
-
-pub fn failure_default<'a, D, T>(deserializer: D) -> Result<T, D::Error>
-where
- D: Deserializer<'a>,
- T: Deserialize<'a> + Default,
-{
- Ok(T::deserialize(Value::deserialize(deserializer)?).unwrap_or_else(fallback_default))
-}
-
-pub fn option_explicit_none<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
-where
- D: Deserializer<'de>,
- T: Deserialize<'de> + Default,
-{
- Ok(match Value::deserialize(deserializer)? {
- Value::String(ref value) if value.to_lowercase() == "none" => None,
- value => Some(T::deserialize(value).unwrap_or_else(fallback_default)),
- })
-}
diff --git a/alacritty_terminal/src/config/scrolling.rs b/alacritty_terminal/src/config/scrolling.rs
index 136e9389..159b0f44 100644
--- a/alacritty_terminal/src/config/scrolling.rs
+++ b/alacritty_terminal/src/config/scrolling.rs
@@ -1,24 +1,23 @@
-use log::error;
+use serde::de::Error as SerdeError;
use serde::{Deserialize, Deserializer};
-use crate::config::{failure_default, LOG_TARGET_CONFIG, MAX_SCROLLBACK_LINES};
+use alacritty_config_derive::ConfigDeserialize;
+
+/// Maximum scrollback amount configurable.
+const MAX_SCROLLBACK_LINES: u32 = 100_000;
/// Struct for scrolling related settings.
-#[serde(default)]
-#[derive(Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq)]
+#[derive(ConfigDeserialize, Copy, Clone, Debug, PartialEq, Eq)]
pub struct Scrolling {
- #[serde(deserialize_with = "failure_default")]
- history: ScrollingHistory,
- #[serde(deserialize_with = "failure_default")]
- multiplier: ScrollingMultiplier,
+ pub multiplier: u8,
- // TODO: REMOVED
- #[serde(deserialize_with = "failure_default")]
- pub auto_scroll: Option<bool>,
+ history: ScrollingHistory,
+}
- // TODO: DEPRECATED
- #[serde(deserialize_with = "failure_default")]
- faux_multiplier: Option<ScrollingMultiplier>,
+impl Default for Scrolling {
+ fn default() -> Self {
+ Self { multiplier: 3, history: Default::default() }
+ }
}
impl Scrolling {
@@ -26,29 +25,12 @@ impl Scrolling {
self.history.0
}
- pub fn multiplier(self) -> u8 {
- self.multiplier.0
- }
-
- pub fn faux_multiplier(self) -> Option<u8> {
- self.faux_multiplier.map(|sm| sm.0)
- }
-
// Update the history size, used in ref tests.
pub fn set_history(&mut self, history: u32) {
self.history = ScrollingHistory(history);
}
}
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
-struct ScrollingMultiplier(u8);
-
-impl Default for ScrollingMultiplier {
- fn default() -> Self {
- Self(3)
- }
-}
-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
struct ScrollingHistory(u32);
@@ -59,33 +41,19 @@ impl Default for ScrollingHistory {
}
impl<'de> Deserialize<'de> for ScrollingHistory {
- fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
- let value = serde_yaml::Value::deserialize(deserializer)?;
- match u32::deserialize(value) {
- Ok(lines) => {
- if lines > MAX_SCROLLBACK_LINES {
- error!(
- target: LOG_TARGET_CONFIG,
- "Problem with config: scrollback size is {}, but expected a maximum of \
- {}; using {1} instead",
- lines,
- MAX_SCROLLBACK_LINES,
- );
- Ok(ScrollingHistory(MAX_SCROLLBACK_LINES))
- } else {
- Ok(ScrollingHistory(lines))
- }
- },
- Err(err) => {
- error!(
- target: LOG_TARGET_CONFIG,
- "Problem with config: {}; using default value", err
- );
- Ok(Default::default())
- },
+ let lines = u32::deserialize(deserializer)?;
+
+ if lines > MAX_SCROLLBACK_LINES {
+ Err(SerdeError::custom(format!(
+ "exceeded maximum scrolling history ({}/{})",
+ lines, MAX_SCROLLBACK_LINES
+ )))
+ } else {
+ Ok(Self(lines))
}
}
}
diff --git a/alacritty_terminal/src/event_loop.rs b/alacritty_terminal/src/event_loop.rs
index f3d4d353..2fe3f64e 100644
--- a/alacritty_terminal/src/event_loop.rs
+++ b/alacritty_terminal/src/event_loop.rs
@@ -22,7 +22,7 @@ use crate::thread;
use crate::tty;
/// Max bytes to read from the PTY.
-const MAX_READ: usize = 0x10_000;
+const MAX_READ: usize = u16::max_value() as usize;
/// Messages that may be sent to the `EventLoop`.
#[derive(Debug)]
diff --git a/alacritty_terminal/src/selection.rs b/alacritty_terminal/src/selection.rs
index da44feac..9c3fa598 100644
--- a/alacritty_terminal/src/selection.rs
+++ b/alacritty_terminal/src/selection.rs
@@ -255,8 +255,8 @@ impl Selection {
match self.ty {
SelectionType::Simple => self.range_simple(start, end, num_cols),
SelectionType::Block => self.range_block(start, end),
- SelectionType::Semantic => Self::range_semantic(term, start.point, end.point),
- SelectionType::Lines => Self::range_lines(term, start.point, end.point),
+ SelectionType::Semantic => Some(Self::range_semantic(term, start.point, end.point)),
+ SelectionType::Lines => Some(Self::range_lines(term, start.point, end.point)),
}
}
@@ -294,7 +294,7 @@ impl Selection {
term: &Term<T>,
mut start: Point<usize>,
mut end: Point<usize>,
- ) -> Option<SelectionRange> {
+ ) -> SelectionRange {
if start == end {
if let Some(matching) = term.bracket_search(start) {
if (matching.line == start.line && matching.col < start.col)
@@ -305,25 +305,25 @@ impl Selection {
end = matching;
}
- return Some(SelectionRange { start, end, is_block: false });
+ return SelectionRange { start, end, is_block: false };
}
}
start = term.semantic_search_left(start);
end = term.semantic_search_right(end);
- Some(SelectionRange { start, end, is_block: false })
+ SelectionRange { start, end, is_block: false }
}
fn range_lines<T>(
term: &Term<T>,
mut start: Point<usize>,
mut end: Point<usize>,
- ) -> Option<SelectionRange> {
+ ) -> SelectionRange {
start = term.line_search_left(start);
end = term.line_search_right(end);
- Some(SelectionRange { start, end, is_block: false })
+ SelectionRange { start, end, is_block: false }
}
fn range_simple(
diff --git a/alacritty_terminal/src/term/color.rs b/alacritty_terminal/src/term/color.rs
index 8626cda5..88af6de6 100644
--- a/alacritty_terminal/src/term/color.rs
+++ b/alacritty_terminal/src/term/color.rs
@@ -257,24 +257,24 @@ impl<'a> From<&'a Colors> for List {
impl List {
pub fn fill_named(&mut self, colors: &Colors) {
// Normals.
- self[ansi::NamedColor::Black] = colors.normal().black;
- self[ansi::NamedColor::Red] = colors.normal().red;
- self[ansi::NamedColor::Green] = colors.normal().green;
- self[ansi::NamedColor::Yellow] = colors.normal().yellow;
- self[ansi::NamedColor::Blue] = colors.normal().blue;
- self[ansi::NamedColor::Magenta] = colors.normal().magenta;
- self[ansi::NamedColor::Cyan] = colors.normal().cyan;
- self[ansi::NamedColor::White] = colors.normal().white;
+ self[ansi::NamedColor::Black] = colors.normal.black;
+ self[ansi::NamedColor::Red] = colors.normal.red;
+ self[ansi::NamedColor::Green] = colors.normal.green;
+ self[ansi::NamedColor::Yellow] = colors.normal.yellow;
+ self[ansi::NamedColor::Blue] = colors.normal.blue;
+ self[ansi::NamedColor::Magenta] = colors.normal.magenta;
+ self[ansi::NamedColor::Cyan] = colors.normal.cyan;
+ self[ansi::NamedColor::White] = colors.normal.white;
// Brights.
- self[ansi::NamedColor::BrightBlack] = colors.bright().black;
- self[ansi::NamedColor::BrightRed] = colors.bright().red;
- self[ansi::NamedColor::BrightGreen] = colors.bright().green;
- self[ansi::NamedColor::BrightYellow] = colors.bright().yellow;
- self[ansi::NamedColor::BrightBlue] = colors.bright().blue;
- self[ansi::NamedColor::BrightMagenta] = colors.bright().magenta;
- self[ansi::NamedColor::BrightCyan] = colors.bright().cyan;
- self[ansi::NamedColor::BrightWhite] = colors.bright().white;
+ self[ansi::NamedColor::BrightBlack] = colors.bright.black;
+ self[ansi::NamedColor::BrightRed] = colors.bright.red;
+ self[ansi::NamedColor::BrightGreen] = colors.bright.green;
+ self[ansi::NamedColor::BrightYellow] = colors.bright.yellow;
+ self[ansi::NamedColor::BrightBlue] = colors.bright.blue;
+ self[ansi::NamedColor::BrightMagenta] = colors.bright.magenta;
+ self[ansi::NamedColor::BrightCyan] = colors.bright.cyan;
+ self[ansi::NamedColor::BrightWhite] = colors.bright.white;
self[ansi::NamedColor::BrightForeground] =
colors.primary.bright_foreground.unwrap_or(colors.primary.foreground);
@@ -299,14 +299,14 @@ impl List {
},
None => {
trace!("Deriving dim colors from normal colors");
- self[ansi::NamedColor::DimBlack] = colors.normal().black * DIM_FACTOR;
- self[ansi::NamedColor::DimRed] = colors.normal().red * DIM_FACTOR;
- self[ansi::NamedColor::DimGreen] = colors.normal().green * DIM_FACTOR;
- self[ansi::NamedColor::DimYellow] = colors.normal().yellow * DIM_FACTOR;
- self[ansi::NamedColor::DimBlue] = colors.normal().blue * DIM_FACTOR;
- self[ansi::NamedColor::DimMagenta] = colors.normal().magenta * DIM_FACTOR;
- self[ansi::NamedColor::DimCyan] = colors.normal().cyan * DIM_FACTOR;
- self[ansi::NamedColor::DimWhite] = colors.normal().white * DIM_FACTOR;
+ self[ansi::NamedColor::DimBlack] = colors.normal.black * DIM_FACTOR;
+ self[ansi::NamedColor::DimRed] = colors.normal.red * DIM_FACTOR;
+ self[ansi::NamedColor::DimGreen] = colors.normal.green * DIM_FACTOR;
+ self[ansi::NamedColor::DimYellow] = colors.normal.yellow * DIM_FACTOR;
+ self[ansi::NamedColor::DimBlue] = colors.normal.blue * DIM_FACTOR;
+ self[ansi::NamedColor::DimMagenta] = colors.normal.magenta * DIM_FACTOR;
+ self[ansi::NamedColor::DimCyan] = colors.normal.cyan * DIM_FACTOR;
+ self[ansi::NamedColor::DimWhite] = colors.normal.white * DIM_FACTOR;
},
}
}
@@ -319,7 +319,7 @@ impl List {
for b in 0..6 {
// Override colors 16..232 with the config (if present).
if let Some(indexed_color) =
- colors.indexed_colors.iter().find(|ic| ic.index == index as u8)
+ colors.indexed_colors.iter().find(|ic| ic.index() == index as u8)
{
self[index] = indexed_color.color;
} else {
@@ -346,7 +346,7 @@ impl List {
// Override colors 232..256 with the config (if present).
if let Some(indexed_color) =
- colors.indexed_colors.iter().find(|ic| ic.index == color_index)
+ colors.indexed_colors.iter().find(|ic| ic.index() == color_index)
{
self[index] = indexed_color.color;
index += 1;
diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs
index 154a24a2..64493bd9 100644
--- a/alacritty_terminal/src/term/mod.rs
+++ b/alacritty_terminal/src/term/mod.rs
@@ -157,7 +157,7 @@ impl<'a, C> Iterator for RenderableCellsIter<'a, C> {
if self.cursor.rendered {
return self.next_cursor_cell();
} else {
- return self.next_cursor();
+ return Some(self.next_cursor());
}
} else {
// Handle non-cursor cells.
@@ -213,7 +213,7 @@ impl<'a, C> RenderableCellsIter<'a, C> {
}
/// Get the next renderable cell as the cursor.
- fn next_cursor(&mut self) -> Option<RenderableCell> {
+ fn next_cursor(&mut self) -> RenderableCell {
// Handle cursor.
self.cursor.rendered = true;
@@ -236,7 +236,7 @@ impl<'a, C> RenderableCellsIter<'a, C> {
cell.fg = self.cursor.cursor_color.color(cell.fg, cell.bg);
}
- Some(cell)
+ cell
}
/// Check selection state of a cell.
@@ -323,8 +323,8 @@ impl RenderableCell {
let mut is_match = false;
if iter.is_selected(point) {
- let config_bg = iter.config.colors.selection.background();
- let selected_fg = iter.config.colors.selection.foreground().color(fg_rgb, bg_rgb);
+ let config_bg = iter.config.colors.selection.background;
+ let selected_fg = iter.config.colors.selection.foreground.color(fg_rgb, bg_rgb);
bg_rgb = config_bg.color(fg_rgb, bg_rgb);
fg_rgb = selected_fg;
@@ -377,7 +377,7 @@ impl RenderableCell {
_ => rgb,
},
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
@@ -395,7 +395,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,
) {
@@ -851,7 +851,7 @@ impl<T> Term<T> {
colors,
color_modified: [false; color::COUNT],
original_colors: colors,
- semantic_escape_chars: config.selection.semantic_escape_chars().to_owned(),
+ semantic_escape_chars: config.selection.semantic_escape_chars.to_owned(),
cursor_style: None,
default_cursor_style: config.cursor.style(),
vi_mode_cursor_style: config.cursor.vi_mode_style(),
@@ -870,7 +870,7 @@ impl<T> Term<T> {
where
T: EventListener,
{
- self.semantic_escape_chars = config.selection.semantic_escape_chars().to_owned();
+ self.semantic_escape_chars = config.selection.semantic_escape_chars.to_owned();
self.original_colors.fill_named(&config.colors);
self.original_colors.fill_cube(&config.colors);
self.original_colors.fill_gray_ramp(&config.colors);
@@ -880,9 +880,6 @@ impl<T> Term<T> {
}
}
self.visual_bell.update_config(config);
- if let Some(0) = config.scrolling.faux_multiplier() {
- self.mode.remove(TermMode::ALTERNATE_SCROLL);
- }
self.default_cursor_style = config.cursor.style();
self.vi_mode_cursor_style = config.cursor.vi_mode_style();
@@ -1418,7 +1415,7 @@ impl<T> Term<T> {
let cursor_shape = if hidden {
point.line = Line(0);
CursorShape::Hidden
- } else if !self.is_focused && config.cursor.unfocused_hollow() {
+ } else if !self.is_focused && config.cursor.unfocused_hollow {
CursorShape::HollowBlock
} else {
let cursor_style = self.cursor_style.unwrap_or(self.default_cursor_style);
@@ -1435,9 +1432,9 @@ impl<T> Term<T> {
let cursor_color = if self.color_modified[NamedColor::Cursor as usize] {
CellRgb::Rgb(self.colors[NamedColor::Cursor])
} else {
- color.cursor()
+ color.background
};
- let text_color = color.text();
+ let text_color = color.foreground;
// Expand across wide cell when inside wide char or spacer.
let buffer_point = self.visible_to_buffer(point);