aboutsummaryrefslogtreecommitdiff
path: root/alacritty/src/config/ui_config.rs
diff options
context:
space:
mode:
Diffstat (limited to 'alacritty/src/config/ui_config.rs')
-rw-r--r--alacritty/src/config/ui_config.rs168
1 files changed, 166 insertions, 2 deletions
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!(),
+ }
+ }
+}