aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/config
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty/src/config')
-rw-r--r--alacritty/src/config/bindings.rs8
-rw-r--r--alacritty/src/config/color.rs37
-rw-r--r--alacritty/src/config/mod.rs3
-rw-r--r--alacritty/src/config/ui_config.rs168
4 files changed, 213 insertions, 3 deletions
diff --git a/alacritty/src/config/bindings.rs b/alacritty/src/config/bindings.rs
index 4d0fcadd..732875db 100644
--- a/alacritty/src/config/bindings.rs
+++ b/alacritty/src/config/bindings.rs
@@ -16,6 +16,8 @@ use alacritty_terminal::config::Program;
use alacritty_terminal::term::TermMode;
use alacritty_terminal::vi_mode::ViMotion;
+use crate::config::ui_config::Hint;
+
/// Describes a state and action to take in that state.
///
/// This is the shared component of `MouseBinding` and `KeyBinding`.
@@ -91,6 +93,10 @@ pub enum Action {
#[config(skip)]
Command(Program),
+ /// Regex keyboard hints.
+ #[config(skip)]
+ Hint(Hint),
+
/// Move vi mode cursor.
#[config(skip)]
ViMotion(ViMotion),
@@ -1132,7 +1138,7 @@ impl<'a> Deserialize<'a> for KeyBinding {
/// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the
/// impl below.
#[derive(Debug, Copy, Clone, Hash, Default, Eq, PartialEq)]
-pub struct ModsWrapper(ModifiersState);
+pub struct ModsWrapper(pub ModifiersState);
impl ModsWrapper {
pub fn into_inner(self) -> ModifiersState {
diff --git a/alacritty/src/config/color.rs b/alacritty/src/config/color.rs
index cd5d964d..d55cf26f 100644
--- a/alacritty/src/config/color.rs
+++ b/alacritty/src/config/color.rs
@@ -16,6 +16,7 @@ pub struct Colors {
pub indexed_colors: Vec<IndexedColor>,
pub search: SearchColors,
pub line_indicator: LineIndicatorColors,
+ pub hints: HintColors,
}
impl Colors {
@@ -34,6 +35,42 @@ pub struct LineIndicatorColors {
pub background: Option<Rgb>,
}
+#[derive(ConfigDeserialize, Default, Copy, Clone, Debug, PartialEq, Eq)]
+pub struct HintColors {
+ pub start: HintStartColors,
+ pub end: HintEndColors,
+}
+
+#[derive(ConfigDeserialize, Copy, Clone, Debug, PartialEq, Eq)]
+pub struct HintStartColors {
+ pub foreground: CellRgb,
+ pub background: CellRgb,
+}
+
+impl Default for HintStartColors {
+ fn default() -> Self {
+ Self {
+ foreground: CellRgb::Rgb(Rgb { r: 0x1d, g: 0x1f, b: 0x21 }),
+ background: CellRgb::Rgb(Rgb { r: 0xe9, g: 0xff, b: 0x5e }),
+ }
+ }
+}
+
+#[derive(ConfigDeserialize, Copy, Clone, Debug, PartialEq, Eq)]
+pub struct HintEndColors {
+ pub foreground: CellRgb,
+ pub background: CellRgb,
+}
+
+impl Default for HintEndColors {
+ fn default() -> Self {
+ Self {
+ foreground: CellRgb::Rgb(Rgb { r: 0xe9, g: 0xff, b: 0x5e }),
+ background: CellRgb::Rgb(Rgb { r: 0x1d, g: 0x1f, b: 0x21 }),
+ }
+ }
+}
+
#[derive(Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq)]
pub struct IndexedColor {
pub color: Rgb,
diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs
index c321915e..4ddc81c2 100644
--- a/alacritty/src/config/mod.rs
+++ b/alacritty/src/config/mod.rs
@@ -159,6 +159,9 @@ fn read_config(path: &Path, cli_config: Value) -> Result<Config> {
let mut config = Config::deserialize(config_value)?;
config.ui_config.config_paths = config_paths;
+ // Create key bindings for regex hints.
+ config.ui_config.generate_hint_bindings();
+
Ok(config)
}
diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs
index 2d7b5c98..6d06aa7d 100644
--- a/alacritty/src/config/ui_config.rs
+++ b/alacritty/src/config/ui_config.rs
@@ -1,13 +1,20 @@
+use std::cell::RefCell;
use std::path::PathBuf;
+use std::rc::Rc;
use log::error;
+use serde::de::Error as SerdeError;
use serde::{Deserialize, Deserializer};
+use unicode_width::UnicodeWidthChar;
use alacritty_config_derive::ConfigDeserialize;
-use alacritty_terminal::config::{Percentage, LOG_TARGET_CONFIG};
+use alacritty_terminal::config::{Percentage, Program, LOG_TARGET_CONFIG};
+use alacritty_terminal::term::search::RegexSearch;
use crate::config::bell::BellConfig;
-use crate::config::bindings::{self, Binding, KeyBinding, MouseBinding};
+use crate::config::bindings::{
+ self, Action, Binding, BindingMode, Key, KeyBinding, ModsWrapper, MouseBinding,
+};
use crate::config::color::Colors;
use crate::config::debug::Debug;
use crate::config::font::Font;
@@ -46,6 +53,9 @@ pub struct UiConfig {
#[config(skip)]
pub config_paths: Vec<PathBuf>,
+ /// Regex hints for interacting with terminal content.
+ pub hints: Hints,
+
/// Keybindings.
key_bindings: KeyBindings,
@@ -72,11 +82,27 @@ impl Default for UiConfig {
bell: Default::default(),
colors: Default::default(),
draw_bold_text_with_bright_colors: Default::default(),
+ hints: Default::default(),
}
}
}
impl UiConfig {
+ /// Generate key bindings for all keyboard hints.
+ pub fn generate_hint_bindings(&mut self) {
+ for hint in self.hints.enabled.drain(..) {
+ let binding = KeyBinding {
+ trigger: hint.binding.key,
+ mods: hint.binding.mods.0,
+ mode: BindingMode::empty(),
+ notmode: BindingMode::empty(),
+ action: Action::Hint(hint),
+ };
+
+ self.key_bindings.0.push(binding);
+ }
+ }
+
#[inline]
pub fn background_opacity(&self) -> f32 {
self.background_opacity.as_f32()
@@ -169,3 +195,141 @@ pub struct Delta<T: Default> {
/// Vertical change.
pub y: T,
}
+
+/// Regex terminal hints.
+#[derive(ConfigDeserialize, Default, Debug, PartialEq, Eq)]
+pub struct Hints {
+ /// Characters for the hint labels.
+ alphabet: HintsAlphabet,
+
+ /// All configured terminal hints.
+ enabled: Vec<Hint>,
+}
+
+impl Hints {
+ /// Characters for the hint labels.
+ pub fn alphabet(&self) -> &str {
+ &self.alphabet.0
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct HintsAlphabet(String);
+
+impl Default for HintsAlphabet {
+ fn default() -> Self {
+ Self(String::from("jfkdls;ahgurieowpq"))
+ }
+}
+
+impl<'de> Deserialize<'de> for HintsAlphabet {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let value = String::deserialize(deserializer)?;
+
+ let mut character_count = 0;
+ for character in value.chars() {
+ if character.width() != Some(1) {
+ return Err(D::Error::custom("characters must be of width 1"));
+ }
+ character_count += 1;
+ }
+
+ if character_count < 2 {
+ return Err(D::Error::custom("must include at last 2 characters"));
+ }
+
+ Ok(Self(value))
+ }
+}
+
+/// Hint configuration.
+#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
+pub struct Hint {
+ /// Command the text will be piped to.
+ pub command: Program,
+
+ /// Regex for finding matches.
+ pub regex: LazyRegex,
+
+ /// Binding required to search for this hint.
+ binding: HintBinding,
+}
+
+/// Binding for triggering a keyboard hint.
+#[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq)]
+pub struct HintBinding {
+ pub key: Key,
+ pub mods: ModsWrapper,
+}
+
+/// Lazy regex with interior mutability.
+#[derive(Clone, Debug)]
+pub struct LazyRegex(Rc<RefCell<LazyRegexVariant>>);
+
+impl LazyRegex {
+ /// Execute a function with the compiled regex DFAs as parameter.
+ pub fn with_compiled<T, F>(&self, f: F) -> T
+ where
+ F: Fn(&RegexSearch) -> T,
+ {
+ f(self.0.borrow_mut().compiled())
+ }
+}
+
+impl<'de> Deserialize<'de> for LazyRegex {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let regex = LazyRegexVariant::Pattern(String::deserialize(deserializer)?);
+ Ok(Self(Rc::new(RefCell::new(regex))))
+ }
+}
+
+/// Implement placeholder to allow derive upstream, since we never need it for this struct itself.
+impl PartialEq for LazyRegex {
+ fn eq(&self, _other: &Self) -> bool {
+ false
+ }
+}
+impl Eq for LazyRegex {}
+
+/// Regex which is compiled on demand, to avoid expensive computations at startup.
+#[derive(Clone, Debug)]
+pub enum LazyRegexVariant {
+ Compiled(Box<RegexSearch>),
+ Pattern(String),
+}
+
+impl LazyRegexVariant {
+ /// Get a reference to the compiled regex.
+ ///
+ /// If the regex is not already compiled, this will compile the DFAs and store them for future
+ /// access.
+ fn compiled(&mut self) -> &RegexSearch {
+ // Check if the regex has already been compiled.
+ let regex = match self {
+ Self::Compiled(regex_search) => return regex_search,
+ Self::Pattern(regex) => regex,
+ };
+
+ // Compile the regex.
+ let regex_search = match RegexSearch::new(&regex) {
+ Ok(regex_search) => regex_search,
+ Err(error) => {
+ error!("hint regex is invalid: {}", error);
+ RegexSearch::new("").unwrap()
+ },
+ };
+ *self = Self::Compiled(Box::new(regex_search));
+
+ // Return a reference to the compiled DFAs.
+ match self {
+ Self::Compiled(dfas) => dfas,
+ Self::Pattern(_) => unreachable!(),
+ }
+ }
+}