diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config/bindings.rs | 209 | ||||
-rw-r--r-- | src/config/mod.rs (renamed from src/config.rs) | 788 | ||||
-rw-r--r-- | src/input.rs | 46 | ||||
-rw-r--r-- | src/renderer/mod.rs | 8 | ||||
-rw-r--r-- | src/term/mod.rs | 6 | ||||
-rw-r--r-- | src/url.rs | 2 |
6 files changed, 717 insertions, 342 deletions
diff --git a/src/config/bindings.rs b/src/config/bindings.rs new file mode 100644 index 00000000..701fadc1 --- /dev/null +++ b/src/config/bindings.rs @@ -0,0 +1,209 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use glutin::{MouseButton, ModifiersState}; + +use crate::input::{MouseBinding, KeyBinding, Action}; +use crate::term::TermMode; +use super::Key; + +macro_rules! bindings { + ( + $ty:ident; + $( + $key:path + $(,[$($mod:ident: $enabled:expr),*])* + $(,+$mode:expr)* + $(,~$notmode:expr)* + ;$action:expr + );* + $(;)* + ) => {{ + let mut v = Vec::new(); + + $( + let mut _mods = ModifiersState { + $($($mod: $enabled),*,)* + ..Default::default() + }; + let mut _mode = TermMode::empty(); + $(_mode = $mode;)* + let mut _notmode = TermMode::empty(); + $(_notmode = $notmode;)* + + v.push($ty { + trigger: $key, + mods: _mods, + mode: _mode, + notmode: _notmode, + action: $action, + ..Default::default() + }); + )* + + v + }} +} + +pub fn default_mouse_bindings() -> Vec<MouseBinding> { + bindings!( + MouseBinding; + MouseButton::Middle; Action::PasteSelection; + ) +} + +pub fn default_key_bindings() -> Vec<KeyBinding> { + let mut bindings = bindings!( + KeyBinding; + Key::Paste; Action::Paste; + Key::Copy; Action::Copy; + Key::L, [ctrl: true]; Action::ClearLogNotice; + Key::L, [ctrl: true]; Action::Esc("\x0c".into()); + Key::Home, +TermMode::APP_CURSOR; Action::Esc("\x1bOH".into()); + Key::Home, ~TermMode::APP_CURSOR; Action::Esc("\x1b[H".into()); + Key::End, +TermMode::APP_CURSOR; Action::Esc("\x1bOF".into()); + Key::End, ~TermMode::APP_CURSOR; Action::Esc("\x1b[F".into()); + Key::PageUp, [shift: true]; Action::Esc("\x1b[5;2~".into()); + Key::PageUp, [ctrl: true]; Action::Esc("\x1b[5;5~".into()); + Key::PageUp; Action::Esc("\x1b[5~".into()); + Key::PageDown, [shift: true]; Action::Esc("\x1b[6;2~".into()); + Key::PageDown, [ctrl: true]; Action::Esc("\x1b[6;5~".into()); + Key::PageDown; Action::Esc("\x1b[6~".into()); + Key::Tab, [shift: true]; Action::Esc("\x1b[Z".into()); + Key::Back; Action::Esc("\x7f".into()); + Key::Back, [alt: true]; Action::Esc("\x1b\x7f".into()); + Key::Insert; Action::Esc("\x1b[2~".into()); + Key::Delete; Action::Esc("\x1b[3~".into()); + Key::Left, [shift: true]; Action::Esc("\x1b[1;2D".into()); + Key::Left, [ctrl: true]; Action::Esc("\x1b[1;5D".into()); + Key::Left, [alt: true]; Action::Esc("\x1b[1;3D".into()); + Key::Left, ~TermMode::APP_CURSOR; Action::Esc("\x1b[D".into()); + Key::Left, +TermMode::APP_CURSOR; Action::Esc("\x1bOD".into()); + Key::Right, [shift: true]; Action::Esc("\x1b[1;2C".into()); + Key::Right, [ctrl: true]; Action::Esc("\x1b[1;5C".into()); + Key::Right, [alt: true]; Action::Esc("\x1b[1;3C".into()); + Key::Right, ~TermMode::APP_CURSOR; Action::Esc("\x1b[C".into()); + Key::Right, +TermMode::APP_CURSOR; Action::Esc("\x1bOC".into()); + Key::Up, [shift: true]; Action::Esc("\x1b[1;2A".into()); + Key::Up, [ctrl: true]; Action::Esc("\x1b[1;5A".into()); + Key::Up, [alt: true]; Action::Esc("\x1b[1;3A".into()); + Key::Up, ~TermMode::APP_CURSOR; Action::Esc("\x1b[A".into()); + Key::Up, +TermMode::APP_CURSOR; Action::Esc("\x1bOA".into()); + Key::Down, [shift: true]; Action::Esc("\x1b[1;2B".into()); + Key::Down, [ctrl: true]; Action::Esc("\x1b[1;5B".into()); + Key::Down, [alt: true]; Action::Esc("\x1b[1;3B".into()); + Key::Down, ~TermMode::APP_CURSOR; Action::Esc("\x1b[B".into()); + Key::Down, +TermMode::APP_CURSOR; Action::Esc("\x1bOB".into()); + Key::F1; Action::Esc("\x1bOP".into()); + Key::F2; Action::Esc("\x1bOQ".into()); + Key::F3; Action::Esc("\x1bOR".into()); + Key::F4; Action::Esc("\x1bOS".into()); + Key::F5; Action::Esc("\x1b[15~".into()); + Key::F6; Action::Esc("\x1b[17~".into()); + Key::F7; Action::Esc("\x1b[18~".into()); + Key::F8; Action::Esc("\x1b[19~".into()); + Key::F9; Action::Esc("\x1b[20~".into()); + Key::F10; Action::Esc("\x1b[21~".into()); + Key::F11; Action::Esc("\x1b[23~".into()); + Key::F12; Action::Esc("\x1b[24~".into()); + Key::F1, [shift: true]; Action::Esc("\x1b[1;2P".into()); + Key::F2, [shift: true]; Action::Esc("\x1b[1;2Q".into()); + Key::F3, [shift: true]; Action::Esc("\x1b[1;2R".into()); + Key::F4, [shift: true]; Action::Esc("\x1b[1;2S".into()); + Key::F5, [shift: true]; Action::Esc("\x1b[15;2~".into()); + Key::F6, [shift: true]; Action::Esc("\x1b[17;2~".into()); + Key::F7, [shift: true]; Action::Esc("\x1b[18;2~".into()); + Key::F8, [shift: true]; Action::Esc("\x1b[19;2~".into()); + Key::F9, [shift: true]; Action::Esc("\x1b[20;2~".into()); + Key::F10, [shift: true]; Action::Esc("\x1b[21;2~".into()); + Key::F11, [shift: true]; Action::Esc("\x1b[23;2~".into()); + Key::F12, [shift: true]; Action::Esc("\x1b[24;2~".into()); + Key::F1, [ctrl: true]; Action::Esc("\x1b[1;5P".into()); + Key::F2, [ctrl: true]; Action::Esc("\x1b[1;5Q".into()); + Key::F3, [ctrl: true]; Action::Esc("\x1b[1;5R".into()); + Key::F4, [ctrl: true]; Action::Esc("\x1b[1;5S".into()); + Key::F5, [ctrl: true]; Action::Esc("\x1b[15;5~".into()); + Key::F6, [ctrl: true]; Action::Esc("\x1b[17;5~".into()); + Key::F7, [ctrl: true]; Action::Esc("\x1b[18;5~".into()); + Key::F8, [ctrl: true]; Action::Esc("\x1b[19;5~".into()); + Key::F9, [ctrl: true]; Action::Esc("\x1b[20;5~".into()); + Key::F10, [ctrl: true]; Action::Esc("\x1b[21;5~".into()); + Key::F11, [ctrl: true]; Action::Esc("\x1b[23;5~".into()); + Key::F12, [ctrl: true]; Action::Esc("\x1b[24;5~".into()); + Key::F1, [alt: true]; Action::Esc("\x1b[1;6P".into()); + Key::F2, [alt: true]; Action::Esc("\x1b[1;6Q".into()); + Key::F3, [alt: true]; Action::Esc("\x1b[1;6R".into()); + Key::F4, [alt: true]; Action::Esc("\x1b[1;6S".into()); + Key::F5, [alt: true]; Action::Esc("\x1b[15;6~".into()); + Key::F6, [alt: true]; Action::Esc("\x1b[17;6~".into()); + Key::F7, [alt: true]; Action::Esc("\x1b[18;6~".into()); + Key::F8, [alt: true]; Action::Esc("\x1b[19;6~".into()); + Key::F9, [alt: true]; Action::Esc("\x1b[20;6~".into()); + Key::F10, [alt: true]; Action::Esc("\x1b[21;6~".into()); + Key::F11, [alt: true]; Action::Esc("\x1b[23;6~".into()); + Key::F12, [alt: true]; Action::Esc("\x1b[24;6~".into()); + Key::F1, [logo: true]; Action::Esc("\x1b[1;3P".into()); + Key::F2, [logo: true]; Action::Esc("\x1b[1;3Q".into()); + Key::F3, [logo: true]; Action::Esc("\x1b[1;3R".into()); + Key::F4, [logo: true]; Action::Esc("\x1b[1;3S".into()); + Key::F5, [logo: true]; Action::Esc("\x1b[15;3~".into()); + Key::F6, [logo: true]; Action::Esc("\x1b[17;3~".into()); + Key::F7, [logo: true]; Action::Esc("\x1b[18;3~".into()); + Key::F8, [logo: true]; Action::Esc("\x1b[19;3~".into()); + Key::F9, [logo: true]; Action::Esc("\x1b[20;3~".into()); + Key::F10, [logo: true]; Action::Esc("\x1b[21;3~".into()); + Key::F11, [logo: true]; Action::Esc("\x1b[23;3~".into()); + Key::F12, [logo: true]; Action::Esc("\x1b[24;3~".into()); + Key::NumpadEnter; Action::Esc("\n".into()); + ); + + bindings.extend(platform_key_bindings()); + + bindings +} + +#[cfg(not(any(target_os = "macos", test)))] +pub fn platform_key_bindings() -> Vec<KeyBinding> { + bindings!( + KeyBinding; + Key::V, [ctrl: true, shift: true]; Action::Paste; + Key::C, [ctrl: true, shift: true]; Action::Copy; + Key::Insert, [shift: true]; Action::PasteSelection; + Key::Key0, [ctrl: true]; Action::ResetFontSize; + Key::Equals, [ctrl: true]; Action::IncreaseFontSize; + Key::Subtract, [ctrl: true]; Action::DecreaseFontSize; + ) +} + +#[cfg(all(target_os = "macos", not(test)))] +pub fn platform_key_bindings() -> Vec<KeyBinding> { + bindings!( + KeyBinding; + Key::Key0, [logo: true]; Action::ResetFontSize; + Key::Equals, [logo: true]; Action::IncreaseFontSize; + Key::Minus, [logo: true]; Action::DecreaseFontSize; + Key::K, [logo: true]; Action::ClearHistory; + Key::K, [logo: true]; Action::Esc("\x0c".into()); + Key::V, [logo: true]; Action::Paste; + Key::C, [logo: true]; Action::Copy; + Key::H, [logo: true]; Action::Hide; + Key::Q, [logo: true]; Action::Quit; + Key::W, [logo: true]; Action::Quit; + ) +} + +// Don't return any bindings for tests since they are commented-out by default +#[cfg(test)] +pub fn platform_key_bindings() -> Vec<KeyBinding> { + vec![] +} diff --git a/src/config.rs b/src/config/mod.rs index 200acbfe..fd40776d 100644 --- a/src/config.rs +++ b/src/config/mod.rs @@ -20,7 +20,6 @@ use serde::{self, de, Deserialize}; use serde::de::Error as SerdeError; use serde::de::{Visitor, MapAccess, Unexpected}; use notify::{Watcher, watcher, DebouncedEvent, RecursiveMode}; - use glutin::ModifiersState; use crate::cli::Options; @@ -28,32 +27,50 @@ use crate::input::{Action, Binding, MouseBinding, KeyBinding}; use crate::index::{Line, Column}; use crate::ansi::{CursorStyle, NamedColor, Color}; -const MAX_SCROLLBACK_LINES: u32 = 100_000; +mod bindings; -/// Function that returns true for serde default -fn true_bool() -> bool { - true -} +const MAX_SCROLLBACK_LINES: u32 = 100_000; +static DEFAULT_ALACRITTY_CONFIG: &'static str = + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty.yml")); -#[derive(Clone, Debug, Deserialize)] +#[serde(default)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] pub struct Selection { + #[serde(deserialize_with = "deserialize_escape_chars")] pub semantic_escape_chars: String, - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub save_to_clipboard: bool, } impl Default for Selection { fn default() -> Selection { Selection { - semantic_escape_chars: String::new(), - save_to_clipboard: false + semantic_escape_chars: default_escape_chars(), + save_to_clipboard: Default::default(), } } } -#[derive(Clone, Debug, Deserialize)] +fn deserialize_escape_chars<'a, D>(deserializer: D) -> ::std::result::Result<String, D::Error> + where D: de::Deserializer<'a> +{ + match String::deserialize(deserializer) { + Ok(escape_chars) => Ok(escape_chars), + Err(err) => { + error!("Problem with config: {}; using default value", err); + Ok(default_escape_chars()) + }, + } +} + +fn default_escape_chars() -> String { + String::from(",│`|:\"' ()[]{}<>") +} + +#[serde(default)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] pub struct ClickHandler { - #[serde(deserialize_with="deserialize_duration_ms")] + #[serde(deserialize_with = "deserialize_duration_ms")] pub threshold: Duration, } @@ -79,58 +96,57 @@ fn deserialize_duration_ms<'a, D>(deserializer: D) -> ::std::result::Result<Dura } } -#[derive(Clone, Debug, Deserialize)] +#[serde(default)] +#[derive(Default, Clone, Debug, Deserialize, PartialEq, Eq)] pub struct Mouse { - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub double_click: ClickHandler, - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub triple_click: ClickHandler, - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub hide_when_typing: bool, - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub url: Url, // TODO: DEPRECATED - #[serde(default)] pub faux_scrollback_lines: Option<usize>, } -#[derive(Default, Clone, Debug, Deserialize)] +#[serde(default)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] pub struct Url { // Program for opening links - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub launcher: Option<CommandWrapper>, // Modifier used to open links - #[serde(default, deserialize_with = "deserialize_modifiers")] + #[serde(deserialize_with = "deserialize_modifiers")] pub modifiers: ModifiersState, } +impl Default for Url { + fn default() -> Url { + Url { + #[cfg(not(any(target_os = "macos", windows)))] + launcher: Some(CommandWrapper::Just(String::from("xdg-open"))), + #[cfg(target_os = "macos")] + launcher: Some(CommandWrapper::Just(String::from("open"))), + #[cfg(windows)] + launcher: Some(CommandWrapper::Just(String::from("explorer"))), + modifiers: Default::default(), + } + } +} + fn deserialize_modifiers<'a, D>(deserializer: D) -> ::std::result::Result<ModifiersState, D::Error> where D: de::Deserializer<'a> { ModsWrapper::deserialize(deserializer).map(|wrapper| wrapper.into_inner()) } -impl Default for Mouse { - fn default() -> Mouse { - Mouse { - double_click: ClickHandler { - threshold: Duration::from_millis(300), - }, - triple_click: ClickHandler { - threshold: Duration::from_millis(300), - }, - hide_when_typing: false, - url: Url::default(), - faux_scrollback_lines: None, - } - } -} - /// `VisualBellAnimations` are modeled after a subset of CSS transitions and Robert /// Penner's Easing Functions. -#[derive(Clone, Copy, Debug, Deserialize)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)] pub enum VisualBellAnimation { Ease, // CSS EaseOut, // CSS @@ -150,30 +166,29 @@ impl Default for VisualBellAnimation { } } -#[derive(Debug, Deserialize)] +#[serde(default)] +#[derive(Debug, Deserialize, PartialEq, Eq)] pub struct VisualBellConfig { /// Visual bell animation function - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] animation: VisualBellAnimation, /// Visual bell duration in milliseconds - #[serde(default, deserialize_with = "deserialize_visual_bell_duration")] + #[serde(deserialize_with = "failure_default")] duration: u16, /// Visual bell flash color - #[serde(default="default_visual_bell_color", deserialize_with = "rgb_from_hex")] + #[serde(deserialize_with = "rgb_from_hex")] color: Rgb, } -fn deserialize_visual_bell_duration<'a, D>(deserializer: D) -> ::std::result::Result<u16, D::Error> - where D: de::Deserializer<'a> -{ - match u16::deserialize(deserializer) { - Ok(duration) => Ok(duration), - Err(err) => { - error!("Problem with config: {}; using default value", err); - Ok(0) - }, +impl Default for VisualBellConfig { + fn default() -> VisualBellConfig { + VisualBellConfig { + animation: Default::default(), + duration: Default::default(), + color: default_visual_bell_color(), + } } } @@ -201,17 +216,7 @@ impl VisualBellConfig { } } -impl Default for VisualBellConfig { - fn default() -> VisualBellConfig { - VisualBellConfig { - animation: VisualBellAnimation::default(), - color: default_visual_bell_color(), - duration: 0, - } - } -} - -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, PartialEq, Eq)] pub struct Shell<'a> { program: Cow<'a, str>, @@ -248,7 +253,7 @@ impl<'a> Shell<'a> { } /// Wrapper around f32 that represents an alpha value between 0.0 and 1.0 -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Alpha(f32); impl Alpha { @@ -282,7 +287,7 @@ impl Default for Alpha { } } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Decorations { Full, Transparent, @@ -373,29 +378,42 @@ impl<'de> Deserialize<'de> for Decorations { } } -#[derive(Debug, Copy, Clone, Deserialize)] +#[serde(default)] +#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Eq)] pub struct WindowConfig { /// Initial dimensions #[serde(default, deserialize_with = "failure_default")] dimensions: Dimensions, /// Pixel padding - #[serde(default="default_padding", deserialize_with = "deserialize_padding")] + #[serde(deserialize_with = "deserialize_padding")] padding: Delta<u8>, /// Draw the window with title bar / borders - #[serde(default)] + #[serde(deserialize_with = "failure_default")] decorations: Decorations, /// Spread out additional padding evenly - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] dynamic_padding: bool, /// Start maximized - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] start_maximized: bool, } +impl Default for WindowConfig { + fn default() -> Self { + WindowConfig{ + dimensions: Default::default(), + padding: default_padding(), + decorations: Default::default(), + dynamic_padding: Default::default(), + start_maximized: Default::default(), + } + } +} + fn default_padding() -> Delta<u8> { Delta { x: 2, y: 2 } } @@ -426,20 +444,8 @@ impl WindowConfig { } } -impl Default for WindowConfig { - fn default() -> Self { - WindowConfig{ - dimensions: Default::default(), - padding: default_padding(), - decorations: Default::default(), - dynamic_padding: false, - start_maximized: false, - } - } -} - /// Top-level config type -#[derive(Debug, Deserialize)] +#[derive(Debug, PartialEq, Deserialize)] pub struct Config { /// Initial dimensions #[serde(default, deserialize_with = "failure_default")] @@ -462,7 +468,7 @@ pub struct Config { render_timer: bool, /// Should draw bold text with brighter colors instead of bold font - #[serde(default="true_bool", deserialize_with = "default_true_bool")] + #[serde(default = "default_true_bool", deserialize_with = "deserialize_true_bool")] draw_bold_text_with_bright_colors: bool, #[serde(default, deserialize_with = "failure_default")] @@ -477,11 +483,11 @@ pub struct Config { window: WindowConfig, /// Keybindings - #[serde(default, deserialize_with = "failure_default_vec")] + #[serde(default="default_key_bindings", deserialize_with = "deserialize_key_bindings")] key_bindings: Vec<KeyBinding>, /// Bindings for the mouse - #[serde(default, deserialize_with = "failure_default_vec")] + #[serde(default="default_mouse_bindings", deserialize_with = "deserialize_mouse_bindings")] mouse_bindings: Vec<MouseBinding>, #[serde(default, deserialize_with = "failure_default")] @@ -503,50 +509,95 @@ pub struct Config { visual_bell: VisualBellConfig, /// Use dynamic title - #[serde(default="true_bool", deserialize_with = "default_true_bool")] + #[serde(default = "default_true_bool", deserialize_with = "deserialize_true_bool")] dynamic_title: bool, /// Live config reload - #[serde(default="true_bool", deserialize_with = "default_true_bool")] + #[serde(default = "default_true_bool", deserialize_with = "deserialize_true_bool")] live_config_reload: bool, /// Number of spaces in one tab - #[serde(default="default_tabspaces", deserialize_with = "deserialize_tabspaces")] + #[serde(default = "default_tabspaces", deserialize_with = "deserialize_tabspaces")] tabspaces: usize, /// How much scrolling history to keep - #[serde(default, deserialize_with="failure_default")] + #[serde(default, deserialize_with = "failure_default")] scrolling: Scrolling, /// Cursor configuration - #[serde(default, deserialize_with="failure_default")] + #[serde(default, deserialize_with = "failure_default")] cursor: Cursor, /// Keep the log file after quitting - #[serde(default, deserialize_with="failure_default")] + #[serde(default, deserialize_with = "failure_default")] persistent_logging: bool, - // TODO: DEPRECATED + /// Enable experimental conpty backend instead of using winpty. + /// Will only take effect on Windows 10 Oct 2018 and later. + #[cfg(windows)] #[serde(default, deserialize_with = "failure_default")] + enable_experimental_conpty_backend: bool, + + // TODO: DEPRECATED custom_cursor_colors: Option<bool>, // TODO: DEPRECATED - #[serde(default, deserialize_with = "failure_default")] hide_cursor_when_typing: Option<bool>, // TODO: DEPRECATED - #[serde(default, deserialize_with = "failure_default")] cursor_style: Option<CursorStyle>, // TODO: DEPRECATED - #[serde(default, deserialize_with = "failure_default")] unfocused_hollow_cursor: Option<bool>, +} - /// Enable experimental conpty backend instead of using winpty. - /// Will only take effect on Windows 10 Oct 2018 and later. - #[cfg(windows)] - #[serde(default, deserialize_with="failure_default")] - enable_experimental_conpty_backend: bool +impl Default for Config { + fn default() -> Self { + serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG) + .expect("default config is invalid") + } +} + +fn default_key_bindings() -> Vec<KeyBinding> { + bindings::default_key_bindings() +} + +fn default_mouse_bindings() -> Vec<MouseBinding> { + bindings::default_mouse_bindings() +} + +fn deserialize_key_bindings<'a, D>(deserializer: D) + -> ::std::result::Result<Vec<KeyBinding>, D::Error> +where + D: de::Deserializer<'a>, +{ + deserialize_bindings(deserializer, bindings::default_key_bindings()) +} + +fn deserialize_mouse_bindings<'a, D>(deserializer: D) + -> ::std::result::Result<Vec<MouseBinding>, D::Error> +where + D: de::Deserializer<'a>, +{ + deserialize_bindings(deserializer, bindings::default_mouse_bindings()) +} + +fn deserialize_bindings<'a, D, T>(deserializer: D, mut default: Vec<Binding<T>>) + -> ::std::result::Result<Vec<Binding<T>>, D::Error> +where + D: de::Deserializer<'a>, + T: Copy + Eq + std::hash::Hash + std::fmt::Debug, + Binding<T>: de::Deserialize<'a>, +{ + let mut bindings: Vec<Binding<T>> = failure_default_vec(deserializer)?; + + for binding in bindings.iter() { + default.retain(|b| !b.triggers_match(binding)); + } + + bindings.extend(default); + + Ok(bindings) } fn failure_default_vec<'a, D, T>(deserializer: D) -> ::std::result::Result<Vec<T>, D::Error> @@ -592,7 +643,7 @@ fn deserialize_tabspaces<'a, D>(deserializer: D) -> ::std::result::Result<usize, } } -fn default_true_bool<'a, D>(deserializer: D) -> ::std::result::Result<bool, D::Error> +fn deserialize_true_bool<'a, D>(deserializer: D) -> ::std::result::Result<bool, D::Error> where D: de::Deserializer<'a> { match bool::deserialize(deserializer) { @@ -604,6 +655,10 @@ fn default_true_bool<'a, D>(deserializer: D) -> ::std::result::Result<bool, D::E } } +fn default_true_bool() -> bool { + true +} + fn failure_default<'a, D, T>(deserializer: D) -> ::std::result::Result<T, D::Error> where D: de::Deserializer<'a>, @@ -618,56 +673,40 @@ fn failure_default<'a, D, T>(deserializer: D) } } -#[cfg(not(any(windows, target_os="macos")))] -static DEFAULT_ALACRITTY_CONFIG: &'static str = include_str!("../alacritty.yml"); -#[cfg(target_os="macos")] -static DEFAULT_ALACRITTY_CONFIG: &'static str = include_str!("../alacritty_macos.yml"); -#[cfg(windows)] -static DEFAULT_ALACRITTY_CONFIG: &'static str = include_str!("../alacritty_windows.yml"); - -impl Default for Config { - fn default() -> Self { - serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG) - .expect("default config is invalid") - } -} - /// Struct for scrolling related settings -#[derive(Copy, Clone, Debug, Deserialize)] +#[serde(default)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)] pub struct Scrolling { - #[serde(deserialize_with="deserialize_scrolling_history")] - #[serde(default="default_scrolling_history")] + #[serde(deserialize_with = "deserialize_scrolling_history")] pub history: u32, - #[serde(deserialize_with="deserialize_scrolling_multiplier")] - #[serde(default="default_scrolling_multiplier")] + #[serde(deserialize_with = "deserialize_scrolling_multiplier")] pub multiplier: u8, - #[serde(deserialize_with="deserialize_scrolling_multiplier")] - #[serde(default="default_scrolling_multiplier")] + #[serde(deserialize_with = "deserialize_scrolling_multiplier")] pub faux_multiplier: u8, - #[serde(default, deserialize_with="failure_default")] + #[serde(deserialize_with = "failure_default")] pub auto_scroll: bool, } -fn default_scrolling_history() -> u32 { - 10_000 -} - -// Default for normal and faux scrolling -fn default_scrolling_multiplier() -> u8 { - 3 -} - impl Default for Scrolling { fn default() -> Self { Self { history: default_scrolling_history(), multiplier: default_scrolling_multiplier(), faux_multiplier: default_scrolling_multiplier(), - auto_scroll: false, + auto_scroll: Default::default(), } } } +fn default_scrolling_history() -> u32 { + 10_000 +} + +// Default for normal and faux scrolling +fn default_scrolling_multiplier() -> u8 { + 3 +} + fn deserialize_scrolling_history<'a, D>(deserializer: D) -> ::std::result::Result<u32, D::Error> where D: de::Deserializer<'a> { @@ -739,6 +778,7 @@ impl<'a> de::Deserialize<'a> for ModsWrapper { "Shift" => res.shift = true, "Alt" | "Option" => res.alt = true, "Control" => res.ctrl = true, + "None" => (), _ => error!("Unknown modifier {:?}", modifier), } } @@ -771,7 +811,8 @@ impl<'a> de::Deserialize<'a> for ActionWrapper { fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Paste, Copy, PasteSelection, IncreaseFontSize, DecreaseFontSize, \ ResetFontSize, ScrollPageUp, ScrollPageDown, ScrollToTop, \ - ScrollToBottom, ClearHistory, Hide, ClearLogNotice, SpawnNewInstance or Quit") + ScrollToBottom, ClearHistory, Hide, ClearLogNotice, SpawnNewInstance, \ + None or Quit") } fn visit_str<E>(self, value: &str) -> ::std::result::Result<ActionWrapper, E> @@ -793,6 +834,7 @@ impl<'a> de::Deserialize<'a> for ActionWrapper { "Quit" => Action::Quit, "ClearLogNotice" => Action::ClearLogNotice, "SpawnNewInstance" => Action::SpawnNewInstance, + "None" => Action::None, _ => return Err(E::invalid_value(Unexpected::Str(value), &self)), })) } @@ -801,8 +843,8 @@ impl<'a> de::Deserialize<'a> for ActionWrapper { } } -#[derive(Debug, Deserialize, Clone)] #[serde(untagged)] +#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] pub enum CommandWrapper { Just(String), WithArgs { @@ -918,6 +960,7 @@ impl<'a> de::Deserialize<'a> for MouseButton { /// Bindings are deserialized into a `RawBinding` before being parsed as a /// `KeyBinding` or `MouseBinding`. +#[derive(PartialEq, Eq)] struct RawBinding { key: Option<Key>, mouse: Option<::glutin::MouseButton>, @@ -1196,21 +1239,87 @@ pub enum Error { Yaml(serde_yaml::Error), } -#[derive(Debug, Deserialize)] +#[serde(default)] +#[derive(Debug, Deserialize, PartialEq, Eq)] pub struct Colors { - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub primary: PrimaryColors, - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub cursor: CursorColors, + #[serde(deserialize_with = "deserialize_normal_colors")] pub normal: AnsiColors, + #[serde(deserialize_with = "deserialize_bright_colors")] pub bright: AnsiColors, - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub dim: Option<AnsiColors>, - #[serde(default, deserialize_with = "failure_default_vec")] + #[serde(deserialize_with = "failure_default_vec")] pub indexed_colors: Vec<IndexedColor>, } -#[derive(Debug, Deserialize)] +impl Default for Colors { + fn default() -> Colors { + Colors { + primary: Default::default(), + cursor: Default::default(), + normal: default_normal_colors(), + bright: default_bright_colors(), + dim: Default::default(), + indexed_colors: Default::default(), + } + } +} + +fn default_normal_colors() -> AnsiColors { + AnsiColors { + black: Rgb {r: 0x00, g: 0x00, b: 0x00}, + red: Rgb {r: 0xd5, g: 0x4e, b: 0x53}, + green: Rgb {r: 0xb9, g: 0xca, b: 0x4a}, + yellow: Rgb {r: 0xe6, g: 0xc5, b: 0x47}, + blue: Rgb {r: 0x7a, g: 0xa6, b: 0xda}, + magenta: Rgb {r: 0xc3, g: 0x97, b: 0xd8}, + cyan: Rgb {r: 0x70, g: 0xc0, b: 0xba}, + white: Rgb {r: 0xea, g: 0xea, b: 0xea}, + } +} + +fn default_bright_colors() -> AnsiColors { + AnsiColors { + black: Rgb {r: 0x66, g: 0x66, b: 0x66}, + red: Rgb {r: 0xff, g: 0x33, b: 0x34}, + green: Rgb {r: 0x9e, g: 0xc4, b: 0x00}, + yellow: Rgb {r: 0xe7, g: 0xc5, b: 0x47}, + blue: Rgb {r: 0x7a, g: 0xa6, b: 0xda}, + magenta: Rgb {r: 0xb7, g: 0x7e, b: 0xe0}, + cyan: Rgb {r: 0x54, g: 0xce, b: 0xd6}, + white: Rgb {r: 0xff, g: 0xff, b: 0xff}, + } +} + +fn deserialize_normal_colors<'a, D>(deserializer: D) -> ::std::result::Result<AnsiColors, D::Error> + where D: de::Deserializer<'a> +{ + match AnsiColors::deserialize(deserializer) { + Ok(escape_chars) => Ok(escape_chars), + Err(err) => { + error!("Problem with config: {}; using default value", err); + Ok(default_normal_colors()) + }, + } +} + +fn deserialize_bright_colors<'a, D>(deserializer: D) -> ::std::result::Result<AnsiColors, D::Error> + where D: de::Deserializer<'a> +{ + match AnsiColors::deserialize(deserializer) { + Ok(escape_chars) => Ok(escape_chars), + Err(err) => { + error!("Problem with config: {}; using default value", err); + Ok(default_bright_colors()) + }, + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq)] pub struct IndexedColor { #[serde(deserialize_with = "deserialize_color_index")] pub index: u8, @@ -1246,11 +1355,12 @@ fn deserialize_color_index<'a, D>(deserializer: D) -> ::std::result::Result<u8, } } -#[derive(Copy, Clone, Debug, Deserialize)] +#[serde(default)] +#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq)] pub struct Cursor { - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub style: CursorStyle, - #[serde(default="true_bool", deserialize_with = "default_true_bool")] + #[serde(deserialize_with = "deserialize_true_bool")] pub unfocused_hollow: bool, } @@ -1263,26 +1373,39 @@ impl Default for Cursor { } } -#[derive(Debug, Copy, Clone, Default, Deserialize)] +#[serde(default)] +#[derive(Debug, Copy, Clone, Default, Deserialize, PartialEq, Eq)] pub struct CursorColors { - #[serde(default, deserialize_with = "deserialize_optional_color")] + #[serde(deserialize_with = "deserialize_optional_color")] pub text: Option<Rgb>, - #[serde(default, deserialize_with = "deserialize_optional_color")] + #[serde(deserialize_with = "deserialize_optional_color")] pub cursor: Option<Rgb>, } -#[derive(Debug, Deserialize)] +#[serde(default)] +#[derive(Debug, Deserialize, PartialEq, Eq)] pub struct PrimaryColors { #[serde(deserialize_with = "rgb_from_hex")] pub background: Rgb, #[serde(deserialize_with = "rgb_from_hex")] pub foreground: Rgb, - #[serde(default, deserialize_with = "deserialize_optional_color")] + #[serde(deserialize_with = "deserialize_optional_color")] pub bright_foreground: Option<Rgb>, - #[serde(default, deserialize_with = "deserialize_optional_color")] + #[serde(deserialize_with = "deserialize_optional_color")] pub dim_foreground: Option<Rgb>, } +impl Default for PrimaryColors { + fn default() -> Self { + PrimaryColors { + background: default_background(), + foreground: default_foreground(), + bright_foreground: Default::default(), + dim_foreground: Default::default(), + } + } +} + fn deserialize_optional_color<'a, D>(deserializer: D) -> ::std::result::Result<Option<Rgb>, D::Error> where D: de::Deserializer<'a> { @@ -1299,50 +1422,16 @@ fn deserialize_optional_color<'a, D>(deserializer: D) -> ::std::result::Result<O } } -impl Default for PrimaryColors { - fn default() -> Self { - PrimaryColors { - background: Rgb { r: 0, g: 0, b: 0 }, - foreground: Rgb { r: 0xea, g: 0xea, b: 0xea }, - bright_foreground: None, - dim_foreground: None, - } - } +fn default_background() -> Rgb { + Rgb { r: 0, g: 0, b: 0 } } -impl Default for Colors { - fn default() -> Colors { - Colors { - primary: PrimaryColors::default(), - cursor: CursorColors::default(), - normal: AnsiColors { - black: Rgb {r: 0x00, g: 0x00, b: 0x00}, - red: Rgb {r: 0xd5, g: 0x4e, b: 0x53}, - green: Rgb {r: 0xb9, g: 0xca, b: 0x4a}, - yellow: Rgb {r: 0xe6, g: 0xc5, b: 0x47}, - blue: Rgb {r: 0x7a, g: 0xa6, b: 0xda}, - magenta: Rgb {r: 0xc3, g: 0x97, b: 0xd8}, - cyan: Rgb {r: 0x70, g: 0xc0, b: 0xba}, - white: Rgb {r: 0xea, g: 0xea, b: 0xea}, - }, - bright: AnsiColors { - black: Rgb {r: 0x66, g: 0x66, b: 0x66}, - red: Rgb {r: 0xff, g: 0x33, b: 0x34}, - green: Rgb {r: 0x9e, g: 0xc4, b: 0x00}, - yellow: Rgb {r: 0xe7, g: 0xc5, b: 0x47}, - blue: Rgb {r: 0x7a, g: 0xa6, b: 0xda}, - magenta: Rgb {r: 0xb7, g: 0x7e, b: 0xe0}, - cyan: Rgb {r: 0x54, g: 0xce, b: 0xd6}, - white: Rgb {r: 0xff, g: 0xff, b: 0xff}, - }, - dim: None, - indexed_colors: Vec::new(), - } - } +fn default_foreground() -> Rgb { + Rgb { r: 0xea, g: 0xea, b: 0xea } } /// The 8-colors sections of config -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, PartialEq, Eq)] pub struct AnsiColors { #[serde(deserialize_with = "rgb_from_hex")] pub black: Rgb, @@ -1650,11 +1739,18 @@ impl Config { self.render_timer } + #[cfg(target_os = "macos")] #[inline] pub fn use_thin_strokes(&self) -> bool { self.font.use_thin_strokes } + #[cfg(not(target_os = "macos"))] + #[inline] + pub fn use_thin_strokes(&self) -> bool { + false + } + pub fn path(&self) -> Option<&Path> { self.config_path .as_ref() @@ -1808,21 +1904,18 @@ impl Config { /// Window Dimensions /// /// Newtype to avoid passing values incorrectly -#[derive(Debug, Copy, Clone, Deserialize)] +#[serde(default)] +#[derive(Default, Debug, Copy, Clone, Deserialize, PartialEq, Eq)] pub struct Dimensions { /// Window width in character columns + #[serde(deserialize_with = "failure_default")] columns: Column, /// Window Height in character lines + #[serde(deserialize_with = "failure_default")] lines: Line, } -impl Default for Dimensions { - fn default() -> Dimensions { - Dimensions::new(Column(80), Line(24)) - } -} - impl Dimensions { pub fn new(columns: Column, lines: Line) -> Self { Dimensions { @@ -1845,14 +1938,14 @@ impl Dimensions { } /// A delta for a point in a 2 dimensional plane -#[derive(Clone, Copy, Debug, Default, Deserialize)] -#[serde(bound(deserialize = "T: Deserialize<'de> + Default"))] -pub struct Delta<T: Default> { +#[serde(default, bound(deserialize = "T: Deserialize<'de> + Default"))] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)] +pub struct Delta<T: Default + PartialEq + Eq> { /// Horizontal change - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub x: T, /// Vertical change - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] pub y: T, } @@ -1897,12 +1990,13 @@ impl DeserializeSize for Size { .deserialize_any(NumVisitor::<D>{ _marker: PhantomData }) .map(|v| Size::new(v as _)); - // Use font size 12 as fallback + // Use default font size as fallback match size { Ok(size) => Ok(size), Err(err) => { - error!("Problem with config: {}; using size 12", err); - Ok(Size::new(12.)) + let size = default_font_size(); + error!("Problem with config: {}; using size {}", err, size.as_f32_pts()); + Ok(size) }, } } @@ -1914,68 +2008,54 @@ impl DeserializeSize for Size { /// 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. -#[derive(Debug, Deserialize, Clone)] +#[serde(default)] +#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] pub struct Font { - /// Font family - pub normal: FontDescription, + /// Normal font face + #[serde(deserialize_with = "failure_default")] + normal: FontDescription, - #[serde(default="default_italic_desc")] - pub italic: FontDescription, + /// Bold font face + #[serde(deserialize_with = "failure_default")] + italic: SecondaryFontDescription, - #[serde(default="default_bold_desc")] - pub bold: FontDescription, + /// Italic font face + #[serde(deserialize_with = "failure_default")] + bold: SecondaryFontDescription, - // Font size in points - #[serde(deserialize_with="DeserializeSize::deserialize")] + /// Font size in points + #[serde(deserialize_with = "DeserializeSize::deserialize")] pub size: Size, /// Extra spacing per character - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] offset: Delta<i8>, /// Glyph offset within character cell - #[serde(default, deserialize_with = "failure_default")] + #[serde(deserialize_with = "failure_default")] glyph_offset: Delta<i8>, - #[serde(default="true_bool", deserialize_with = "default_true_bool")] + #[cfg(target_os = "macos")] + #[serde(deserialize_with = "deserialize_true_bool")] use_thin_strokes: bool, // TODO: Deprecated - #[serde(default, deserialize_with = "deserialize_scale_with_dpi")] + #[serde(deserialize_with = "deserialize_scale_with_dpi")] scale_with_dpi: Option<()>, } -fn deserialize_scale_with_dpi<'a, D>(deserializer: D) -> ::std::result::Result<Option<()>, D::Error> -where - D: de::Deserializer<'a>, -{ - // This is necessary in order to get serde to complete deserialization of the configuration - let _ignored = bool::deserialize(deserializer); - error!("The scale_with_dpi setting has been removed, \ - on X11 the WINIT_HIDPI_FACTOR environment variable can be used instead."); - Ok(None) -} - -fn default_bold_desc() -> FontDescription { - Font::default().bold -} - -fn default_italic_desc() -> FontDescription { - Font::default().italic -} - -/// Description of a single font -#[derive(Debug, Deserialize, Clone)] -pub struct FontDescription { - pub family: String, - pub style: Option<String>, -} - -impl FontDescription { - fn new_with_family<S: Into<String>>(family: S) -> FontDescription { - FontDescription { - family: family.into(), - style: None, +impl Default for Font { + fn default() -> Font { + Font { + #[cfg(target_os = "macos")] + use_thin_strokes: true, + size: default_font_size(), + normal: FontDescription::new_with_style("Regular"), + bold: SecondaryFontDescription::new_with_style("Bold"), + italic: SecondaryFontDescription::new_with_style("Italic"), + scale_with_dpi: Default::default(), + glyph_offset: Default::default(), + offset: Default::default(), } } } @@ -2006,52 +2086,95 @@ impl Font { .. self } } + + // Get normal font description + pub fn normal(&self) -> &FontDescription { + &self.normal + } + + // Get italic font description + pub fn italic(&self) -> FontDescription { + self.italic.desc(&self.normal) + } + + // Get bold font description + pub fn bold(&self) -> FontDescription { + self.bold.desc(&self.normal) + } } -#[cfg(target_os = "macos")] -impl Default for Font { - fn default() -> Font { - Font { - normal: FontDescription::new_with_family("Menlo"), - bold: FontDescription::new_with_family("Menlo"), - italic: FontDescription::new_with_family("Menlo"), - size: Size::new(11.0), - use_thin_strokes: true, - scale_with_dpi: None, - glyph_offset: Default::default(), - offset: Default::default(), +fn default_font_size() -> Size { + Size::new(11.) +} + +fn deserialize_scale_with_dpi<'a, D>(deserializer: D) -> ::std::result::Result<Option<()>, D::Error> +where + D: de::Deserializer<'a>, +{ + // This is necessary in order to get serde to complete deserialization of the configuration + let _ignored = bool::deserialize(deserializer); + error!("The scale_with_dpi setting has been removed, \ + on X11 the WINIT_HIDPI_FACTOR environment variable can be used instead."); + Ok(None) +} + +/// Description of the normal font +#[serde(default)] +#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +pub struct FontDescription { + #[serde(deserialize_with = "failure_default")] + pub family: String, + #[serde(deserialize_with = "failure_default")] + pub style: Option<String>, +} + +impl Default for FontDescription { + fn default() -> FontDescription { + FontDescription { + #[cfg(not(any(target_os = "macos", windows)))] + family: "monospace".into(), + #[cfg(target_os = "macos")] + family: "Menlo".into(), + #[cfg(windows)] + family: "Consolas".into(), + style: None, } } } -#[cfg(any(target_os = "linux",target_os = "freebsd",target_os = "openbsd"))] -impl Default for Font { - fn default() -> Font { - Font { - normal: FontDescription::new_with_family("monospace"), - bold: FontDescription::new_with_family("monospace"), - italic: FontDescription::new_with_family("monospace"), - size: Size::new(11.0), - use_thin_strokes: false, - scale_with_dpi: None, - glyph_offset: Default::default(), - offset: Default::default(), +impl FontDescription { + fn new_with_style(style: &str) -> Self { + Self { + style: Some(style.into()), + ..Default::default() } } } -#[cfg(windows)] -impl Default for Font { - fn default() -> Font { - Font { - normal: FontDescription::new_with_family("Consolas"), - bold: FontDescription::new_with_family("Consolas"), - italic: FontDescription::new_with_family("Consolas"), - size: Size::new(11.0), - use_thin_strokes: false, - scale_with_dpi: None, - glyph_offset: Default::default(), - offset: Default::default(), +/// Description of the italic and bold font +#[serde(default)] +#[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)] +pub struct SecondaryFontDescription { + #[serde(deserialize_with = "failure_default")] + family: Option<String>, + #[serde(deserialize_with = "failure_default")] + style: Option<String>, +} + +impl SecondaryFontDescription { + pub fn desc(&self, fallback: &FontDescription) -> FontDescription { + FontDescription { + family: self.family.clone().unwrap_or_else(|| fallback.family.clone()), + style: self.style.clone(), + } + } +} + +impl SecondaryFontDescription { + fn new_with_style(style: &str) -> Self { + Self { + style: Some(style.into()), + ..Default::default() } } } @@ -2131,55 +2254,7 @@ impl Monitor { } } -#[cfg(test)] -mod tests { - use crate::cli::Options; - use super::Config; - - #[cfg(target_os="macos")] - static ALACRITTY_YML: &'static str = - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty_macos.yml")); - #[cfg(windows)] - static ALACRITTY_YML: &'static str = - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty_windows.yml")); - #[cfg(not(any(target_os="macos", windows)))] - static ALACRITTY_YML: &'static str = - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty.yml")); - - #[test] - fn parse_config() { - let config: Config = ::serde_yaml::from_str(ALACRITTY_YML) - .expect("deserialize config"); - - // Sanity check that mouse bindings are being parsed - assert!(!config.mouse_bindings.is_empty()); - - // Sanity check that key bindings are being parsed - assert!(!config.key_bindings.is_empty()); - } - - #[test] - fn dynamic_title_ignoring_options_by_default() { - let config: Config = ::serde_yaml::from_str(ALACRITTY_YML) - .expect("deserialize config"); - let old_dynamic_title = config.dynamic_title; - let options = Options::default(); - let config = config.update_dynamic_title(&options); - assert_eq!(old_dynamic_title, config.dynamic_title); - } - - #[test] - fn dynamic_title_overridden_by_options() { - let config: Config = ::serde_yaml::from_str(ALACRITTY_YML) - .expect("deserialize config"); - let mut options = Options::default(); - options.title = Some("foo".to_owned()); - let config = config.update_dynamic_title(&options); - assert!(!config.dynamic_title); - } -} - -#[derive(Deserialize, Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)] pub enum Key { Scancode(u32), Key1, @@ -2514,3 +2589,50 @@ impl Key { } } } + +#[cfg(test)] +mod tests { + use crate::cli::Options; + use super::{Config, DEFAULT_ALACRITTY_CONFIG}; + + #[test] + fn parse_config() { + let config: Config = ::serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG) + .expect("deserialize config"); + + // Sanity check that mouse bindings are being parsed + assert!(!config.mouse_bindings.is_empty()); + + // Sanity check that key bindings are being parsed + assert!(!config.key_bindings.is_empty()); + } + + #[test] + fn dynamic_title_ignoring_options_by_default() { + let config: Config = ::serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG) + .expect("deserialize config"); + let old_dynamic_title = config.dynamic_title; + let options = Options::default(); + let config = config.update_dynamic_title(&options); + assert_eq!(old_dynamic_title, config.dynamic_title); + } + + #[test] + fn dynamic_title_overridden_by_options() { + let config: Config = ::serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG) + .expect("deserialize config"); + let mut options = Options::default(); + options.title = Some("foo".to_owned()); + let config = config.update_dynamic_title(&options); + assert!(!config.dynamic_title); + } + + #[test] + fn default_match_empty() { + let default = Config::default(); + + let empty = serde_yaml::from_str("key: val\n").unwrap(); + + assert_eq!(default, empty); + } +} diff --git a/src/input.rs b/src/input.rs index be8ed0d1..c2473448 100644 --- a/src/input.rs +++ b/src/input.rs @@ -81,7 +81,7 @@ pub trait ActionContext { /// Describes a state and action to take in that state /// /// This is the shared component of `MouseBinding` and `KeyBinding` -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Binding<T> { /// Modifier keys required to activate binding pub mods: ModifiersState, @@ -107,6 +107,30 @@ pub type KeyBinding = Binding<Key>; /// Bindings that are triggered by a mouse button pub type MouseBinding = Binding<MouseButton>; +impl Default for KeyBinding { + fn default() -> KeyBinding { + KeyBinding { + mods: Default::default(), + action: Action::Esc(String::new()), + mode: TermMode::NONE, + notmode: TermMode::NONE, + trigger: Key::A, + } + } +} + +impl Default for MouseBinding { + fn default() -> MouseBinding { + MouseBinding { + mods: Default::default(), + action: Action::Esc(String::new()), + mode: TermMode::NONE, + notmode: TermMode::NONE, + trigger: MouseButton::Left, + } + } +} + impl<T: Eq> Binding<T> { #[inline] fn is_triggered_by( @@ -124,6 +148,14 @@ impl<T: Eq> Binding<T> { self.not_mode_matches(mode) && self.mods_match(mods, relaxed) } + + #[inline] + pub fn triggers_match(&self, binding: &Binding<T>) -> bool { + self.trigger == binding.trigger + && self.mode == binding.mode + && self.notmode == binding.notmode + && self.mods == binding.mods + } } impl<T> Binding<T> { @@ -154,7 +186,7 @@ impl<T> Binding<T> { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum Action { /// Write an escape sequence Esc(String), @@ -206,6 +238,15 @@ pub enum Action { /// Spawn a new instance of Alacritty. SpawnNewInstance, + + /// No action. + None, +} + +impl Default for Action { + fn default() -> Action { + Action::None + } } impl Action { @@ -287,6 +328,7 @@ impl Action { Action::SpawnNewInstance => { ctx.spawn_new_instance(); }, + Action::None => (), } } diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 4d5d8de3..f743e4b9 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -234,7 +234,7 @@ impl GlyphCache { let size = font.size(); // Load regular font - let regular_desc = Self::make_desc(&font.normal, font::Slant::Normal, font::Weight::Normal); + let regular_desc = Self::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal); let regular = rasterizer.load_font(®ular_desc, size)?; @@ -250,12 +250,12 @@ impl GlyphCache { }; // Load bold font - let bold_desc = Self::make_desc(&font.bold, font::Slant::Normal, font::Weight::Bold); + let bold_desc = Self::make_desc(&font.bold(), font::Slant::Normal, font::Weight::Bold); let bold = load_or_regular(bold_desc); // Load italic font - let italic_desc = Self::make_desc(&font.italic, font::Slant::Italic, font::Weight::Normal); + let italic_desc = Self::make_desc(&font.italic(), font::Slant::Italic, font::Weight::Normal); let italic = load_or_regular(italic_desc); @@ -272,7 +272,7 @@ impl GlyphCache { } else { font::Style::Description { slant, weight } }; - FontDesc::new(&desc.family[..], style) + FontDesc::new(desc.family.clone(), style) } pub fn font_metrics(&self) -> font::Metrics { diff --git a/src/term/mod.rs b/src/term/mod.rs index 07cd27aa..c435d501 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -897,6 +897,8 @@ impl Term { let scroll_region = Line(0)..grid.num_lines(); + let colors = color::List::from(config.colors()); + Term { next_title: None, next_mouse_cursor: None, @@ -917,9 +919,9 @@ impl Term { mode: Default::default(), scroll_region, size_info: size, - colors: color::List::from(config.colors()), + colors, color_modified: [false; color::COUNT], - original_colors: color::List::from(config.colors()), + original_colors: colors, semantic_escape_chars: config.selection().semantic_escape_chars.clone(), cursor_style: None, default_cursor_style: config.cursor_style(), @@ -117,7 +117,7 @@ impl UrlParser { } #[cfg(test)] -mod test { +mod tests { use std::mem; use crate::grid::Grid; |