diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config.rs | 328 | ||||
-rw-r--r-- | src/main.rs | 12 | ||||
-rw-r--r-- | src/term/mod.rs | 11 |
3 files changed, 236 insertions, 115 deletions
diff --git a/src/config.rs b/src/config.rs index 2a2ba6db..d9724e61 100644 --- a/src/config.rs +++ b/src/config.rs @@ -53,20 +53,37 @@ pub struct ClickHandler { pub threshold: Duration, } +impl Default for ClickHandler { + fn default() -> Self { + ClickHandler { threshold: default_threshold_ms() } + } +} + +fn default_threshold_ms() -> Duration { + Duration::from_millis(300) +} + fn deserialize_duration_ms<'a, D>(deserializer: D) -> ::std::result::Result<Duration, D::Error> where D: de::Deserializer<'a> { - let threshold_ms = u64::deserialize(deserializer)?; - Ok(Duration::from_millis(threshold_ms)) + match u64::deserialize(deserializer) { + Ok(threshold_ms) => Ok(Duration::from_millis(threshold_ms)), + Err(err) => { + eprintln!("problem with config: {}; Using default value", err); + Ok(default_threshold_ms()) + }, + } } - #[derive(Clone, Debug, Deserialize)] pub struct Mouse { + #[serde(default, deserialize_with = "failure_default")] pub double_click: ClickHandler, + #[serde(default, deserialize_with = "failure_default")] pub triple_click: ClickHandler, /// up/down arrows sent when scrolling in alt screen buffer + #[serde(deserialize_with = "deserialize_faux_scrollback_lines")] #[serde(default="default_faux_scrollback_lines")] pub faux_scrollback_lines: usize, } @@ -75,6 +92,18 @@ fn default_faux_scrollback_lines() -> usize { 1 } +fn deserialize_faux_scrollback_lines<'a, D>(deserializer: D) -> ::std::result::Result<usize, D::Error> + where D: de::Deserializer<'a> +{ + match usize::deserialize(deserializer) { + Ok(lines) => Ok(lines), + Err(err) => { + eprintln!("problem with config: {}; Using default value", err); + Ok(default_faux_scrollback_lines()) + }, + } +} + impl Default for Mouse { fn default() -> Mouse { Mouse { @@ -105,25 +134,40 @@ pub enum VisualBellAnimation { Linear, } +impl Default for VisualBellAnimation { + fn default() -> Self { + VisualBellAnimation::EaseOutExpo + } +} + #[derive(Debug, Deserialize)] pub struct VisualBellConfig { /// Visual bell animation function - #[serde(default="default_visual_bell_animation")] + #[serde(default, deserialize_with = "failure_default")] animation: VisualBellAnimation, /// Visual bell duration in milliseconds + #[serde(deserialize_with = "deserialize_visual_bell_duration")] #[serde(default="default_visual_bell_duration")] duration: u16, } -fn default_visual_bell_animation() -> VisualBellAnimation { - VisualBellAnimation::EaseOutExpo -} - fn default_visual_bell_duration() -> u16 { 150 } +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) => { + eprintln!("problem with config: {}; Using default value", err); + Ok(default_visual_bell_duration()) + }, + } +} + impl VisualBellConfig { /// Visual bell animation #[inline] @@ -141,7 +185,7 @@ impl VisualBellConfig { impl Default for VisualBellConfig { fn default() -> VisualBellConfig { VisualBellConfig { - animation: default_visual_bell_animation(), + animation: VisualBellAnimation::default(), duration: default_visual_bell_duration(), } } @@ -151,7 +195,7 @@ impl Default for VisualBellConfig { pub struct Shell<'a> { program: Cow<'a, str>, - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] args: Vec<String>, } @@ -221,18 +265,34 @@ impl Default for Alpha { #[derive(Debug, Copy, Clone, Deserialize)] pub struct WindowConfig { /// Initial dimensions - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] dimensions: Dimensions, /// Pixel padding - #[serde(default="default_padding")] + #[serde(default="default_padding", deserialize_with = "deserialize_padding")] padding: Delta, /// Draw the window with title bar / borders - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] decorations: bool, } +fn default_padding() -> Delta { + Delta { x: 2., y: 2. } +} + +fn deserialize_padding<'a, D>(deserializer: D) -> ::std::result::Result<Delta, D::Error> + where D: de::Deserializer<'a> +{ + match Delta::deserialize(deserializer) { + Ok(delta) => Ok(delta), + Err(err) => { + eprintln!("problem with config: {}; Using default value", err); + Ok(default_padding()) + }, + } +} + impl WindowConfig { pub fn decorations(&self) -> bool { self.decorations @@ -253,141 +313,169 @@ impl Default for WindowConfig { #[derive(Debug, Deserialize)] pub struct Config { /// Initial dimensions - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] dimensions: Option<Dimensions>, /// Pixel padding - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] padding: Option<Delta>, /// TERM env variable - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] env: HashMap<String, String>, /// Font configuration - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] font: Font, /// Should show render timer - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] render_timer: bool, /// Should use custom cursor colors - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] custom_cursor_colors: bool, /// Should draw bold text with brighter colors instead of bold font - #[serde(default="true_bool")] + #[serde(default="true_bool", deserialize_with = "default_true_bool")] draw_bold_text_with_bright_colors: bool, - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] colors: Colors, /// Background opacity from 0.0 to 1.0 - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] background_opacity: Alpha, /// Window configuration - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] window: WindowConfig, /// Keybindings - #[serde(default="default_key_bindings")] + #[serde(default, deserialize_with = "failure_default_vec")] key_bindings: Vec<KeyBinding>, /// Bindings for the mouse - #[serde(default="default_mouse_bindings")] + #[serde(default, deserialize_with = "failure_default_vec")] mouse_bindings: Vec<MouseBinding>, - #[serde(default="default_selection")] + #[serde(default, deserialize_with = "failure_default")] selection: Selection, - #[serde(default="default_mouse")] + #[serde(default, deserialize_with = "failure_default")] mouse: Mouse, /// Path to a shell program to run on startup - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] shell: Option<Shell<'static>>, /// Path where config was loaded from + #[serde(default, deserialize_with = "failure_default")] config_path: Option<PathBuf>, /// Visual bell configuration - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] visual_bell: VisualBellConfig, /// Use dynamic title - #[serde(default="true_bool")] + #[serde(default="true_bool", deserialize_with = "default_true_bool")] dynamic_title: bool, /// Hide cursor when typing - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] hide_cursor_when_typing: bool, /// Style of the cursor - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] cursor_style: CursorStyle, /// Live config reload - #[serde(default="true_bool")] + #[serde(default="true_bool", deserialize_with = "default_true_bool")] live_config_reload: bool, -} -fn default_padding() -> Delta { - Delta { x: 2., y: 2. } + /// Number of spaces in one tab + #[serde(default="default_tabspaces", deserialize_with = "deserialize_tabspaces")] + tabspaces: usize, } -#[cfg(not(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"); +fn failure_default_vec<'a, D, T>(deserializer: D) -> ::std::result::Result<Vec<T>, D::Error> + where D: de::Deserializer<'a>, + T: Deserialize<'a> +{ + // Deserialize as generic vector + let vec = match Vec::<serde_yaml::Value>::deserialize(deserializer) { + Ok(vec) => vec, + Err(err) => { + eprintln!("problem with config: {}; Using empty vector", err); + return Ok(Vec::new()); + }, + }; + + // Move to lossy vector + let mut bindings: Vec<T> = Vec::new(); + for value in vec { + match T::deserialize(value) { + Ok(binding) => bindings.push(binding), + Err(err) => { + eprintln!("problem with config: {}; Skipping value", err); + }, + } + } -fn default_config() -> Config { - serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG) - .expect("default config is valid") + Ok(bindings) } -fn default_selection() -> Selection { - default_config().selection +fn default_tabspaces() -> usize { + 8 } -fn default_key_bindings() -> Vec<KeyBinding> { - default_config().key_bindings +fn deserialize_tabspaces<'a, D>(deserializer: D) -> ::std::result::Result<usize, D::Error> + where D: de::Deserializer<'a> +{ + match usize::deserialize(deserializer) { + Ok(value) => Ok(value), + Err(err) => { + eprintln!("problem with config: {}; Using `8`", err); + Ok(default_tabspaces()) + }, + } } -fn default_mouse_bindings() -> Vec<MouseBinding> { - default_config().mouse_bindings +fn default_true_bool<'a, D>(deserializer: D) -> ::std::result::Result<bool, D::Error> + where D: de::Deserializer<'a> +{ + match bool::deserialize(deserializer) { + Ok(value) => Ok(value), + Err(err) => { + eprintln!("problem with config: {}; Using `true`", err); + Ok(true) + }, + } } -fn default_mouse() -> Mouse { - default_config().mouse +fn failure_default<'a, D, T>(deserializer: D) + -> ::std::result::Result<T, D::Error> + where D: de::Deserializer<'a>, + T: Deserialize<'a> + Default +{ + match T::deserialize(deserializer) { + Ok(value) => Ok(value), + Err(err) => { + eprintln!("problem with config: {}; Using default value", err); + Ok(T::default()) + }, + } } +#[cfg(not(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"); + impl Default for Config { - fn default() -> Config { - Config { - draw_bold_text_with_bright_colors: true, - dimensions: None, - padding: None, - font: Default::default(), - render_timer: Default::default(), - custom_cursor_colors: false, - colors: Default::default(), - background_opacity: Default::default(), - key_bindings: Vec::new(), - mouse_bindings: Vec::new(), - selection: Default::default(), - mouse: Default::default(), - shell: None, - config_path: None, - visual_bell: Default::default(), - env: Default::default(), - hide_cursor_when_typing: Default::default(), - cursor_style: Default::default(), - dynamic_title: Default::default(), - live_config_reload: true, - window: Default::default(), - } + fn default() -> Self { + serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG) + .expect("default config is invalid") } } @@ -846,19 +934,26 @@ pub enum Error { #[derive(Debug, Deserialize)] pub struct Colors { + #[serde(default, deserialize_with = "failure_default")] pub primary: PrimaryColors, - #[serde(deserialize_with="deserialize_cursor_colors", default="default_cursor_colors")] + #[serde(default, deserialize_with = "deserialize_cursor_colors")] pub cursor: CursorColors, pub normal: AnsiColors, pub bright: AnsiColors, + #[serde(default, deserialize_with = "failure_default")] pub dim: Option<AnsiColors>, } fn deserialize_cursor_colors<'a, D>(deserializer: D) -> ::std::result::Result<CursorColors, D::Error> where D: de::Deserializer<'a> { - let either = CursorOrPrimaryColors::deserialize(deserializer)?; - Ok(either.into_cursor_colors()) + match CursorOrPrimaryColors::deserialize(deserializer) { + Ok(either) => Ok(either.into_cursor_colors()), + Err(err) => { + eprintln!("problem with config: {}; Using default value", err); + Ok(CursorColors::default()) + }, + } } #[derive(Deserialize)] @@ -901,19 +996,21 @@ impl CursorOrPrimaryColors { } } -fn default_cursor_colors() -> CursorColors { - CursorColors { - text: Rgb { r: 0, g: 0, b: 0 }, - cursor: Rgb { r: 0xff, g: 0xff, b: 0xff }, - } -} - #[derive(Debug)] pub struct CursorColors { pub text: Rgb, pub cursor: Rgb, } +impl Default for CursorColors { + fn default() -> Self { + CursorColors { + text: Rgb { r: 0, g: 0, b: 0 }, + cursor: Rgb { r: 0xff, g: 0xff, b: 0xff }, + } + } +} + #[derive(Debug, Deserialize)] pub struct PrimaryColors { #[serde(deserialize_with = "rgb_from_hex")] @@ -922,14 +1019,20 @@ pub struct PrimaryColors { pub foreground: Rgb, } +impl Default for PrimaryColors { + fn default() -> Self { + PrimaryColors { + background: Rgb { r: 0, g: 0, b: 0 }, + foreground: Rgb { r: 0xea, g: 0xea, b: 0xea }, + } + } +} + impl Default for Colors { fn default() -> Colors { Colors { - primary: PrimaryColors { - background: Rgb { r: 0, g: 0, b: 0 }, - foreground: Rgb { r: 0xea, g: 0xea, b: 0xea }, - }, - cursor: default_cursor_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}, @@ -1000,7 +1103,16 @@ fn rgb_from_hex<'a, D>(deserializer: D) -> ::std::result::Result<Rgb, D::Error> } } - deserializer.deserialize_str(RgbVisitor) + let rgb = deserializer.deserialize_str(RgbVisitor); + + // Use #ff00ff as fallback color + match rgb { + Ok(rgb) => Ok(rgb), + Err(err) => { + eprintln!("problem with config: {}; Using color #ff00ff", err); + Ok(Rgb { r: 255, g: 0, b: 255 }) + }, + } } impl FromStr for Rgb { @@ -1171,6 +1283,10 @@ impl Config { &self.selection } + pub fn tabspaces(&self) -> usize { + self.tabspaces + } + pub fn padding(&self) -> &Delta { self.padding.as_ref() .unwrap_or(&self.window.padding) @@ -1337,8 +1453,10 @@ impl Dimensions { #[derive(Clone, Copy, Debug, Deserialize)] pub struct Delta { /// Horizontal change + #[serde(default, deserialize_with = "failure_default")] pub x: f32, /// Vertical change + #[serde(default, deserialize_with = "failure_default")] pub y: f32, } @@ -1385,9 +1503,18 @@ impl DeserializeSize for Size { } } - deserializer + let size = deserializer .deserialize_any(NumVisitor::<D>{ _marker: PhantomData }) - .map(|v| Size::new(v as _)) + .map(|v| Size::new(v as _)); + + // Use font size 12 as fallback + match size { + Ok(size) => Ok(size), + Err(err) => { + eprintln!("problem with config: {}; Using size 12", err); + Ok(Size::new(12.)) + }, + } } } @@ -1413,13 +1540,14 @@ pub struct Font { pub size: Size, /// Extra spacing per character + #[serde(default, deserialize_with = "failure_default")] offset: Delta, /// Glyph offset within character cell - #[serde(default)] + #[serde(default, deserialize_with = "failure_default")] glyph_offset: Delta, - #[serde(default="true_bool")] + #[serde(default="true_bool", deserialize_with = "default_true_bool")] use_thin_strokes: bool } @@ -1583,6 +1711,10 @@ impl Monitor { mod tests { use super::Config; + #[cfg(target_os="macos")] + static ALACRITTY_YML: &'static str = + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty_macos.yml")); + #[cfg(not(target_os="macos"))] static ALACRITTY_YML: &'static str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty.yml")); @@ -1597,12 +1729,6 @@ mod tests { // Sanity check that key bindings are being parsed assert!(config.key_bindings.len() >= 1); } - - #[test] - fn defaults_are_ok() { - super::default_key_bindings(); - super::default_mouse_bindings(); - } } #[cfg_attr(feature = "clippy", allow(enum_variant_names))] diff --git a/src/main.rs b/src/main.rs index e9ff12d5..42aa9449 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,16 +71,8 @@ fn load_config(options: &cli::Options) -> Config { }); Config::load_from(&*config_path).unwrap_or_else(|err| { - match err { - config::Error::NotFound => { - die!("Config file not found at: {}", config_path.display()); - }, - config::Error::Empty => { - eprintln!("Empty config; Loading defaults"); - Config::default() - }, - _ => die!("{}", err), - } + eprintln!("Error: {}; Loading default config", err); + Config::default() }) } diff --git a/src/term/mod.rs b/src/term/mod.rs index 7e6ff1e0..72c259f7 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -444,8 +444,6 @@ pub mod mode { pub use self::mode::TermMode; -pub const TAB_SPACES: usize = 8; - trait CharsetMapping { fn map(&self, c: char) -> char { c @@ -721,6 +719,9 @@ pub struct Term { default_cursor_style: CursorStyle, dynamic_title: bool, + + /// Number of spaces in one tab + tabspaces: usize, } /// Terminal size info @@ -798,8 +799,9 @@ impl Term { let grid = Grid::new(num_lines, num_cols, &template); + let tabspaces = config.tabspaces(); let tabs = IndexRange::from(Column(0)..grid.num_cols()) - .map(|i| (*i as usize) % TAB_SPACES == 0) + .map(|i| (*i as usize) % tabspaces == 0) .collect::<Vec<bool>>(); let alt = grid.clone(); @@ -832,6 +834,7 @@ impl Term { cursor_style: None, default_cursor_style: config.cursor_style(), dynamic_title: config.dynamic_title(), + tabspaces, } } @@ -1072,7 +1075,7 @@ impl Term { // Recreate tabs list self.tabs = IndexRange::from(Column(0)..self.grid.num_cols()) - .map(|i| (*i as usize) % TAB_SPACES == 0) + .map(|i| (*i as usize) % self.tabspaces == 0) .collect::<Vec<bool>>(); if num_lines > old_lines { |