//! Configuration for the gui portion of the terminal use failure::Error; use std; use std::fs; use std::io::prelude::*; use term::hyperlink; use toml; use term; use term::color::RgbColor; #[derive(Debug, Deserialize, Clone)] pub struct Config { /// The font size, measured in points #[serde(default = "default_font_size")] pub font_size: f64, /// The DPI to assume #[serde(default = "default_dpi")] pub dpi: f64, /// The baseline font to use #[serde(default)] pub font: TextStyle, /// An optional set of style rules to select the font based /// on the cell attributes #[serde(default)] pub font_rules: Vec, /// The color palette pub colors: Option, /// How many lines of scrollback you want to retain pub scrollback_lines: Option, #[serde(default = "default_hyperlink_rules")] pub hyperlink_rules: Vec, } fn default_hyperlink_rules() -> Vec { vec![ // URL with a protocol hyperlink::Rule::new(r"\b\w+://(?:[\w.-]+)\.[a-z]{2,15}\S*\b", "$0").unwrap(), // implicit mailto link hyperlink::Rule::new(r"\b\w+@[\w-]+(\.[\w-]+)+\b", "mailto:$0").unwrap(), ] } fn default_font_size() -> f64 { 10.0 } fn default_dpi() -> f64 { 96.0 } impl Default for Config { fn default() -> Self { Self { font_size: default_font_size(), dpi: default_dpi(), font: TextStyle::default(), font_rules: Vec::new(), colors: None, scrollback_lines: None, hyperlink_rules: default_hyperlink_rules(), } } } /// Represents textual styling. #[derive(Debug, Deserialize, Clone, PartialEq, Eq, Hash)] pub struct TextStyle { /// A font config pattern to parse to locate the font. /// Note that the dpi and current font_size for the terminal /// will be set on the parsed result. pub fontconfig_pattern: String, /// If set, when rendering text that is set to the default /// foreground color, use this color instead. This is most /// useful in a `[[font_rules]]` section to implement changing /// the text color for eg: bold text. pub foreground: Option, } impl Default for TextStyle { fn default() -> Self { Self { fontconfig_pattern: "monospace".into(), foreground: None, } } } impl TextStyle { /// Make a version of this style with bold enabled. /// Semi-lame: we just append fontconfig style settings /// to the string blindly. We could get more involved /// but it would mean adding in the fontsystem stuff here /// and this is probably good enough. fn make_bold(&self) -> Self { Self { fontconfig_pattern: format!("{}:weight=bold", self.fontconfig_pattern), foreground: self.foreground, } } /// Make a version of this style with italic enabled. /// Semi-lame: we just append fontconfig style settings /// to the string blindly. We could get more involved /// but it would mean adding in the fontsystem stuff here /// and this is probably good enough. fn make_italic(&self) -> Self { Self { fontconfig_pattern: format!("{}:style=Italic", self.fontconfig_pattern), foreground: self.foreground, } } } /// Defines a rule that can be used to select a TextStyle given /// an input CellAttributes value. The logic that applies the /// matching can be found in src/font/mod.rs. The concept is that /// the user can specify something like this: /// /// ``` /// [[font_rules]] /// italic = true /// font = { fontconfig_pattern = "Operator Mono SSm Lig:style=Italic" } /// ``` /// /// The above is translated as: "if the CellAttributes have the italic bit /// set, then use the italic style of font rather than the default", and /// stop processing further font rules. #[derive(Debug, Default, Deserialize, Clone)] pub struct StyleRule { /// If present, this rule matches when CellAttributes::intensity holds /// a value that matches this rule. Valid values are "Bold", "Normal", /// "Half". pub intensity: Option, /// If present, this rule matches when CellAttributes::underline holds /// a value that matches this rule. Valid values are "None", "Single", /// "Double". pub underline: Option, /// If present, this rule matches when CellAttributes::italic holds /// a value that matches this rule. pub italic: Option, /// If present, this rule matches when CellAttributes::blink holds /// a value that matches this rule. pub blink: Option, /// If present, this rule matches when CellAttributes::reverse holds /// a value that matches this rule. pub reverse: Option, /// If present, this rule matches when CellAttributes::strikethrough holds /// a value that matches this rule. pub strikethrough: Option, /// If present, this rule matches when CellAttributes::invisible holds /// a value that matches this rule. pub invisible: Option, /// When this rule matches, `font` specifies the styling to be used. pub font: TextStyle, } impl Config { pub fn load() -> Result { let home = std::env::home_dir().ok_or_else(|| format_err!("can't find home dir"))?; let paths = [ home.join(".config").join("wezterm").join("wezterm.toml"), home.join(".wezterm.toml"), ]; for p in paths.iter() { let mut file = match fs::File::open(p) { Ok(file) => file, Err(err) => match err.kind() { std::io::ErrorKind::NotFound => continue, _ => bail!("Error opening {}: {:?}", p.display(), err), }, }; let mut s = String::new(); file.read_to_string(&mut s)?; let cfg: Self = toml::from_str(&s) .map_err(|e| format_err!("Error parsing TOML from {}: {:?}", p.display(), e))?; return Ok(cfg.compute_extra_defaults()); } Ok(Self::default().compute_extra_defaults()) } /// In some cases we need to compute expanded values based /// on those provided by the user. This is where we do that. fn compute_extra_defaults(&self) -> Self { let mut cfg = self.clone(); if cfg.font_rules.len() == 0 { // Expand out some reasonable default font rules let bold = self.font.make_bold(); let italic = self.font.make_italic(); let bold_italic = bold.make_italic(); cfg.font_rules.push(StyleRule { italic: Some(true), font: italic, ..Default::default() }); cfg.font_rules.push(StyleRule { intensity: Some(term::Intensity::Bold), font: bold, ..Default::default() }); cfg.font_rules.push(StyleRule { italic: Some(true), intensity: Some(term::Intensity::Bold), font: bold_italic, ..Default::default() }); } cfg } } #[derive(Debug, Deserialize, Clone)] pub struct Palette { /// The text color to use when the attributes are reset to default pub foreground: Option, /// The background color to use when the attributes are reset to default pub background: Option, /// The color of the cursor pub cursor_fg: Option, pub cursor_bg: Option, /// The color of selected text pub selection_fg: Option, pub selection_bg: Option, /// A list of 8 colors corresponding to the basic ANSI palette pub ansi: Option<[RgbColor; 8]>, /// A list of 8 colors corresponding to bright versions of the /// ANSI palette pub brights: Option<[RgbColor; 8]>, } impl From for term::color::ColorPalette { fn from(cfg: Palette) -> term::color::ColorPalette { let mut p = term::color::ColorPalette::default(); macro_rules! apply_color { ($name:ident) => { if let Some($name) = cfg.$name { p.$name = $name; } }; } apply_color!(foreground); apply_color!(background); apply_color!(cursor_fg); apply_color!(cursor_bg); apply_color!(selection_fg); apply_color!(selection_bg); if let Some(ansi) = cfg.ansi { for (idx, col) in ansi.iter().enumerate() { p.colors.0[idx] = *col; } } if let Some(brights) = cfg.brights { for (idx, col) in brights.iter().enumerate() { p.colors.0[idx + 8] = *col; } } p } }