diff options
author | Christian Duerr <chrisduerr@users.noreply.github.com> | 2019-01-17 09:17:26 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-17 09:17:26 +0000 |
commit | 0d16478f5d997b6da5488885e15bfb09ca8e7f6d (patch) | |
tree | 9905e264149d5955ce1161ecf41ad2f23ec9ec91 /src | |
parent | 5864c30a54b250163c3a94f053f5f1b907adf9c9 (diff) | |
download | alacritty-0d16478f5d997b6da5488885e15bfb09ca8e7f6d.tar.gz alacritty-0d16478f5d997b6da5488885e15bfb09ca8e7f6d.zip |
Make all configuration fields optional
All configuration fields now have fallback values which will be used if
the field is not present. This includes mouse, key bindings and platform
specific differences.
The mouse and key bindings are now filled by default, if the user
rebinds a default mapping, it will be overwritten. To unbind a default
binding, it can be mapped to `chars: ""`.
Since all platform differences can now be correctly handled by the
`src/config/mod.rs` code, it's no longer necessary to maintain separate
configuration files, so the `alacritty_macos.yml` and
`alacritty_windows.yml` have been deleted.
Fixes #40.
Fixes #1923.
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; |