1
0
Fork 0
forked from wry/wry

toml-config: add input modes

This commit is contained in:
Julian Orth 2025-07-21 15:50:24 +02:00
parent e160aa3406
commit a57f0036a8
10 changed files with 797 additions and 46 deletions

View file

@ -27,6 +27,7 @@ mod gfx_api;
mod idle;
mod input;
mod input_match;
pub mod input_mode;
pub mod keymap;
mod libei;
mod log_level;

View file

@ -151,6 +151,8 @@ impl ActionParser<'_> {
"focus-tiles" => FocusTiles,
"create-mark" => CreateMark,
"jump-to-mark" => JumpToMark,
"clear-modes" => PopMode(false),
"pop-mode" => PopMode(true),
_ => {
return Err(
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
@ -414,6 +416,22 @@ impl ActionParser<'_> {
.map_spanned_err(ActionParserError::CopyMark)?;
Ok(Action::CopyMark(src, dst))
}
fn parse_push_mode(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
let (name,) = ext.extract((str("name"),))?;
Ok(Action::SetMode {
name: name.value.to_string(),
latch: false,
})
}
fn parse_latch_mode(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
let (name,) = ext.extract((str("name"),))?;
Ok(Action::SetMode {
name: name.value.to_string(),
latch: true,
})
}
}
impl Parser for ActionParser<'_> {
@ -471,6 +489,8 @@ impl Parser for ActionParser<'_> {
"create-mark" => self.parse_create_mark(&mut ext),
"jump-to-mark" => self.parse_jump_to_mark(&mut ext),
"copy-mark" => self.parse_copy_mark(&mut ext),
"push-mode" => self.parse_push_mode(&mut ext),
"latch-mode" => self.parse_latch_mode(&mut ext),
v => {
ext.ignore_unused();
return Err(ActionParserError::UnknownType(v.to_string()).spanned(ty.span));

View file

@ -20,6 +20,7 @@ use {
gfx_api::GfxApiParser,
idle::IdleParser,
input::InputsParser,
input_mode::InputModesParser,
keymap::KeymapParser,
libei::LibeiParser,
log_level::LogLevelParser,
@ -44,6 +45,7 @@ use {
toml_value::Value,
},
},
ahash::AHashMap,
indexmap::IndexMap,
std::collections::HashSet,
thiserror::Error,
@ -136,7 +138,7 @@ impl Parser for ConfigParser<'_> {
show_bar,
focus_history_val,
),
(middle_click_paste,),
(middle_click_paste, input_modes_val),
) = ext.extract((
(
opt(val("keymap")),
@ -186,7 +188,7 @@ impl Parser for ConfigParser<'_> {
recover(opt(bol("show-bar"))),
opt(val("focus-history")),
),
(recover(opt(bol("middle-click-paste"))),),
(recover(opt(bol("middle-click-paste"))), opt(val("modes"))),
))?;
let mut keymap = None;
if let Some(value) = keymap_val {
@ -475,6 +477,15 @@ impl Parser for ConfigParser<'_> {
}
}
}
let mut input_modes = AHashMap::new();
if let Some(value) = input_modes_val {
match value.parse(&mut InputModesParser(self.0)) {
Ok(v) => input_modes = v,
Err(e) => {
log::warn!("Could not parse the input modes: {}", self.0.error(e),);
}
}
}
Ok(Config {
keymap,
repeat_rate,
@ -516,6 +527,7 @@ impl Parser for ConfigParser<'_> {
show_bar: show_bar.despan(),
focus_history,
middle_click_paste: middle_click_paste.despan(),
input_modes,
})
}
}

View file

@ -0,0 +1,125 @@
use {
crate::{
config::{
Shortcut,
context::Context,
extractor::{Extractor, ExtractorError, opt, recover, str, val},
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
parsers::shortcuts::{ComplexShortcutsParser, ShortcutsParser, ShortcutsParserError},
spanned::SpannedErrorExt,
},
toml::{
toml_span::{DespanExt, Span, Spanned},
toml_value::Value,
},
},
ahash::{AHashMap, AHashSet},
indexmap::IndexMap,
std::collections::HashSet,
thiserror::Error,
};
#[derive(Debug, Error)]
pub enum InputModeParserError {
#[error(transparent)]
Expected(#[from] UnexpectedDataType),
#[error(transparent)]
ExtractorError(#[from] ExtractorError),
#[error("Could not parse the shortcuts")]
ParseShortcuts(#[source] ShortcutsParserError),
}
#[derive(Clone, Debug)]
pub struct InputMode {
pub parent: Option<String>,
pub shortcuts: Vec<Shortcut>,
}
pub struct InputModesParser<'a>(pub &'a Context<'a>);
impl Parser for InputModesParser<'_> {
type Value = AHashMap<String, InputMode>;
type Error = InputModeParserError;
const EXPECTED: &'static [DataType] = &[DataType::Table];
fn parse_table(
&mut self,
_span: Span,
table: &IndexMap<Spanned<String>, Spanned<Value>>,
) -> ParseResult<Self> {
let mut modes = AHashMap::new();
let mut used = AHashSet::new();
for (key, value) in table.iter() {
let mode = match value.parse(&mut InputModeParser(self.0)) {
Ok(m) => m,
Err(e) => {
log::warn!(
"Could not parse input mode {}: {}",
key.value,
self.0.error(e)
);
continue;
}
};
log_used(self.0, &mut used, key);
modes.insert(key.value.to_string(), mode);
}
Ok(modes)
}
}
pub struct InputModeParser<'a>(pub &'a Context<'a>);
impl Parser for InputModeParser<'_> {
type Value = InputMode;
type Error = InputModeParserError;
const EXPECTED: &'static [DataType] = &[DataType::Table];
fn parse_table(
&mut self,
span: Span,
table: &IndexMap<Spanned<String>, Spanned<Value>>,
) -> ParseResult<Self> {
let mut ext = Extractor::new(self.0, span, table);
let (parent, shortcuts_val, complex_shortcuts_val) = ext.extract((
recover(opt(str("parent"))),
opt(val("shortcuts")),
opt(val("complex-shortcuts")),
))?;
let mut used_keys = HashSet::new();
let mut shortcuts = vec![];
if let Some(value) = shortcuts_val {
value
.parse(&mut ShortcutsParser {
cx: self.0,
used_keys: &mut used_keys,
shortcuts: &mut shortcuts,
})
.map_spanned_err(InputModeParserError::ParseShortcuts)?;
}
if let Some(value) = complex_shortcuts_val {
value
.parse(&mut ComplexShortcutsParser {
cx: self.0,
used_keys: &mut used_keys,
shortcuts: &mut shortcuts,
})
.map_spanned_err(InputModeParserError::ParseShortcuts)?;
}
Ok(InputMode {
parent: parent.despan_into(),
shortcuts,
})
}
}
fn log_used(cx: &Context<'_>, used: &mut AHashSet<Spanned<String>>, key: &Spanned<String>) {
if let Some(prev) = used.get(key) {
log::warn!(
"Duplicate input mode overrides previous definition: {}",
cx.error3(key.span)
);
log::info!("Previous definition here: {}", cx.error3(prev.span));
}
used.insert(key.clone());
}