kanata_parser/cfg/
mod.rs

1//! This parses the configuration language to create a `kanata_keyberon::layout::Layout` as well as
2//! associated metadata to help with processing.
3//!
4//! How the configuration maps to keyberon:
5//!
6//! If the mapped keys are defined as:
7//!
8//! (defsrc
9//!     esc  1    2    3    4
10//! )
11//!
12//! and the layers are:
13//!
14//! (deflayer one
15//!     _   a    s    d    _
16//! )
17//!
18//! (deflayer two
19//!     _   a    o    e    _
20//! )
21//!
22//! Then the keyberon layers will be as follows:
23//!
24//! (xx means unimportant and _ means transparent)
25//!
26//! layers[0] = { xx, esc, a, s, d, 4, xx... }
27//! layers[1] = { xx, _  , a, s, d, _, xx... }
28//! layers[2] = { xx, esc, a, o, e, 4, xx... }
29//! layers[3] = { xx, _  , a, o, e, _, xx... }
30//!
31//! Note that this example isn't practical, but `(defsrc esc 1 2 3 4)` is used because these keys
32//! are at the beginning of the array. The column index for layers is the numerical value of
33//! the key from `keys::OsCode`.
34//!
35//! In addition, there are two versions of each layer. One version delegates transparent entries to
36//! the key defined in defsrc, while the other keeps them as actually transparent. This is to match
37//! the behaviour in kmonad.
38//!
39//! The specific values in example above applies to Linux, but the same logic applies to Windows.
40
41pub mod sexpr;
42
43pub(crate) mod alloc;
44use alloc::*;
45
46use crate::sequences::*;
47use kanata_keyberon::chord::ChordsV2;
48
49mod key_override;
50pub use key_override::*;
51
52mod custom_tap_hold;
53use custom_tap_hold::*;
54
55pub mod layer_opts;
56use layer_opts::*;
57
58pub mod list_actions;
59use list_actions::*;
60
61mod defcfg;
62pub use defcfg::*;
63
64mod deftemplate;
65pub use deftemplate::*;
66
67mod switch;
68pub use switch::*;
69
70use crate::custom_action::*;
71use crate::keys::*;
72use crate::layers::*;
73
74mod error;
75pub use error::*;
76
77mod chord;
78use chord::*;
79
80mod fake_key;
81use fake_key::*;
82pub use fake_key::{FAKE_KEY_ROW, NORMAL_KEY_ROW};
83
84mod platform;
85use platform::*;
86
87mod is_a_button;
88use is_a_button::*;
89
90mod key_outputs;
91pub use key_outputs::*;
92
93mod permutations;
94use permutations::*;
95
96mod zippychord;
97pub use zippychord::*;
98
99use crate::lsp_hints::{self, LspHints};
100
101mod str_ext;
102pub use str_ext::*;
103
104use crate::trie::Trie;
105use anyhow::anyhow;
106use ordered_float::OrderedFloat;
107use std::cell::Cell;
108use std::cell::RefCell;
109use std::collections::hash_map::Entry;
110use std::path::Path;
111use std::path::PathBuf;
112use std::sync::Arc;
113
114type HashSet<T> = rustc_hash::FxHashSet<T>;
115type HashMap<K, V> = rustc_hash::FxHashMap<K, V>;
116
117use kanata_keyberon::action::*;
118use kanata_keyberon::key_code::*;
119use kanata_keyberon::layout::*;
120use sexpr::*;
121
122#[cfg(test)]
123mod tests;
124#[cfg(test)]
125pub use sexpr::parse;
126
127#[macro_export]
128macro_rules! bail {
129    ($err:expr $(,)?) => {
130        return Err(ParseError::from(anyhow::anyhow!($err)))
131    };
132    ($fmt:expr, $($arg:tt)*) => {
133        return Err(ParseError::from(anyhow::anyhow!($fmt, $($arg)*)))
134    };
135}
136
137#[macro_export]
138macro_rules! bail_expr {
139    ($expr:expr, $fmt:expr $(,)?) => {
140        return Err(ParseError::from_expr($expr, format!($fmt)))
141    };
142    ($expr:expr, $fmt:expr, $($arg:tt)*) => {
143        return Err(ParseError::from_expr($expr, format!($fmt, $($arg)*)))
144    };
145}
146
147#[macro_export]
148macro_rules! err_expr {
149    ($expr:expr, $fmt:expr $(,)?) => {
150        Err(ParseError::from_expr($expr, format!($fmt)))
151    };
152    ($expr:expr, $fmt:expr, $($arg:tt)*) => {
153        Err(ParseError::from_expr($expr, format!($fmt, $($arg)*)))
154    };
155}
156
157#[macro_export]
158macro_rules! bail_span {
159    ($expr:expr, $fmt:expr $(,)?) => {
160        return Err(ParseError::from_spanned($expr, format!($fmt)))
161    };
162    ($expr:expr, $fmt:expr, $($arg:tt)*) => {
163        return Err(ParseError::from_spanned($expr, format!($fmt, $($arg)*)))
164    };
165}
166
167#[macro_export]
168macro_rules! err_span {
169    ($expr:expr, $fmt:expr $(,)?) => {
170        Err(ParseError::from_spanned($expr, format!($fmt)))
171    };
172    ($expr:expr, $fmt:expr, $($arg:tt)*) => {
173        Err(ParseError::from_spanned($expr, format!($fmt, $($arg)*)))
174    };
175}
176
177#[macro_export]
178macro_rules! anyhow_expr {
179    ($expr:expr, $fmt:expr $(,)?) => {
180        ParseError::from_expr($expr, format!($fmt))
181    };
182    ($expr:expr, $fmt:expr, $($arg:tt)*) => {
183        ParseError::from_expr($expr, format!($fmt, $($arg)*))
184    };
185}
186
187#[macro_export]
188macro_rules! anyhow_span {
189    ($expr:expr, $fmt:expr $(,)?) => {
190        ParseError::from_spanned($expr, format!($fmt))
191    };
192    ($expr:expr, $fmt:expr, $($arg:tt)*) => {
193        ParseError::from_spanned($expr, format!($fmt, $($arg)*))
194    };
195}
196
197pub struct FileContentProvider<'a> {
198    /// A function to load content of a file from a filepath.
199    /// Optionally, it could implement caching and a mechanism preventing "file" and "./file"
200    /// from loading twice.
201    get_file_content_fn: &'a mut dyn FnMut(&Path) -> std::result::Result<String, String>,
202}
203
204impl<'a> FileContentProvider<'a> {
205    pub fn new(
206        get_file_content_fn: &'a mut impl FnMut(&Path) -> std::result::Result<String, String>,
207    ) -> Self {
208        Self {
209            get_file_content_fn,
210        }
211    }
212    pub fn get_file_content(&mut self, filename: &Path) -> std::result::Result<String, String> {
213        (self.get_file_content_fn)(filename)
214    }
215}
216
217pub type KanataCustom = &'static &'static [&'static CustomAction];
218pub type KanataAction = Action<'static, KanataCustom>;
219type KLayout = Layout<'static, KEYS_IN_ROW, 2, KanataCustom>;
220
221type TapHoldCustomFunc =
222    fn(
223        &[OsCode],
224        &Allocations,
225    ) -> &'static (dyn Fn(QueuedIter) -> (Option<WaitingAction>, bool) + Send + Sync);
226
227pub type BorrowedKLayout<'a> = Layout<'a, KEYS_IN_ROW, 2, &'a &'a [&'a CustomAction]>;
228pub type KeySeqsToFKeys = Trie<(u8, u16)>;
229
230pub struct KanataLayout {
231    layout: KLayout,
232    _allocations: Arc<Allocations>,
233}
234
235impl KanataLayout {
236    fn new(layout: KLayout, a: Arc<Allocations>) -> Self {
237        Self {
238            layout,
239            _allocations: a,
240        }
241    }
242
243    /// bm stands for borrow mut.
244    pub fn bm(&mut self) -> &mut BorrowedKLayout<'_> {
245        // shrink the lifetime
246        unsafe { std::mem::transmute(&mut self.layout) }
247    }
248
249    /// b stands for borrow.
250    pub fn b(&self) -> &BorrowedKLayout<'_> {
251        // shrink the lifetime
252        unsafe { std::mem::transmute(&self.layout) }
253    }
254}
255
256pub struct Cfg {
257    /// The list of keys that kanata should be processing. Keys that are missing from `mapped_keys`
258    /// that are received from the OS input mechanism will be forwarded to OS output mechanism
259    /// without going through kanata's processing.
260    pub mapped_keys: MappedKeys,
261    /// The potential outputs for a physical key position. The intention behind this is for sending
262    /// key repeats.
263    pub key_outputs: KeyOutputs,
264    /// Layer info used for printing to the logs.
265    pub layer_info: Vec<LayerInfo>,
266    /// Configuration items in `defcfg`.
267    pub options: CfgOptions,
268    /// The keyberon layout state machine struct.
269    pub layout: KanataLayout,
270    /// Sequences defined in `defseq`.
271    pub sequences: KeySeqsToFKeys,
272    /// Overrides defined in `defoverrides`.
273    pub overrides: Overrides,
274    /// Mapping of fake key name to its column in the fake key row.
275    pub fake_keys: HashMap<String, usize>,
276    /// The maximum value of switch's key-timing item in the configuration.
277    pub switch_max_key_timing: u16,
278    /// Zipchord-like configuration.
279    pub zippy: Option<(ZchPossibleChords, ZchConfig)>,
280}
281
282/// Parse a new configuration from a file.
283pub fn new_from_file(p: &Path) -> MResult<Cfg> {
284    parse_cfg(p)
285}
286
287pub fn new_from_str(cfg_text: &str, file_content: HashMap<String, String>) -> MResult<Cfg> {
288    let mut s = ParserState::default();
289    let icfg = parse_cfg_raw_string(
290        cfg_text,
291        &mut s,
292        &PathBuf::from("configuration"),
293        &mut FileContentProvider {
294            get_file_content_fn: &mut move |fname| match file_content
295                .get(fname.to_string_lossy().as_ref())
296            {
297                Some(s) => Ok(s.clone()),
298                None => Err("File is not known".into()),
299            },
300        },
301        DEF_LOCAL_KEYS,
302        Err("environment variables are not supported".into()),
303    )?;
304    let (layers, allocations) = icfg.klayers.get();
305    let key_outputs = create_key_outputs(&layers, &icfg.overrides, &icfg.chords_v2);
306    let switch_max_key_timing = s.switch_max_key_timing.get();
307    let mut layout = KanataLayout::new(
308        Layout::new_with_trans_action_settings(
309            s.a.sref(s.defsrc_layer),
310            layers,
311            icfg.options.trans_resolution_behavior_v2,
312            icfg.options.delegate_to_first_layer,
313        ),
314        allocations,
315    );
316    layout.bm().chords_v2 = icfg.chords_v2;
317    layout.bm().quick_tap_hold_timeout = icfg.options.concurrent_tap_hold;
318    layout.bm().oneshot.pause_input_processing_delay = icfg.options.rapid_event_delay;
319    let mut fake_keys: HashMap<String, usize> = s
320        .virtual_keys
321        .iter()
322        .map(|(k, v)| (k.clone(), v.0))
323        .collect();
324    fake_keys.shrink_to_fit();
325    log::info!("config file is valid");
326    Ok(Cfg {
327        options: icfg.options,
328        mapped_keys: icfg.mapped_keys,
329        layer_info: icfg.layer_info,
330        key_outputs,
331        layout,
332        sequences: icfg.sequences,
333        overrides: icfg.overrides,
334        fake_keys,
335        switch_max_key_timing,
336        zippy: icfg.zippy,
337    })
338}
339
340pub type MappedKeys = HashSet<OsCode>;
341
342#[derive(Debug)]
343pub struct LayerInfo {
344    pub name: String,
345    pub cfg_text: String,
346    pub icon: Option<String>,
347}
348
349#[allow(clippy::type_complexity)] // return type is not pub
350fn parse_cfg(p: &Path) -> MResult<Cfg> {
351    let mut s = ParserState::default();
352    let icfg = parse_cfg_raw(p, &mut s)?;
353    let (layers, allocations) = icfg.klayers.get();
354    let key_outputs = create_key_outputs(&layers, &icfg.overrides, &icfg.chords_v2);
355    let switch_max_key_timing = s.switch_max_key_timing.get();
356    let mut layout = KanataLayout::new(
357        Layout::new_with_trans_action_settings(
358            s.a.sref(s.defsrc_layer),
359            layers,
360            icfg.options.trans_resolution_behavior_v2,
361            icfg.options.delegate_to_first_layer,
362        ),
363        allocations,
364    );
365    layout.bm().chords_v2 = icfg.chords_v2;
366    layout.bm().quick_tap_hold_timeout = icfg.options.concurrent_tap_hold;
367    layout.bm().oneshot.pause_input_processing_delay = icfg.options.rapid_event_delay;
368    if let Some(s) = icfg.start_action {
369        layout.bm().action_queue.push_front(Some(((1, 0), 0, s)));
370    }
371    let mut fake_keys: HashMap<String, usize> = s
372        .virtual_keys
373        .iter()
374        .map(|(k, v)| (k.clone(), v.0))
375        .collect();
376    fake_keys.shrink_to_fit();
377    log::info!("config file is valid");
378    Ok(Cfg {
379        options: icfg.options,
380        mapped_keys: icfg.mapped_keys,
381        layer_info: icfg.layer_info,
382        key_outputs,
383        layout,
384        sequences: icfg.sequences,
385        overrides: icfg.overrides,
386        fake_keys,
387        switch_max_key_timing,
388        zippy: icfg.zippy,
389    })
390}
391
392#[cfg(all(
393    not(feature = "interception_driver"),
394    any(
395        not(feature = "win_llhook_read_scancodes"),
396        not(feature = "win_sendinput_send_scancodes")
397    ),
398    target_os = "windows"
399))]
400const DEF_LOCAL_KEYS: &str = "deflocalkeys-win";
401#[cfg(all(
402    feature = "win_llhook_read_scancodes",
403    feature = "win_sendinput_send_scancodes",
404    not(feature = "interception_driver"),
405    target_os = "windows"
406))]
407const DEF_LOCAL_KEYS: &str = "deflocalkeys-winiov2";
408#[cfg(all(feature = "interception_driver", target_os = "windows"))]
409const DEF_LOCAL_KEYS: &str = "deflocalkeys-wintercept";
410#[cfg(target_os = "macos")]
411const DEF_LOCAL_KEYS: &str = "deflocalkeys-macos";
412#[cfg(any(target_os = "linux", target_os = "android", target_os = "unknown"))]
413const DEF_LOCAL_KEYS: &str = "deflocalkeys-linux";
414
415fn deflocalkeys_variant_applies_to_current_os(variant: &str) -> bool {
416    variant == DEF_LOCAL_KEYS
417}
418
419#[derive(Debug)]
420pub struct IntermediateCfg {
421    pub options: CfgOptions,
422    pub mapped_keys: MappedKeys,
423    pub layer_info: Vec<LayerInfo>,
424    pub klayers: KanataLayers,
425    pub sequences: KeySeqsToFKeys,
426    pub overrides: Overrides,
427    pub chords_v2: Option<ChordsV2<'static, KanataCustom>>,
428    pub start_action: Option<&'static KanataAction>,
429    pub zippy: Option<(ZchPossibleChords, ZchConfig)>,
430}
431
432// A snapshot of enviroment variables, or an error message with an explanation
433// why env vars are not supported.
434pub type EnvVars = std::result::Result<Vec<(String, String)>, String>;
435
436#[allow(clippy::type_complexity)] // return type is not pub
437fn parse_cfg_raw(p: &Path, s: &mut ParserState) -> MResult<IntermediateCfg> {
438    const INVALID_PATH_ERROR: &str = "The provided config file path is not valid";
439
440    let mut loaded_files: HashSet<PathBuf> = HashSet::default();
441
442    let mut get_file_content_fn_impl = |filepath: &Path| {
443        // Make the include paths relative to main config file instead of kanata executable.
444        let filepath_relative_to_loaded_kanata_cfg = if filepath.is_absolute() {
445            filepath.to_owned()
446        } else {
447            let relative_main_cfg_file_dir = p.parent().ok_or(INVALID_PATH_ERROR)?;
448            relative_main_cfg_file_dir.join(filepath)
449        };
450
451        let Ok(abs_filepath) = filepath_relative_to_loaded_kanata_cfg.canonicalize() else {
452            log::info!(
453                "Failed to resolve relative path: {}. Ignoring this file.",
454                filepath_relative_to_loaded_kanata_cfg.to_string_lossy()
455            );
456            return Ok("".to_owned());
457        };
458
459        // Forbid loading the same file multiple times.
460        // This prevents a potential recursive infinite loop of includes
461        // (if includes within includes were to be allowed).
462        if !loaded_files.insert(abs_filepath.clone()) {
463            return Err("The provided config file was already included before".to_string());
464        };
465
466        std::fs::read_to_string(abs_filepath.to_str().ok_or(INVALID_PATH_ERROR)?)
467            .map_err(|e| format!("Failed to include file: {e}"))
468    };
469    let mut file_content_provider = FileContentProvider::new(&mut get_file_content_fn_impl);
470
471    // `get_file_content_fn_impl` already uses CWD of the main config path,
472    // so we need to provide only the name, not the whole path.
473    let cfg_file_name: PathBuf = p
474        .file_name()
475        .ok_or_else(|| miette::miette!(INVALID_PATH_ERROR))?
476        .into();
477    let text = file_content_provider
478        .get_file_content(&cfg_file_name)
479        .map_err(|e| miette::miette!(e))?;
480
481    let env_vars: EnvVars = Ok(std::env::vars().collect());
482
483    parse_cfg_raw_string(
484        &text,
485        s,
486        p,
487        &mut file_content_provider,
488        DEF_LOCAL_KEYS,
489        env_vars,
490    )
491    .map_err(|e| e.into())
492}
493
494fn expand_includes(
495    xs: Vec<TopLevel>,
496    file_content_provider: &mut FileContentProvider,
497    _lsp_hints: &mut LspHints,
498) -> Result<Vec<TopLevel>> {
499    let include_is_first_atom = gen_first_atom_filter("include");
500    xs.iter().try_fold(Vec::new(), |mut acc, spanned_exprs| {
501        if include_is_first_atom(&&spanned_exprs.t) {
502            let mut exprs =
503                check_first_expr(spanned_exprs.t.iter(), "include").expect("can't fail");
504
505            let expr = exprs.next().ok_or(anyhow_span!(
506                spanned_exprs,
507                "Every include block must contain exactly one filepath"
508            ))?;
509
510            let spanned_filepath = match expr {
511                SExpr::Atom(filepath) => filepath,
512                SExpr::List(_) => {
513                    bail_expr!(expr, "Filepath cannot be a list")
514                }
515            };
516
517            if let Some(expr) = exprs.next() {
518                bail_expr!(
519                    expr,
520                    "Multiple filepaths are not allowed in include blocks. If you want to include multiple files, create a new include block for each of them."
521                )
522            };
523            let include_file_path = spanned_filepath.t.trim_atom_quotes();
524            let file_content = file_content_provider.get_file_content(Path::new(include_file_path))
525                .map_err(|e| anyhow_span!(spanned_filepath, "{e}"))?;
526            let tree = sexpr::parse(&file_content, include_file_path)?;
527            acc.extend(tree);
528
529            #[cfg(feature = "lsp")]
530            _lsp_hints.reference_locations.include.push_from_atom(spanned_filepath);
531
532            Ok(acc)
533        } else {
534            acc.push(spanned_exprs.clone());
535            Ok(acc)
536        }
537    })
538}
539
540const DEFLAYER: &str = "deflayer";
541const DEFLAYER_MAPPED: &str = "deflayermap";
542const DEFLOCALKEYS_VARIANTS: &[&str] = &[
543    "deflocalkeys-win",
544    "deflocalkeys-winiov2",
545    "deflocalkeys-wintercept",
546    "deflocalkeys-linux",
547    "deflocalkeys-macos",
548];
549
550#[cfg(feature = "lsp")]
551thread_local! {
552    pub(crate) static LSP_VARIABLE_REFERENCES: RefCell<crate::lsp_hints::ReferencesMap> =
553        RefCell::new(crate::lsp_hints::ReferencesMap::default());
554}
555
556#[allow(clippy::type_complexity)] // return type is not pub
557pub fn parse_cfg_raw_string(
558    text: &str,
559    s: &mut ParserState,
560    cfg_path: &Path,
561    file_content_provider: &mut FileContentProvider,
562    def_local_keys_variant_to_apply: &str,
563    env_vars: EnvVars,
564) -> Result<IntermediateCfg> {
565    let mut lsp_hints: LspHints = Default::default();
566
567    let spanned_root_exprs = sexpr::parse(text, &cfg_path.to_string_lossy())
568        .and_then(|xs| expand_includes(xs, file_content_provider, &mut lsp_hints))
569        .and_then(|xs| {
570            filter_platform_specific_cfg(xs, def_local_keys_variant_to_apply, &mut lsp_hints)
571        })
572        .and_then(|xs| filter_env_specific_cfg(xs, &env_vars, &mut lsp_hints))
573        .and_then(|xs| expand_templates(xs, &mut lsp_hints))?;
574
575    if let Some(spanned) = spanned_root_exprs
576        .iter()
577        .find(gen_first_atom_filter_spanned("include"))
578    {
579        bail_span!(spanned, "Nested includes are not allowed.")
580    }
581
582    let root_exprs: Vec<_> = spanned_root_exprs.iter().map(|t| t.t.clone()).collect();
583
584    error_on_unknown_top_level_atoms(&spanned_root_exprs)?;
585
586    let mut local_keys: Option<HashMap<String, OsCode>> = None;
587    clear_custom_str_oscode_mapping();
588    for def_local_keys_variant in DEFLOCALKEYS_VARIANTS {
589        let Some((result, _span)) = spanned_root_exprs
590            .iter()
591            .find(gen_first_atom_filter_spanned(def_local_keys_variant))
592            .map(|x| {
593                (
594                    parse_deflocalkeys(def_local_keys_variant, &x.t),
595                    x.span.clone(),
596                )
597            })
598        else {
599            continue;
600        };
601
602        let mapping = result?;
603        if def_local_keys_variant == &def_local_keys_variant_to_apply {
604            assert!(
605                local_keys.is_none(),
606                ">1 mutually exclusive deflocalkeys variants were parsed"
607            );
608            local_keys = Some(mapping);
609        } else {
610            #[cfg(feature = "lsp")]
611            lsp_hints.inactive_code.push(lsp_hints::InactiveCode {
612                span: _span,
613                reason: format!(
614                    "Another localkeys variant is currently active: {def_local_keys_variant_to_apply}"
615                    ),
616            })
617        }
618
619        if let Some(spanned) = spanned_root_exprs
620            .iter()
621            .filter(gen_first_atom_filter_spanned(def_local_keys_variant))
622            .nth(1)
623        {
624            bail_span!(
625                spanned,
626                "Only one {def_local_keys_variant} is allowed, found more. Delete the extras."
627            )
628        }
629    }
630    replace_custom_str_oscode_mapping(&local_keys.unwrap_or_default());
631
632    #[allow(unused_mut)]
633    let mut cfg = root_exprs
634        .iter()
635        .find(gen_first_atom_filter("defcfg"))
636        .map(|cfg| parse_defcfg(cfg))
637        .transpose()?
638        .unwrap_or_else(|| {
639            log::warn!("No defcfg is defined. Consider whether the process-unmapped-keys defcfg option should be yes vs. no. Adding defcfg with process-unmapped-keys defined will remove this warning.");
640            Default::default()
641        });
642    if let Some(spanned) = spanned_root_exprs
643        .iter()
644        .filter(gen_first_atom_filter_spanned("defcfg"))
645        .nth(1)
646    {
647        bail_span!(
648            spanned,
649            "Only one defcfg is allowed, found more. Delete the extras."
650        )
651    }
652    let src_expr = root_exprs
653        .iter()
654        .find(gen_first_atom_filter("defsrc"))
655        .ok_or_else(|| anyhow!("Exactly one defsrc must exist; found none"))?;
656    if let Some(spanned) = spanned_root_exprs
657        .iter()
658        .filter(gen_first_atom_filter_spanned("defsrc"))
659        .nth(1)
660    {
661        bail_span!(
662            spanned,
663            "Exactly one defsrc is allowed, found more. Delete the extras."
664        )
665    }
666    let (mut mapped_keys, mapping_order, _mouse_in_defsrc) = parse_defsrc(src_expr, &cfg)?;
667    #[cfg(any(target_os = "linux", target_os = "android", target_os = "unknown"))]
668    if cfg.linux_opts.linux_device_detect_mode.is_none() {
669        cfg.linux_opts.linux_device_detect_mode = Some(match _mouse_in_defsrc {
670            MouseInDefsrc::MouseUsed => DeviceDetectMode::Any,
671            MouseInDefsrc::NoMouse => DeviceDetectMode::KeyboardMice,
672        });
673    }
674
675    let var_exprs = root_exprs
676        .iter()
677        .filter(gen_first_atom_filter("defvar"))
678        .collect::<Vec<_>>();
679    let vars = parse_vars(&var_exprs, &mut lsp_hints)?;
680
681    let deflayer_labels = [DEFLAYER, DEFLAYER_MAPPED];
682    let deflayer_filter = |exprs: &&Vec<SExpr>| -> bool {
683        if exprs.is_empty() {
684            return false;
685        }
686        if let SExpr::Atom(atom) = &exprs[0] {
687            deflayer_labels.contains(&atom.t.as_str())
688        } else {
689            false
690        }
691    };
692    let deflayer_spanned_filter =
693        |exprs: &&Spanned<Vec<SExpr>>| -> bool { deflayer_filter(&&exprs.t) };
694    let layer_exprs = spanned_root_exprs
695        .iter()
696        .filter(deflayer_spanned_filter)
697        .cloned()
698        .map(|e| match e.t[0].atom(None).unwrap() {
699            DEFLAYER => SpannedLayerExprs::DefsrcMapping(e.clone()),
700            DEFLAYER_MAPPED => SpannedLayerExprs::CustomMapping(e.clone()),
701            _ => unreachable!(),
702        })
703        .collect::<Vec<_>>();
704    if layer_exprs.is_empty() {
705        bail!("No deflayer expressions exist. At least one layer must be defined.")
706    }
707
708    let (layer_idxs, layer_icons) =
709        parse_layer_indexes(&layer_exprs, mapping_order.len(), &vars, &mut lsp_hints)?;
710    let mut sorted_idxs: Vec<(&String, &usize)> =
711        layer_idxs.iter().map(|tuple| (tuple.0, tuple.1)).collect();
712
713    sorted_idxs.sort_by_key(|f| f.1);
714
715    #[allow(clippy::needless_collect)]
716    // Clippy suggests using the sorted_idxs iter directly and manipulating it
717    // to produce the layer_names vec when creating Vec<LayerInfo> below
718    let layer_names = sorted_idxs
719        .into_iter()
720        .map(|(name, _)| (*name).clone())
721        .collect::<Vec<_>>();
722
723    let layer_strings = spanned_root_exprs
724        .iter()
725        .filter(|expr| deflayer_filter(&&expr.t))
726        .map(|expr| expr.span.file_content()[expr.span.clone()].to_string())
727        .collect::<Vec<_>>();
728
729    let layer_info: Vec<LayerInfo> = layer_names
730        .into_iter()
731        .zip(layer_strings)
732        .map(|(name, cfg_text)| LayerInfo {
733            name: name.clone(),
734            cfg_text,
735            icon: layer_icons.get(&name).unwrap_or(&None).clone(),
736        })
737        .collect();
738
739    let defsrc_layer = create_defsrc_layer();
740
741    let deflayer_filter = |exprs: &&Vec<SExpr>| -> bool {
742        if exprs.is_empty() {
743            return false;
744        }
745        if let SExpr::Atom(atom) = &exprs[0] {
746            deflayer_labels.contains(&atom.t.as_str())
747        } else {
748            false
749        }
750    };
751    let layer_exprs = root_exprs
752        .iter()
753        .filter(deflayer_filter)
754        .cloned()
755        .map(|e| match e[0].atom(None).unwrap() {
756            DEFLAYER => LayerExprs::DefsrcMapping(e.clone()),
757            DEFLAYER_MAPPED => LayerExprs::CustomMapping(e.clone()),
758            _ => unreachable!(),
759        })
760        .collect::<Vec<_>>();
761
762    *s = ParserState {
763        a: s.a.clone(),
764        layer_exprs,
765        layer_idxs,
766        mapping_order,
767        defsrc_layer,
768        is_cmd_enabled: {
769            #[cfg(feature = "cmd")]
770            {
771                if cfg.enable_cmd {
772                    log::warn!("DANGER! cmd action is enabled.");
773                    true
774                } else {
775                    false
776                }
777            }
778            #[cfg(not(feature = "cmd"))]
779            {
780                log::info!("NOTE: kanata was compiled to never allow cmd");
781                false
782            }
783        },
784        delegate_to_first_layer: cfg.delegate_to_first_layer,
785        default_sequence_timeout: cfg.sequence_timeout,
786        default_sequence_input_mode: cfg.sequence_input_mode,
787        block_unmapped_keys: cfg.block_unmapped_keys,
788        lsp_hints: RefCell::new(lsp_hints),
789        vars,
790        ..Default::default()
791    };
792
793    let chords_exprs = spanned_root_exprs
794        .iter()
795        .filter(gen_first_atom_filter_spanned("defchords"))
796        .collect::<Vec<_>>();
797    parse_chord_groups(&chords_exprs, s)?;
798
799    let fake_keys_exprs = root_exprs
800        .iter()
801        .filter(gen_first_atom_filter("deffakekeys"))
802        .collect::<Vec<_>>();
803    parse_fake_keys(&fake_keys_exprs, s)?;
804
805    let vkeys_exprs = root_exprs
806        .iter()
807        .filter(gen_first_atom_filter("defvirtualkeys"))
808        .collect::<Vec<_>>();
809    parse_virtual_keys(&vkeys_exprs, s)?;
810
811    let sequence_exprs = root_exprs
812        .iter()
813        .filter(gen_first_atom_filter("defseq"))
814        .collect::<Vec<_>>();
815    let sequences = parse_sequences(&sequence_exprs, s)?;
816
817    let alias_exprs = spanned_root_exprs
818        .iter()
819        .filter(gen_first_atom_start_filter_spanned("defalias"))
820        .collect::<Vec<_>>();
821    parse_aliases(&alias_exprs, s, &env_vars)?;
822
823    let start_action = cfg
824        .start_alias
825        .as_ref()
826        .and_then(|start| s.aliases.get(start).copied());
827    if let (Some(_), None) = (cfg.start_alias.as_ref(), start_action) {
828        bail!("alias-to-trigger-on-load was given, but alias could not be found")
829    }
830
831    let mut klayers = parse_layers(s, &mut mapped_keys, &cfg)?;
832
833    resolve_chord_groups(&mut klayers, s)?;
834    let layers = s.a.bref_slice(klayers);
835    s.layers = layers;
836    let override_exprs = root_exprs
837        .iter()
838        .filter(gen_first_atom_filter("defoverrides"))
839        .collect::<Vec<_>>();
840    let overrides = match override_exprs.len() {
841        0 => Overrides::new(&[]),
842        1 => parse_overrides(override_exprs[0], s)?,
843        _ => {
844            let spanned = spanned_root_exprs
845                .iter()
846                .filter(gen_first_atom_filter_spanned("defoverrides"))
847                .nth(1)
848                .expect("> 2 overrides");
849            bail_span!(
850                spanned,
851                "Only one defoverrides allowed, found more. Delete the extras."
852            )
853        }
854    };
855
856    let defchordsv2_filter = |exprs: &&Vec<SExpr>| -> bool {
857        if exprs.is_empty() {
858            return false;
859        }
860        if let SExpr::Atom(atom) = &exprs[0] {
861            matches!(atom.t.as_str(), "defchordsv2" | "defchordsv2-experimental")
862        } else {
863            false
864        }
865    };
866    let defchordsv2_spanned_filter =
867        |exprs: &&Spanned<Vec<SExpr>>| -> bool { defchordsv2_filter(&&exprs.t) };
868
869    s.pctx.trans_forbidden_reason = Some("Transparent action is forbidden within chordsv2");
870    let chords_v2_exprs = root_exprs
871        .iter()
872        .filter(defchordsv2_filter)
873        .collect::<Vec<_>>();
874    let chords_v2 = match chords_v2_exprs.len() {
875        0 => None,
876        1 => {
877            let cfks = parse_defchordv2(chords_v2_exprs[0], s)?;
878            Some(ChordsV2::new(cfks, cfg.chords_v2_min_idle))
879        }
880        _ => {
881            let spanned = spanned_root_exprs
882                .iter()
883                .filter(defchordsv2_spanned_filter)
884                .nth(1)
885                .expect("> 2 overrides");
886            bail_span!(
887                spanned,
888                "Only one defchordsv2 allowed, found more.\nDelete the extras."
889            )
890        }
891    };
892    s.pctx.trans_forbidden_reason = None;
893    if chords_v2.is_some() && !cfg.concurrent_tap_hold {
894        return Err(anyhow!(
895            "With defchordsv2 defined, concurrent-tap-hold in defcfg must be true.\n\
896            It is currently false or unspecified."
897        )
898        .into());
899    }
900
901    let defzippy_filter = |exprs: &&Vec<SExpr>| -> bool {
902        if exprs.is_empty() {
903            return false;
904        }
905        if let SExpr::Atom(atom) = &exprs[0] {
906            matches!(atom.t.as_str(), "defzippy" | "defzippy-experimental")
907        } else {
908            false
909        }
910    };
911    let defzippy_spanned_filter =
912        |exprs: &&Spanned<Vec<SExpr>>| -> bool { defzippy_filter(&&exprs.t) };
913
914    let zippy_exprs = root_exprs
915        .iter()
916        .filter(defzippy_filter)
917        .collect::<Vec<_>>();
918    let zippy = match zippy_exprs.len() {
919        0 => None,
920        1 => {
921            let zippy = parse_zippy(zippy_exprs[0], s, file_content_provider)?;
922            Some(zippy)
923        }
924        _ => {
925            let spanned = spanned_root_exprs
926                .iter()
927                .filter(defzippy_spanned_filter)
928                .nth(1)
929                .expect("> 2 overrides");
930            bail_span!(
931                spanned,
932                "Only one defzippy allowed, found more.\nDelete the extras."
933            )
934        }
935    };
936
937    #[cfg(feature = "lsp")]
938    LSP_VARIABLE_REFERENCES.with_borrow_mut(|refs| {
939        s.lsp_hints
940            .borrow_mut()
941            .reference_locations
942            .variable
943            .0
944            .extend(refs.0.drain());
945    });
946
947    let klayers = unsafe { KanataLayers::new(layers, s.a.clone()) };
948    Ok(IntermediateCfg {
949        options: cfg,
950        mapped_keys,
951        layer_info,
952        klayers,
953        sequences,
954        overrides,
955        chords_v2,
956        start_action,
957        zippy,
958    })
959}
960
961fn error_on_unknown_top_level_atoms(exprs: &[Spanned<Vec<SExpr>>]) -> Result<()> {
962    for expr in exprs {
963        expr.t
964            .first()
965            .ok_or_else(|| {
966                anyhow_span!(
967                    expr,
968                    "Found empty list as a configuration item, you should delete this"
969                )
970            })?
971            .atom(None)
972            .map(|a| match a {
973                "defcfg"
974                | "defalias"
975                | "defaliasenvcond"
976                | "defsrc"
977                | DEFLAYER
978                | DEFLAYER_MAPPED
979                | "defoverrides"
980                | "deflocalkeys-macos"
981                | "deflocalkeys-linux"
982                | "deflocalkeys-win"
983                | "deflocalkeys-winiov2"
984                | "deflocalkeys-wintercept"
985                | "deffakekeys"
986                | "defvirtualkeys"
987                | "defchords"
988                | "defvar"
989                | "deftemplate"
990                | "defchordsv2"
991                | "defchordsv2-experimental"
992                | "defzippy"
993                | "defzippy-experimental"
994                | "defseq" => Ok(()),
995                _ => err_span!(expr, "Found unknown configuration item"),
996            })
997            .ok_or_else(|| {
998                anyhow_expr!(
999                    expr.t.first().expect("not empty"),
1000                    "Invalid: found list as first item in a configuration item"
1001                )
1002            })??;
1003    }
1004    Ok(())
1005}
1006
1007/// Return a closure that filters a root expression by the content of the first element. The
1008/// closure returns true if the first element is an atom that matches the input `a` and false
1009/// otherwise.
1010fn gen_first_atom_filter(a: &str) -> impl Fn(&&Vec<SExpr>) -> bool {
1011    let a = a.to_owned();
1012    move |expr| {
1013        if expr.is_empty() {
1014            return false;
1015        }
1016        if let SExpr::Atom(atom) = &expr[0] {
1017            atom.t == a
1018        } else {
1019            false
1020        }
1021    }
1022}
1023
1024/// Return a closure that filters a root expression by the content of the first element. The
1025/// closure returns true if the first element is an atom that starts with the input `a` and false
1026/// otherwise.
1027fn gen_first_atom_start_filter_spanned(a: &str) -> impl Fn(&&Spanned<Vec<SExpr>>) -> bool {
1028    let a = a.to_owned();
1029    move |expr| {
1030        if expr.t.is_empty() {
1031            return false;
1032        }
1033        if let SExpr::Atom(atom) = &expr.t[0] {
1034            atom.t.starts_with(&a)
1035        } else {
1036            false
1037        }
1038    }
1039}
1040
1041/// Return a closure that filters a root expression by the content of the first element. The
1042/// closure returns true if the first element is an atom that matches the input `a` and false
1043/// otherwise.
1044fn gen_first_atom_filter_spanned(a: &str) -> impl Fn(&&Spanned<Vec<SExpr>>) -> bool {
1045    let a = a.to_owned();
1046    move |expr| {
1047        if expr.t.is_empty() {
1048            return false;
1049        }
1050        if let SExpr::Atom(atom) = &expr.t[0] {
1051            atom.t == a
1052        } else {
1053            false
1054        }
1055    }
1056}
1057
1058/// Consumes the first element and returns the rest of the iterator. Returns `Ok` if the first
1059/// element is an atom and equals `expected_first`.
1060fn check_first_expr<'a>(
1061    mut exprs: impl Iterator<Item = &'a SExpr>,
1062    expected_first: &str,
1063) -> Result<impl Iterator<Item = &'a SExpr>> {
1064    let first_atom = exprs
1065        .next()
1066        .ok_or_else(|| anyhow!("Passed empty list to {expected_first}"))?
1067        .atom(None)
1068        .ok_or_else(|| anyhow!("First entry is expected to be an atom for {expected_first}"))?;
1069    if first_atom != expected_first {
1070        bail!("Passed non-{expected_first} expression to {expected_first}: {first_atom}");
1071    }
1072    Ok(exprs)
1073}
1074
1075/// Parse custom keys from an expression starting with deflocalkeys.
1076fn parse_deflocalkeys(
1077    def_local_keys_variant: &str,
1078    expr: &[SExpr],
1079) -> Result<HashMap<String, OsCode>> {
1080    let mut localkeys = HashMap::default();
1081    let mut exprs = check_first_expr(expr.iter(), def_local_keys_variant)?;
1082    // Read k-v pairs from the configuration
1083    while let Some(key_expr) = exprs.next() {
1084        let key = key_expr.atom(None).ok_or_else(|| {
1085            anyhow_expr!(key_expr, "No lists are allowed in {def_local_keys_variant}")
1086        })?;
1087        if localkeys.contains_key(key) {
1088            bail_expr!(
1089                key_expr,
1090                "Duplicate {key} found in {def_local_keys_variant}"
1091            );
1092        }
1093
1094        // Bug:
1095        // Trying to convert a number to OsCode is OS-dependent and is fallible.
1096        // A valid number for Linux could throw an error on Windows.
1097        //
1098        // Fix:
1099        // When the deflocalkeys variant does not apply to the current OS,
1100        // use a dummy OsCode to keep the "same name" validation
1101        // while avoiding the u16->OsCode conversion attempt.
1102        if !deflocalkeys_variant_applies_to_current_os(def_local_keys_variant) {
1103            localkeys.insert(key.to_owned(), OsCode::KEY_RESERVED);
1104            continue;
1105        }
1106
1107        let osc = match exprs.next() {
1108            Some(v) => v
1109                .atom(None)
1110                .ok_or_else(|| anyhow_expr!(v, "No lists are allowed in {def_local_keys_variant}"))
1111                .and_then(|osc| {
1112                    osc.parse::<u16>().map_err(|_| {
1113                        anyhow_expr!(v, "Unknown number in {def_local_keys_variant}: {osc}")
1114                    })
1115                })
1116                .and_then(|osc| {
1117                    OsCode::from_u16(osc).ok_or_else(|| {
1118                        anyhow_expr!(v, "Unknown number in {def_local_keys_variant}: {osc}")
1119                    })
1120                })?,
1121            None => bail_expr!(key_expr, "Key without a number in {def_local_keys_variant}"),
1122        };
1123        log::debug!("custom mapping: {key} {}", osc.as_u16());
1124        localkeys.insert(key.to_owned(), osc);
1125    }
1126    Ok(localkeys)
1127}
1128
1129#[derive(Debug, Copy, Clone)]
1130enum MouseInDefsrc {
1131    MouseUsed,
1132    NoMouse,
1133}
1134
1135/// Parse mapped keys from an expression starting with defsrc. Returns the key mapping as well as
1136/// a vec of the indexes in order. The length of the returned vec should be matched by the length
1137/// of all layer declarations.
1138fn parse_defsrc(
1139    expr: &[SExpr],
1140    defcfg: &CfgOptions,
1141) -> Result<(MappedKeys, Vec<usize>, MouseInDefsrc)> {
1142    let exprs = check_first_expr(expr.iter(), "defsrc")?;
1143    let mut mkeys = MappedKeys::default();
1144    let mut ordered_codes = Vec::new();
1145    let mut is_mouse_used = MouseInDefsrc::NoMouse;
1146    for expr in exprs {
1147        let s = match expr {
1148            SExpr::Atom(a) => &a.t,
1149            _ => bail_expr!(expr, "No lists allowed in defsrc"),
1150        };
1151        let oscode = str_to_oscode(s)
1152            .ok_or_else(|| anyhow_expr!(expr, "Unknown key in defsrc: \"{}\"", s))?;
1153        is_mouse_used = match (is_mouse_used, oscode) {
1154            (
1155                MouseInDefsrc::NoMouse,
1156                OsCode::BTN_LEFT
1157                | OsCode::BTN_RIGHT
1158                | OsCode::BTN_MIDDLE
1159                | OsCode::BTN_SIDE
1160                | OsCode::BTN_EXTRA
1161                | OsCode::MouseWheelUp
1162                | OsCode::MouseWheelDown
1163                | OsCode::MouseWheelLeft
1164                | OsCode::MouseWheelRight,
1165            ) => MouseInDefsrc::MouseUsed,
1166            _ => is_mouse_used,
1167        };
1168
1169        if mkeys.contains(&oscode) {
1170            bail_expr!(expr, "Repeat declaration of key in defsrc: \"{}\"", s)
1171        }
1172        mkeys.insert(oscode);
1173        ordered_codes.push(oscode.into());
1174    }
1175
1176    let mapped_exceptions = match &defcfg.process_unmapped_keys_exceptions {
1177        Some(excluded_keys) => {
1178            for excluded_key in excluded_keys.iter() {
1179                log::debug!("process unmapped keys exception: {:?}", excluded_key);
1180                if mkeys.contains(&excluded_key.0) {
1181                    bail_expr!(
1182                        &excluded_key.1,
1183                        "Keys cannot be included in defsrc and also excepted in process-unmapped-keys."
1184                    );
1185                }
1186            }
1187
1188            excluded_keys
1189                .iter()
1190                .map(|excluded_key| excluded_key.0)
1191                .collect()
1192        }
1193        None => vec![],
1194    };
1195
1196    log::info!("process unmapped keys: {}", defcfg.process_unmapped_keys);
1197    if defcfg.process_unmapped_keys {
1198        for osc in 0..KEYS_IN_ROW as u16 {
1199            if let Some(osc) = OsCode::from_u16(osc) {
1200                match KeyCode::from(osc) {
1201                    KeyCode::No => {}
1202                    _ => {
1203                        if !mapped_exceptions.contains(&osc) {
1204                            mkeys.insert(osc);
1205                        }
1206                    }
1207                }
1208            }
1209        }
1210    }
1211
1212    mkeys.shrink_to_fit();
1213    Ok((mkeys, ordered_codes, is_mouse_used))
1214}
1215
1216type LayerIndexes = HashMap<String, usize>;
1217type Aliases = HashMap<String, &'static KanataAction>;
1218
1219/// Returns layer names and their indexes into the keyberon layout. This also checks that:
1220/// - All layers have the same number of items as the defsrc,
1221/// - There are no duplicate layer names
1222/// - Parentheses weren't used directly or kmonad-style escapes for parentheses weren't used.
1223fn parse_layer_indexes(
1224    exprs: &[SpannedLayerExprs],
1225    expected_len: usize,
1226    vars: &HashMap<String, SExpr>,
1227    _lsp_hints: &mut LspHints,
1228) -> Result<(LayerIndexes, LayerIcons)> {
1229    let mut layer_indexes = HashMap::default();
1230    let mut layer_icons = HashMap::default();
1231    for (i, expr_type) in exprs.iter().enumerate() {
1232        let (mut subexprs, expr, do_element_count_check, deflayer_keyword) = match expr_type {
1233            SpannedLayerExprs::DefsrcMapping(e) => {
1234                (check_first_expr(e.t.iter(), DEFLAYER)?, e, true, DEFLAYER)
1235            }
1236            SpannedLayerExprs::CustomMapping(e) => (
1237                check_first_expr(e.t.iter(), DEFLAYER_MAPPED)?,
1238                e,
1239                false,
1240                DEFLAYER_MAPPED,
1241            ),
1242        };
1243        let layer_expr = subexprs.next().ok_or_else(|| {
1244            anyhow_span!(
1245                expr,
1246                "{deflayer_keyword} requires a layer name after `{deflayer_keyword}` token"
1247            )
1248        })?;
1249        let (layer_name, _layer_name_span, icon) = {
1250            let name = layer_expr.atom(Some(vars));
1251            match name {
1252                Some(name) => (name.to_owned(), layer_expr.span(), None),
1253                None => {
1254                    // unwrap: this **must** be a list due to atom() call above.
1255                    let list = layer_expr.list(Some(vars)).unwrap();
1256                    let first = list.first().ok_or_else(|| anyhow_expr!(
1257                            layer_expr,
1258                            "{deflayer_keyword} requires a string name within this pair of parentheses (or a string name without any)"
1259                        ))?;
1260                    let name = first.atom(Some(vars)).ok_or_else(|| anyhow_expr!(
1261                            layer_expr,
1262                            "layer name after {deflayer_keyword} must be a string when enclosed within one pair of parentheses"
1263                        ))?;
1264                    let layer_opts = parse_layer_opts(&list[1..])?;
1265                    let icon = layer_opts
1266                        .get(DEFLAYER_ICON[0])
1267                        .map(|icon_s| icon_s.trim_atom_quotes().to_owned());
1268                    (name.to_owned(), first.span(), icon)
1269                }
1270            }
1271        };
1272        if layer_indexes.contains_key(&layer_name) {
1273            bail_expr!(layer_expr, "duplicate layer name: {}", layer_name);
1274        }
1275        // Check if user tried to use parentheses directly - `(` and `)`
1276        // or escaped them like in kmonad - `\(` and `\)`.
1277        for subexpr in subexprs {
1278            if let Some(list) = subexpr.list(None) {
1279                if list.is_empty() {
1280                    bail_expr!(
1281                        subexpr,
1282                        "You can't put parentheses in deflayer directly, because they are special characters for delimiting lists.\n\
1283                         To get `(` and `)` in US layout, you should use `S-9` and `S-0` respectively.\n\
1284                         For more context, see: https://github.com/jtroo/kanata/issues/459"
1285                    )
1286                }
1287                if list.len() == 1
1288                    && list
1289                        .first()
1290                        .is_some_and(|s| s.atom(None).is_some_and(|atom| atom == "\\"))
1291                {
1292                    bail_expr!(
1293                        subexpr,
1294                        "Escaping shifted characters with `\\` is currently not supported in kanata.\n\
1295                         To get `(` and `)` in US layout, you should use `S-9` and `S-0` respectively.\n\
1296                         For more context, see: https://github.com/jtroo/kanata/issues/163"
1297                    )
1298                }
1299            }
1300        }
1301        if do_element_count_check {
1302            let num_actions = expr.t.len() - 2;
1303            if num_actions != expected_len {
1304                bail_span!(
1305                    expr,
1306                    "Layer {} has {} item(s), but requires {} to match defsrc",
1307                    layer_name,
1308                    num_actions,
1309                    expected_len
1310                )
1311            }
1312        }
1313
1314        #[cfg(feature = "lsp")]
1315        _lsp_hints
1316            .definition_locations
1317            .layer
1318            .insert(layer_name.clone(), _layer_name_span.clone());
1319
1320        layer_indexes.insert(layer_name.clone(), i);
1321        layer_icons.insert(layer_name, icon);
1322    }
1323
1324    Ok((layer_indexes, layer_icons))
1325}
1326
1327#[derive(Debug, Clone)]
1328enum LayerExprs {
1329    DefsrcMapping(Vec<SExpr>),
1330    CustomMapping(Vec<SExpr>),
1331}
1332
1333#[derive(Debug, Clone)]
1334enum SpannedLayerExprs {
1335    DefsrcMapping(Spanned<Vec<SExpr>>),
1336    CustomMapping(Spanned<Vec<SExpr>>),
1337}
1338
1339#[derive(Debug, Clone, Default)]
1340pub struct ParserContext {
1341    is_within_defvirtualkeys: bool,
1342    trans_forbidden_reason: Option<&'static str>,
1343}
1344
1345#[derive(Debug)]
1346pub struct ParserState {
1347    layers: KLayers,
1348    layer_exprs: Vec<LayerExprs>,
1349    aliases: Aliases,
1350    layer_idxs: LayerIndexes,
1351    mapping_order: Vec<usize>,
1352    virtual_keys: HashMap<String, (usize, &'static KanataAction)>,
1353    chord_groups: HashMap<String, ChordGroup>,
1354    defsrc_layer: [KanataAction; KEYS_IN_ROW],
1355    vars: HashMap<String, SExpr>,
1356    is_cmd_enabled: bool,
1357    delegate_to_first_layer: bool,
1358    default_sequence_timeout: u16,
1359    default_sequence_input_mode: SequenceInputMode,
1360    block_unmapped_keys: bool,
1361    switch_max_key_timing: Cell<u16>,
1362    multi_action_nest_count: Cell<u16>,
1363    pctx: ParserContext,
1364    pub lsp_hints: RefCell<LspHints>,
1365    a: Arc<Allocations>,
1366}
1367
1368impl ParserState {
1369    fn vars(&self) -> Option<&HashMap<String, SExpr>> {
1370        Some(&self.vars)
1371    }
1372}
1373
1374impl Default for ParserState {
1375    fn default() -> Self {
1376        let default_cfg = CfgOptions::default();
1377        Self {
1378            layers: Default::default(),
1379            layer_exprs: Default::default(),
1380            aliases: Default::default(),
1381            layer_idxs: Default::default(),
1382            mapping_order: Default::default(),
1383            defsrc_layer: [KanataAction::NoOp; KEYS_IN_ROW],
1384            virtual_keys: Default::default(),
1385            chord_groups: Default::default(),
1386            vars: Default::default(),
1387            is_cmd_enabled: default_cfg.enable_cmd,
1388            delegate_to_first_layer: default_cfg.delegate_to_first_layer,
1389            default_sequence_timeout: default_cfg.sequence_timeout,
1390            default_sequence_input_mode: default_cfg.sequence_input_mode,
1391            block_unmapped_keys: default_cfg.block_unmapped_keys,
1392            switch_max_key_timing: Cell::new(0),
1393            multi_action_nest_count: Cell::new(0),
1394            lsp_hints: Default::default(),
1395            a: unsafe { Allocations::new() },
1396            pctx: ParserContext::default(),
1397        }
1398    }
1399}
1400
1401#[derive(Debug, Clone)]
1402struct ChordGroup {
1403    id: u16,
1404    name: String,
1405    keys: Vec<String>,
1406    coords: Vec<((u8, u16), ChordKeys)>,
1407    chords: HashMap<u128, SExpr>,
1408    timeout: u16,
1409}
1410
1411fn parse_vars(exprs: &[&Vec<SExpr>], _lsp_hints: &mut LspHints) -> Result<HashMap<String, SExpr>> {
1412    let mut vars: HashMap<String, SExpr> = Default::default();
1413    for expr in exprs {
1414        let mut subexprs = check_first_expr(expr.iter(), "defvar")?;
1415        // Read k-v pairs from the configuration
1416        while let Some(var_name_expr) = subexprs.next() {
1417            let var_name = match var_name_expr {
1418                SExpr::Atom(a) => &a.t,
1419                _ => bail_expr!(var_name_expr, "variable name must not be a list"),
1420            };
1421            let var_expr = match subexprs.next() {
1422                Some(v) => match v {
1423                    SExpr::Atom(_) => v.clone(),
1424                    SExpr::List(l) => parse_list_var(l, &vars),
1425                },
1426                None => bail_expr!(var_name_expr, "variable name must have a subsequent value"),
1427            };
1428            #[cfg(feature = "lsp")]
1429            _lsp_hints
1430                .definition_locations
1431                .variable
1432                .insert(var_name.to_owned(), var_name_expr.span());
1433            if vars.insert(var_name.into(), var_expr).is_some() {
1434                bail_expr!(var_name_expr, "duplicate variable name: {}", var_name);
1435            }
1436        }
1437    }
1438    Ok(vars)
1439}
1440
1441fn parse_list_var(expr: &Spanned<Vec<SExpr>>, vars: &HashMap<String, SExpr>) -> SExpr {
1442    let ret = match expr.t.first() {
1443        Some(SExpr::Atom(a)) => match a.t.as_str() {
1444            "concat" => {
1445                let mut concat_str = String::new();
1446                let visitees = &expr.t[1..];
1447                push_all_atoms(visitees, vars, &mut concat_str);
1448                SExpr::Atom(Spanned {
1449                    span: expr.span.clone(),
1450                    t: concat_str,
1451                })
1452            }
1453            _ => SExpr::List(expr.clone()),
1454        },
1455        _ => SExpr::List(expr.clone()),
1456    };
1457    ret
1458}
1459
1460fn push_all_atoms(exprs: &[SExpr], vars: &HashMap<String, SExpr>, pusheen: &mut String) {
1461    for expr in exprs {
1462        if let Some(a) = expr.atom(Some(vars)) {
1463            pusheen.push_str(a.trim_atom_quotes());
1464        } else if let Some(l) = expr.list(Some(vars)) {
1465            push_all_atoms(l, vars, pusheen);
1466        }
1467    }
1468}
1469
1470/// Parse alias->action mappings from multiple exprs starting with defalias.
1471/// Mutates the input `s` by storing aliases inside.
1472fn parse_aliases(
1473    exprs: &[&Spanned<Vec<SExpr>>],
1474    s: &mut ParserState,
1475    env_vars: &EnvVars,
1476) -> Result<()> {
1477    for expr in exprs {
1478        handle_standard_defalias(&expr.t, s)?;
1479        handle_envcond_defalias(expr, s, env_vars)?;
1480    }
1481    Ok(())
1482}
1483
1484fn handle_standard_defalias(expr: &[SExpr], s: &mut ParserState) -> Result<()> {
1485    let subexprs = match check_first_expr(expr.iter(), "defalias") {
1486        Ok(s) => s,
1487        Err(_) => return Ok(()),
1488    };
1489    read_alias_name_action_pairs(subexprs, s)
1490}
1491
1492fn handle_envcond_defalias(
1493    exprs: &Spanned<Vec<SExpr>>,
1494    s: &mut ParserState,
1495    env_vars: &EnvVars,
1496) -> Result<()> {
1497    let mut subexprs = match check_first_expr(exprs.t.iter(), "defaliasenvcond") {
1498        Ok(exprs) => exprs,
1499        Err(_) => return Ok(()),
1500    };
1501
1502    let conderr = "defaliasenvcond must have a list with 2 strings as the first parameter:\n\
1503            (<env var name> <env var value>)";
1504
1505    // Check that there is a list containing the environment variable name and value that
1506    // determines if this defalias entry should be used. If there is no match, return early.
1507    match subexprs.next() {
1508        Some(expr) => {
1509            let envcond = expr.list(s.vars()).ok_or_else(|| {
1510                anyhow_expr!(expr, "Found a string, but expected a list.\n{conderr}")
1511            })?;
1512            if envcond.len() != 2 {
1513                bail_expr!(expr, "List has the incorrect number of items.\n{conderr}");
1514            }
1515            let env_var_name = envcond[0].atom(s.vars()).ok_or_else(|| {
1516                anyhow_expr!(
1517                    expr,
1518                    "Environment variable name must be a string, not a list.\n{conderr}"
1519                )
1520            })?;
1521            let env_var_value = envcond[1].atom(s.vars()).ok_or_else(|| {
1522                anyhow_expr!(
1523                    expr,
1524                    "Environment variable value must be a string, not a list.\n{conderr}"
1525                )
1526            })?;
1527            match env_vars {
1528                Ok(vars) => {
1529                    let values_of_matching_vars: Vec<_> = vars
1530                        .iter()
1531                        .filter_map(|(k, v)| if k == env_var_name { Some(v) } else { None })
1532                        .collect();
1533                    if values_of_matching_vars.is_empty() {
1534                        let msg = format!("Env var '{env_var_name}' is not set");
1535                        #[cfg(feature = "lsp")]
1536                        s.lsp_hints
1537                            .borrow_mut()
1538                            .inactive_code
1539                            .push(lsp_hints::InactiveCode {
1540                                span: exprs.span.clone(),
1541                                reason: msg.clone(),
1542                            });
1543                        log::info!("{msg}, skipping associated aliases");
1544                        return Ok(());
1545                    } else if !values_of_matching_vars.iter().any(|&v| v == env_var_value) {
1546                        let msg =
1547                            format!("Env var '{env_var_name}' is set, but value doesn't match");
1548                        #[cfg(feature = "lsp")]
1549                        s.lsp_hints
1550                            .borrow_mut()
1551                            .inactive_code
1552                            .push(lsp_hints::InactiveCode {
1553                                span: exprs.span.clone(),
1554                                reason: msg.clone(),
1555                            });
1556                        log::info!("{msg}, skipping associated aliases");
1557                        return Ok(());
1558                    }
1559                }
1560                Err(err) => {
1561                    bail_expr!(expr, "{err}");
1562                }
1563            }
1564            log::info!("Found env var ({env_var_name} {env_var_value}), using associated aliases");
1565        }
1566        None => bail_expr!(&exprs.t[0], "Missing a list item.\n{conderr}"),
1567    };
1568    read_alias_name_action_pairs(subexprs, s)
1569}
1570
1571fn read_alias_name_action_pairs<'a>(
1572    mut exprs: impl Iterator<Item = &'a SExpr>,
1573    s: &mut ParserState,
1574) -> Result<()> {
1575    // Read k-v pairs from the configuration
1576    while let Some(alias_expr) = exprs.next() {
1577        let alias = match alias_expr {
1578            SExpr::Atom(a) => &a.t,
1579            _ => bail_expr!(
1580                alias_expr,
1581                "Alias names cannot be lists. Invalid alias: {:?}",
1582                alias_expr
1583            ),
1584        };
1585        let action = match exprs.next() {
1586            Some(v) => v,
1587            None => bail_expr!(alias_expr, "Found alias without an action - add an action"),
1588        };
1589        let action = parse_action(action, s)?;
1590        if s.aliases.insert(alias.into(), action).is_some() {
1591            bail_expr!(alias_expr, "Duplicate alias: {}", alias);
1592        }
1593        #[cfg(feature = "lsp")]
1594        s.lsp_hints
1595            .borrow_mut()
1596            .definition_locations
1597            .alias
1598            .insert(alias.into(), alias_expr.span());
1599    }
1600    Ok(())
1601}
1602
1603/// Parse a `kanata_keyberon::action::Action` from a `SExpr`.
1604fn parse_action(expr: &SExpr, s: &ParserState) -> Result<&'static KanataAction> {
1605    expr.atom(s.vars())
1606        .map(|a| parse_action_atom(&Spanned::new(a.into(), expr.span()), s))
1607        .unwrap_or_else(|| {
1608            expr.list(s.vars())
1609                .map(|l| parse_action_list(l, s))
1610                .expect("must be atom or list")
1611        })
1612        .map_err(|mut e| {
1613            if e.span.is_none() {
1614                e.span = Some(expr.span())
1615            };
1616            e
1617        })
1618}
1619
1620/// Returns a single custom action in the proper wrapped type.
1621fn custom(ca: CustomAction, a: &Allocations) -> Result<&'static KanataAction> {
1622    Ok(a.sref(Action::Custom(a.sref(a.sref_slice(ca)))))
1623}
1624
1625/// Parse a `kanata_keyberon::action::Action` from a string.
1626fn parse_action_atom(ac_span: &Spanned<String>, s: &ParserState) -> Result<&'static KanataAction> {
1627    let ac = &*ac_span.t;
1628    if is_list_action(ac) {
1629        bail_span!(
1630            ac_span,
1631            "This is a list action and must be in parentheses: ({ac} ...)"
1632        );
1633    }
1634    match ac {
1635        "_" | "‗" | "≝" => {
1636            if let Some(trans_forbidden_reason) = s.pctx.trans_forbidden_reason {
1637                bail_span!(ac_span, "{trans_forbidden_reason}");
1638            } else {
1639                return Ok(s.a.sref(Action::Trans));
1640            }
1641        }
1642        "XX" | "✗" | "∅" | "•" => {
1643            return Ok(s.a.sref(Action::NoOp));
1644        }
1645        "lrld" => return custom(CustomAction::LiveReload, &s.a),
1646        "lrld-next" | "lrnx" => return custom(CustomAction::LiveReloadNext, &s.a),
1647        "lrld-prev" | "lrpv" => return custom(CustomAction::LiveReloadPrev, &s.a),
1648        "sldr" => {
1649            return custom(
1650                CustomAction::SequenceLeader(
1651                    s.default_sequence_timeout,
1652                    s.default_sequence_input_mode,
1653                ),
1654                &s.a,
1655            );
1656        }
1657        "scnl" => return custom(CustomAction::SequenceCancel, &s.a),
1658        "mlft" | "mouseleft" => return custom(CustomAction::Mouse(Btn::Left), &s.a),
1659        "mrgt" | "mouseright" => return custom(CustomAction::Mouse(Btn::Right), &s.a),
1660        "mmid" | "mousemid" => return custom(CustomAction::Mouse(Btn::Mid), &s.a),
1661        "mfwd" | "mouseforward" => return custom(CustomAction::Mouse(Btn::Forward), &s.a),
1662        "mbck" | "mousebackward" => return custom(CustomAction::Mouse(Btn::Backward), &s.a),
1663        "mltp" | "mousetapleft" => return custom(CustomAction::MouseTap(Btn::Left), &s.a),
1664        "mrtp" | "mousetapright" => return custom(CustomAction::MouseTap(Btn::Right), &s.a),
1665        "mmtp" | "mousetapmid" => return custom(CustomAction::MouseTap(Btn::Mid), &s.a),
1666        "mftp" | "mousetapforward" => return custom(CustomAction::MouseTap(Btn::Forward), &s.a),
1667        "mbtp" | "mousetapbackward" => return custom(CustomAction::MouseTap(Btn::Backward), &s.a),
1668        "mwu" | "mousewheelup" => {
1669            return custom(
1670                CustomAction::MWheelNotch {
1671                    direction: MWheelDirection::Up,
1672                },
1673                &s.a,
1674            );
1675        }
1676        "mwd" | "mousewheeldown" => {
1677            return custom(
1678                CustomAction::MWheelNotch {
1679                    direction: MWheelDirection::Down,
1680                },
1681                &s.a,
1682            );
1683        }
1684        "mwl" | "mousewheelleft" => {
1685            return custom(
1686                CustomAction::MWheelNotch {
1687                    direction: MWheelDirection::Left,
1688                },
1689                &s.a,
1690            );
1691        }
1692        "mwr" | "mousewheelright" => {
1693            return custom(
1694                CustomAction::MWheelNotch {
1695                    direction: MWheelDirection::Right,
1696                },
1697                &s.a,
1698            );
1699        }
1700        "rpt" | "repeat" | "rpt-key" => return custom(CustomAction::Repeat, &s.a),
1701        "rpt-any" => return Ok(s.a.sref(Action::Repeat)),
1702        "dynamic-macro-record-stop" => {
1703            return custom(CustomAction::DynamicMacroRecordStop(0), &s.a);
1704        }
1705        "reverse-release-order" => match s.multi_action_nest_count.get() {
1706            0 => bail_span!(
1707                ac_span,
1708                "reverse-release-order is only allowed inside of a (multi ...) action list"
1709            ),
1710            _ => return custom(CustomAction::ReverseReleaseOrder, &s.a),
1711        },
1712        "use-defsrc" => {
1713            return Ok(s.a.sref(Action::Src));
1714        }
1715        "mvmt" | "mousemovement" | "🖰mv" => {
1716            bail_span!(ac_span, "{ac} can only be used as an input")
1717        }
1718        _ => {}
1719    };
1720    if let Some(oscode) = str_to_oscode(ac) {
1721        if matches!(ac, "comp" | "cmp") {
1722            log::warn!(
1723                "comp/cmp/cmps is not actually a compose key even though its correpsonding code is KEY_COMPOSE. Its actual functionality is context menu which somewhat behaves like right-click.\nTo remove this warning, replace this usage with an equivalent key name such as: menu"
1724            );
1725        }
1726        return Ok(s.a.sref(k(oscode.into())));
1727    }
1728    if let Some(alias) = ac.strip_prefix('@') {
1729        return match s.aliases.get(alias) {
1730            Some(ac) => {
1731                #[cfg(feature = "lsp")]
1732                s.lsp_hints
1733                    .borrow_mut()
1734                    .reference_locations
1735                    .alias
1736                    .push(alias, ac_span.span.clone());
1737                Ok(*ac)
1738            }
1739            None => match s.pctx.is_within_defvirtualkeys {
1740                true => bail_span!(
1741                    ac_span,
1742                    "Aliases are not usable within defvirtualkeys. You may use vars or templates.",
1743                ),
1744                false => bail_span!(
1745                    ac_span,
1746                    "Referenced unknown alias {}. Note that order of declarations matter.",
1747                    alias
1748                ),
1749            },
1750        };
1751    }
1752    if let Some(unisym) = ac.strip_prefix('🔣') {
1753        // TODO: when unicode accepts multiple chars, change this to feed the whole string, not just the first char
1754        return custom(
1755            CustomAction::Unicode(unisym.chars().next().expect("1 char")),
1756            &s.a,
1757        );
1758    }
1759    // Parse a sequence like `C-S-v` or `C-A-del`
1760    let (mut keys, unparsed_str) = parse_mod_prefix(ac)?;
1761    keys.push(
1762        str_to_oscode(unparsed_str)
1763            .ok_or_else(|| {
1764                // check aliases
1765                if s.aliases.contains_key(ac) {
1766                    anyhow!("Unknown key/action: {ac}. If you meant to use an alias, prefix it with '@' symbol: @{ac}")
1767                } else if s.vars.contains_key(ac) {
1768                    anyhow!("Unknown key/action: {ac}. If you meant to use a variable, prefix it with '$' symbol: ${ac}")
1769                } else {
1770                    anyhow!("Unknown key/action: {ac}")
1771                }
1772            })?
1773            .into(),
1774    );
1775    if keys.contains(&KEY_OVERLAP) {
1776        bail!("O- is only valid in sequences for lists of keys");
1777    }
1778    Ok(s.a.sref(Action::MultipleKeyCodes(s.a.sref(s.a.sref_vec(keys)))))
1779}
1780
1781/// Parse a `kanata_keyberon::action::Action` from a `SExpr::List`.
1782fn parse_action_list(ac: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
1783    if ac.is_empty() {
1784        return Ok(s.a.sref(Action::NoOp));
1785    }
1786    let ac_type = match &ac[0] {
1787        SExpr::Atom(a) => &a.t,
1788        _ => bail!("All list actions must start with string and not a list"),
1789    };
1790    if !is_list_action(ac_type) {
1791        bail_expr!(&ac[0], "Unknown action type: {ac_type}");
1792    }
1793    match ac_type.as_str() {
1794        LAYER_SWITCH => parse_layer_base(&ac[1..], s),
1795        LAYER_TOGGLE | LAYER_WHILE_HELD => parse_layer_toggle(&ac[1..], s),
1796        TAP_HOLD => parse_tap_hold(&ac[1..], s, HoldTapConfig::Default),
1797        TAP_HOLD_PRESS | TAP_HOLD_PRESS_A => {
1798            parse_tap_hold(&ac[1..], s, HoldTapConfig::HoldOnOtherKeyPress)
1799        }
1800        TAP_HOLD_RELEASE | TAP_HOLD_RELEASE_A => {
1801            parse_tap_hold(&ac[1..], s, HoldTapConfig::PermissiveHold)
1802        }
1803        TAP_HOLD_PRESS_TIMEOUT | TAP_HOLD_PRESS_TIMEOUT_A => {
1804            parse_tap_hold_timeout(&ac[1..], s, HoldTapConfig::HoldOnOtherKeyPress)
1805        }
1806        TAP_HOLD_RELEASE_TIMEOUT | TAP_HOLD_RELEASE_TIMEOUT_A => {
1807            parse_tap_hold_timeout(&ac[1..], s, HoldTapConfig::PermissiveHold)
1808        }
1809        TAP_HOLD_RELEASE_KEYS_TAP_RELEASE => parse_tap_hold_keys_trigger_tap_release(&ac[1..], s),
1810        TAP_HOLD_RELEASE_KEYS | TAP_HOLD_RELEASE_KEYS_A => {
1811            parse_tap_hold_keys(&ac[1..], s, TAP_HOLD_RELEASE_KEYS, custom_tap_hold_release)
1812        }
1813        TAP_HOLD_EXCEPT_KEYS | TAP_HOLD_EXCEPT_KEYS_A => {
1814            parse_tap_hold_keys(&ac[1..], s, TAP_HOLD_EXCEPT_KEYS, custom_tap_hold_except)
1815        }
1816        MULTI => parse_multi(&ac[1..], s),
1817        MACRO => parse_macro(&ac[1..], s, RepeatMacro::No),
1818        MACRO_REPEAT | MACRO_REPEAT_A => parse_macro(&ac[1..], s, RepeatMacro::Yes),
1819        MACRO_RELEASE_CANCEL | MACRO_RELEASE_CANCEL_A => {
1820            parse_macro_release_cancel(&ac[1..], s, RepeatMacro::No)
1821        }
1822        MACRO_REPEAT_RELEASE_CANCEL | MACRO_REPEAT_RELEASE_CANCEL_A => {
1823            parse_macro_release_cancel(&ac[1..], s, RepeatMacro::Yes)
1824        }
1825        MACRO_CANCEL_ON_NEXT_PRESS => {
1826            parse_macro_cancel_on_next_press(&ac[1..], s, RepeatMacro::No)
1827        }
1828        MACRO_REPEAT_CANCEL_ON_NEXT_PRESS => {
1829            parse_macro_cancel_on_next_press(&ac[1..], s, RepeatMacro::Yes)
1830        }
1831        MACRO_CANCEL_ON_NEXT_PRESS_CANCEL_ON_RELEASE => {
1832            parse_macro_cancel_on_next_press_cancel_on_release(&ac[1..], s, RepeatMacro::No)
1833        }
1834        MACRO_REPEAT_CANCEL_ON_NEXT_PRESS_CANCEL_ON_RELEASE => {
1835            parse_macro_cancel_on_next_press_cancel_on_release(&ac[1..], s, RepeatMacro::Yes)
1836        }
1837        UNICODE | SYM => parse_unicode(&ac[1..], s),
1838        ONE_SHOT | ONE_SHOT_PRESS | ONE_SHOT_PRESS_A => {
1839            parse_one_shot(&ac[1..], s, OneShotEndConfig::EndOnFirstPress)
1840        }
1841        ONE_SHOT_RELEASE | ONE_SHOT_RELEASE_A => {
1842            parse_one_shot(&ac[1..], s, OneShotEndConfig::EndOnFirstRelease)
1843        }
1844        ONE_SHOT_PRESS_PCANCEL | ONE_SHOT_PRESS_PCANCEL_A => {
1845            parse_one_shot(&ac[1..], s, OneShotEndConfig::EndOnFirstPressOrRepress)
1846        }
1847        ONE_SHOT_RELEASE_PCANCEL | ONE_SHOT_RELEASE_PCANCEL_A => {
1848            parse_one_shot(&ac[1..], s, OneShotEndConfig::EndOnFirstReleaseOrRepress)
1849        }
1850        ONE_SHOT_PAUSE_PROCESSING => parse_one_shot_pause_processing(&ac[1..], s),
1851        TAP_DANCE => parse_tap_dance(&ac[1..], s, TapDanceConfig::Lazy),
1852        TAP_DANCE_EAGER => parse_tap_dance(&ac[1..], s, TapDanceConfig::Eager),
1853        CHORD => parse_chord(&ac[1..], s),
1854        RELEASE_KEY | RELEASE_KEY_A => parse_release_key(&ac[1..], s),
1855        RELEASE_LAYER | RELEASE_LAYER_A => parse_release_layer(&ac[1..], s),
1856        ON_PRESS_FAKEKEY | ON_PRESS_FAKEKEY_A => parse_on_press_fake_key_op(&ac[1..], s),
1857        ON_RELEASE_FAKEKEY | ON_RELEASE_FAKEKEY_A => parse_on_release_fake_key_op(&ac[1..], s),
1858        ON_PRESS_DELAY | ON_PRESS_FAKEKEY_DELAY | ON_PRESS_FAKEKEY_DELAY_A => {
1859            parse_fake_key_delay(&ac[1..], s)
1860        }
1861        ON_RELEASE_DELAY | ON_RELEASE_FAKEKEY_DELAY | ON_RELEASE_FAKEKEY_DELAY_A => {
1862            parse_on_release_fake_key_delay(&ac[1..], s)
1863        }
1864        ON_IDLE_FAKEKEY => parse_on_idle_fakekey(&ac[1..], s),
1865        ON_PRESS | ON_PRESS_A => parse_on_press(&ac[1..], s),
1866        ON_RELEASE | ON_RELEASE_A => parse_on_release(&ac[1..], s),
1867        ON_IDLE => parse_on_idle(&ac[1..], s),
1868        ON_PHYSICAL_IDLE => parse_on_physical_idle(&ac[1..], s),
1869        HOLD_FOR_DURATION => parse_hold_for_duration(&ac[1..], s),
1870        MWHEEL_UP | MWHEEL_UP_A => parse_mwheel(&ac[1..], MWheelDirection::Up, s),
1871        MWHEEL_DOWN | MWHEEL_DOWN_A => parse_mwheel(&ac[1..], MWheelDirection::Down, s),
1872        MWHEEL_LEFT | MWHEEL_LEFT_A => parse_mwheel(&ac[1..], MWheelDirection::Left, s),
1873        MWHEEL_RIGHT | MWHEEL_RIGHT_A => parse_mwheel(&ac[1..], MWheelDirection::Right, s),
1874        MWHEEL_ACCEL_UP => parse_mwheel_accel(&ac[1..], MWheelDirection::Up, s),
1875        MWHEEL_ACCEL_DOWN => parse_mwheel_accel(&ac[1..], MWheelDirection::Down, s),
1876        MWHEEL_ACCEL_LEFT => parse_mwheel_accel(&ac[1..], MWheelDirection::Left, s),
1877        MWHEEL_ACCEL_RIGHT => parse_mwheel_accel(&ac[1..], MWheelDirection::Right, s),
1878        MOVEMOUSE_UP | MOVEMOUSE_UP_A => parse_move_mouse(&ac[1..], MoveDirection::Up, s),
1879        MOVEMOUSE_DOWN | MOVEMOUSE_DOWN_A => parse_move_mouse(&ac[1..], MoveDirection::Down, s),
1880        MOVEMOUSE_LEFT | MOVEMOUSE_LEFT_A => parse_move_mouse(&ac[1..], MoveDirection::Left, s),
1881        MOVEMOUSE_RIGHT | MOVEMOUSE_RIGHT_A => parse_move_mouse(&ac[1..], MoveDirection::Right, s),
1882        MOVEMOUSE_ACCEL_UP | MOVEMOUSE_ACCEL_UP_A => {
1883            parse_move_mouse_accel(&ac[1..], MoveDirection::Up, s)
1884        }
1885        MOVEMOUSE_ACCEL_DOWN | MOVEMOUSE_ACCEL_DOWN_A => {
1886            parse_move_mouse_accel(&ac[1..], MoveDirection::Down, s)
1887        }
1888        MOVEMOUSE_ACCEL_LEFT | MOVEMOUSE_ACCEL_LEFT_A => {
1889            parse_move_mouse_accel(&ac[1..], MoveDirection::Left, s)
1890        }
1891        MOVEMOUSE_ACCEL_RIGHT | MOVEMOUSE_ACCEL_RIGHT_A => {
1892            parse_move_mouse_accel(&ac[1..], MoveDirection::Right, s)
1893        }
1894        MOVEMOUSE_SPEED | MOVEMOUSE_SPEED_A => parse_move_mouse_speed(&ac[1..], s),
1895        SETMOUSE | SETMOUSE_A => parse_set_mouse(&ac[1..], s),
1896        DYNAMIC_MACRO_RECORD => parse_dynamic_macro_record(&ac[1..], s),
1897        DYNAMIC_MACRO_PLAY => parse_dynamic_macro_play(&ac[1..], s),
1898        ARBITRARY_CODE => parse_arbitrary_code(&ac[1..], s),
1899        CMD => parse_cmd(&ac[1..], s, CmdType::Standard),
1900        CMD_OUTPUT_KEYS => parse_cmd(&ac[1..], s, CmdType::OutputKeys),
1901        CMD_LOG => parse_cmd_log(&ac[1..], s),
1902        PUSH_MESSAGE => parse_push_message(&ac[1..], s),
1903        FORK => parse_fork(&ac[1..], s),
1904        CAPS_WORD | CAPS_WORD_A => {
1905            parse_caps_word(&ac[1..], CapsWordRepressBehaviour::Overwrite, s)
1906        }
1907        CAPS_WORD_CUSTOM | CAPS_WORD_CUSTOM_A => {
1908            parse_caps_word_custom(&ac[1..], CapsWordRepressBehaviour::Overwrite, s)
1909        }
1910        CAPS_WORD_TOGGLE | CAPS_WORD_TOGGLE_A => {
1911            parse_caps_word(&ac[1..], CapsWordRepressBehaviour::Toggle, s)
1912        }
1913        CAPS_WORD_CUSTOM_TOGGLE | CAPS_WORD_CUSTOM_TOGGLE_A => {
1914            parse_caps_word_custom(&ac[1..], CapsWordRepressBehaviour::Toggle, s)
1915        }
1916        DYNAMIC_MACRO_RECORD_STOP_TRUNCATE => parse_macro_record_stop_truncate(&ac[1..], s),
1917        SWITCH => parse_switch(&ac[1..], s),
1918        SEQUENCE => parse_sequence_start(&ac[1..], s),
1919        SEQUENCE_NOERASE => parse_sequence_noerase(&ac[1..], s),
1920        UNMOD => parse_unmod(UNMOD, &ac[1..], s),
1921        UNSHIFT | UNSHIFT_A => parse_unmod(UNSHIFT, &ac[1..], s),
1922        LIVE_RELOAD_NUM => parse_live_reload_num(&ac[1..], s),
1923        LIVE_RELOAD_FILE => parse_live_reload_file(&ac[1..], s),
1924        CLIPBOARD_SET => parse_clipboard_set(&ac[1..], s),
1925        CLIPBOARD_CMD_SET => parse_cmd(&ac[1..], s, CmdType::ClipboardSet),
1926        CLIPBOARD_SAVE => parse_clipboard_save(&ac[1..], s),
1927        CLIPBOARD_RESTORE => parse_clipboard_restore(&ac[1..], s),
1928        CLIPBOARD_SAVE_SET => parse_clipboard_save_set(&ac[1..], s),
1929        CLIPBOARD_SAVE_CMD_SET => parse_cmd(&ac[1..], s, CmdType::ClipboardSaveSet),
1930        CLIPBOARD_SAVE_SWAP => parse_clipboard_save_swap(&ac[1..], s),
1931        _ => unreachable!(),
1932    }
1933}
1934
1935fn parse_layer_base(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
1936    let idx = layer_idx(ac_params, &s.layer_idxs, s)?;
1937    set_layer_change_lsp_hint(&ac_params[0], &mut s.lsp_hints.borrow_mut());
1938    Ok(s.a.sref(Action::DefaultLayer(idx)))
1939}
1940
1941fn parse_layer_toggle(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
1942    let idx = layer_idx(ac_params, &s.layer_idxs, s)?;
1943    set_layer_change_lsp_hint(&ac_params[0], &mut s.lsp_hints.borrow_mut());
1944    Ok(s.a.sref(Action::Layer(idx)))
1945}
1946
1947#[allow(unused_variables)]
1948fn set_layer_change_lsp_hint(layer_name_expr: &SExpr, lsp_hints: &mut LspHints) {
1949    #[cfg(feature = "lsp")]
1950    {
1951        let layer_name_atom = match layer_name_expr {
1952            SExpr::Atom(x) => x,
1953            SExpr::List(_) => unreachable!("checked in layer_idx"),
1954        };
1955        lsp_hints
1956            .reference_locations
1957            .layer
1958            .push_from_atom(layer_name_atom);
1959    }
1960}
1961
1962fn layer_idx(ac_params: &[SExpr], layers: &LayerIndexes, s: &ParserState) -> Result<usize> {
1963    if ac_params.len() != 1 {
1964        bail!(
1965            "Layer actions expect one item: the layer name, found {} items",
1966            ac_params.len()
1967        )
1968    }
1969    let layer_name = ac_params[0]
1970        .atom(s.vars())
1971        .ok_or_else(|| anyhow_expr!(&ac_params[0], "layer name should be a string not a list",))?;
1972    match layers.get(layer_name) {
1973        Some(i) => Ok(*i),
1974        None => err_expr!(
1975            &ac_params[0],
1976            "layer name is not declared in any deflayer: {layer_name}"
1977        ),
1978    }
1979}
1980
1981fn parse_tap_hold(
1982    ac_params: &[SExpr],
1983    s: &ParserState,
1984    config: HoldTapConfig<'static>,
1985) -> Result<&'static KanataAction> {
1986    if ac_params.len() != 4 {
1987        bail!(
1988            r"tap-hold expects 4 items after it, got {}.
1989Params in order:
1990<tap-repress-timeout> <hold-timeout> <tap-action> <hold-action>",
1991            ac_params.len(),
1992        )
1993    }
1994    let tap_repress_timeout = parse_u16(&ac_params[0], s, "tap repress timeout")?;
1995    let hold_timeout = parse_non_zero_u16(&ac_params[1], s, "hold timeout")?;
1996    let tap_action = parse_action(&ac_params[2], s)?;
1997    let hold_action = parse_action(&ac_params[3], s)?;
1998    if matches!(tap_action, Action::HoldTap { .. }) {
1999        bail!("tap-hold does not work in the tap-action of tap-hold")
2000    }
2001    Ok(s.a.sref(Action::HoldTap(s.a.sref(HoldTapAction {
2002        config,
2003        tap_hold_interval: tap_repress_timeout,
2004        timeout: hold_timeout,
2005        tap: *tap_action,
2006        hold: *hold_action,
2007        timeout_action: *hold_action,
2008        on_press_reset_timeout_to: None,
2009    }))))
2010}
2011
2012fn parse_tap_hold_timeout(
2013    ac_params: &[SExpr],
2014    s: &ParserState,
2015    config: HoldTapConfig<'static>,
2016) -> Result<&'static KanataAction> {
2017    const PARAMS_FOR_RELEASE: &str = "Params in order:\n\
2018       <tap-repress-timeout> <hold-timeout> <tap-action> <hold-action> <timeout-action> [?reset-timeout-on-press]";
2019    match config {
2020        HoldTapConfig::PermissiveHold => {
2021            if ac_params.len() != 5 && ac_params.len() != 6 {
2022                bail!(
2023                    "tap-hold-release-timeout expects at least 5 items after it, got {}.\n\
2024                    {PARAMS_FOR_RELEASE}",
2025                    ac_params.len(),
2026                )
2027            }
2028        }
2029        HoldTapConfig::HoldOnOtherKeyPress => {
2030            if ac_params.len() != 5 {
2031                bail!(
2032                    "tap-hold-press-timeout expects 5 items after it, got {}.\n\
2033                    Params in order:\n\
2034                    <tap-repress-timeout> <hold-timeout> <tap-action> <hold-action> <timeout-action>",
2035                    ac_params.len(),
2036                )
2037            }
2038        }
2039        _ => unreachable!("other configs not expected"),
2040    };
2041    let tap_repress_timeout = parse_u16(&ac_params[0], s, "tap repress timeout")?;
2042    let hold_timeout = parse_non_zero_u16(&ac_params[1], s, "hold timeout")?;
2043    let tap_action = parse_action(&ac_params[2], s)?;
2044    let hold_action = parse_action(&ac_params[3], s)?;
2045    let timeout_action = parse_action(&ac_params[4], s)?;
2046    if matches!(tap_action, Action::HoldTap { .. }) {
2047        bail!("tap-hold does not work in the tap-action of tap-hold")
2048    }
2049    let on_press_reset_timeout_to = match config {
2050        HoldTapConfig::PermissiveHold => match ac_params.len() {
2051            6 => match ac_params[5].atom(s.vars()) {
2052                Some("reset-timeout-on-press") => std::num::NonZeroU16::new(hold_timeout),
2053                _ => bail_expr!(&ac_params[5], "Unexpected parameter.\n{PARAMS_FOR_RELEASE}"),
2054            },
2055            5 => None,
2056            _ => unreachable!("other lengths not expected"),
2057        },
2058        HoldTapConfig::HoldOnOtherKeyPress => None,
2059        _ => unreachable!("other configs not expected"),
2060    };
2061    Ok(s.a.sref(Action::HoldTap(s.a.sref(HoldTapAction {
2062        config,
2063        tap_hold_interval: tap_repress_timeout,
2064        timeout: hold_timeout,
2065        tap: *tap_action,
2066        hold: *hold_action,
2067        timeout_action: *timeout_action,
2068        on_press_reset_timeout_to,
2069    }))))
2070}
2071
2072fn parse_tap_hold_keys(
2073    ac_params: &[SExpr],
2074    s: &ParserState,
2075    custom_name: &str,
2076    custom_func: TapHoldCustomFunc,
2077) -> Result<&'static KanataAction> {
2078    if ac_params.len() != 5 {
2079        bail!(
2080            r"{} expects 5 items after it, got {}.
2081Params in order:
2082<tap-repress-timeout> <hold-timeout> <tap-action> <hold-action> <tap-trigger-keys>",
2083            custom_name,
2084            ac_params.len(),
2085        )
2086    }
2087    let tap_repress_timeout = parse_u16(&ac_params[0], s, "tap repress timeout")?;
2088    let hold_timeout = parse_non_zero_u16(&ac_params[1], s, "hold timeout")?;
2089    let tap_action = parse_action(&ac_params[2], s)?;
2090    let hold_action = parse_action(&ac_params[3], s)?;
2091    let tap_trigger_keys = parse_key_list(&ac_params[4], s, "tap-trigger-keys")?;
2092    if matches!(tap_action, Action::HoldTap { .. }) {
2093        bail!("tap-hold does not work in the tap-action of tap-hold")
2094    }
2095    Ok(s.a.sref(Action::HoldTap(s.a.sref(HoldTapAction {
2096        config: HoldTapConfig::Custom(custom_func(&tap_trigger_keys, &s.a)),
2097        tap_hold_interval: tap_repress_timeout,
2098        timeout: hold_timeout,
2099        tap: *tap_action,
2100        hold: *hold_action,
2101        timeout_action: *hold_action,
2102        on_press_reset_timeout_to: None,
2103    }))))
2104}
2105
2106fn parse_tap_hold_keys_trigger_tap_release(
2107    ac_params: &[SExpr],
2108    s: &ParserState,
2109) -> Result<&'static KanataAction> {
2110    if !matches!(ac_params.len(), 6) {
2111        bail!(
2112            r"{} expects 6 items after it, got {}.
2113Params in order:
2114<tap-repress-timeout> <hold-timeout> <tap-action> <hold-action> <tap-trigger-keys-on-press> <tap-trigger-keys-on-press-then-release>",
2115            TAP_HOLD_RELEASE_KEYS_TAP_RELEASE,
2116            ac_params.len(),
2117        )
2118    }
2119    let tap_repress_timeout = parse_u16(&ac_params[0], s, "tap repress timeout")?;
2120    let hold_timeout = parse_non_zero_u16(&ac_params[1], s, "hold timeout")?;
2121    let tap_action = parse_action(&ac_params[2], s)?;
2122    let hold_action = parse_action(&ac_params[3], s)?;
2123    let tap_trigger_keys_on_press =
2124        parse_key_list(&ac_params[4], s, "tap-trigger-keys-on-multi-press")?;
2125    let tap_trigger_keys_on_press_then_release =
2126        parse_key_list(&ac_params[5], s, "tap-trigger-keys-on-release")?;
2127    if matches!(tap_action, Action::HoldTap { .. }) {
2128        bail!("tap-hold does not work in the tap-action of tap-hold")
2129    }
2130    Ok(s.a.sref(Action::HoldTap(s.a.sref(HoldTapAction {
2131        config: HoldTapConfig::Custom(custom_tap_hold_release_trigger_tap_release(
2132            &tap_trigger_keys_on_press,
2133            &tap_trigger_keys_on_press_then_release,
2134            &s.a,
2135        )),
2136        tap_hold_interval: tap_repress_timeout,
2137        timeout: hold_timeout,
2138        tap: *tap_action,
2139        hold: *hold_action,
2140        timeout_action: *hold_action,
2141        on_press_reset_timeout_to: None,
2142    }))))
2143}
2144
2145/// Parse a list expression with length 2 having format:
2146///     (name value)
2147/// The items name and value must both be strings.
2148/// The name string is validated to ensure it matches the input.
2149/// The value is parsed into a u8.
2150#[allow(unused)]
2151fn parse_named_u8_param(name: &str, name_and_param: &SExpr, s: &ParserState) -> Result<u8> {
2152    let err = || {
2153        format!(
2154            "Expected a list with two items: {name} followed by a number. Example:\n\
2155             ({name} 2)"
2156        )
2157    };
2158    let Some(list) = name_and_param.list(s.vars()) else {
2159        bail_expr!(name_and_param, "{}", err());
2160    };
2161    if list.len() != 2 {
2162        bail_expr!(name_and_param, "{}", err());
2163    }
2164    let Some(expr_name) = list[0].atom(s.vars()) else {
2165        bail_expr!(&list[0], "Expected {name}");
2166    };
2167    if expr_name != name {
2168        bail_expr!(&list[0], "Expected {name}");
2169    }
2170    parse_u8_with_range(&list[1], s, name, 0, 255)
2171}
2172
2173fn parse_u8_with_range(expr: &SExpr, s: &ParserState, label: &str, min: u8, max: u8) -> Result<u8> {
2174    expr.atom(s.vars())
2175        .map(str::parse::<u8>)
2176        .and_then(|u| u.ok())
2177        .and_then(|u| {
2178            assert!(min <= max);
2179            if u >= min && u <= max { Some(u) } else { None }
2180        })
2181        .ok_or_else(|| anyhow_expr!(expr, "{label} must be {min}-{max}"))
2182}
2183
2184fn parse_u16(expr: &SExpr, s: &ParserState, label: &str) -> Result<u16> {
2185    expr.atom(s.vars())
2186        .map(str::parse::<u16>)
2187        .and_then(|u| u.ok())
2188        .ok_or_else(|| anyhow_expr!(expr, "{label} must be 0-65535"))
2189}
2190
2191fn parse_f32(
2192    expr: &SExpr,
2193    s: &ParserState,
2194    label: &str,
2195    min: f32,
2196    max: f32,
2197) -> Result<OrderedFloat<f32>> {
2198    expr.atom(s.vars())
2199        .map(str::parse::<f32>)
2200        .and_then(|u| {
2201            u.ok().and_then(|v| {
2202                if v >= min && v <= max {
2203                    Some(v.into())
2204                } else {
2205                    None
2206                }
2207            })
2208        })
2209        .ok_or_else(|| anyhow_expr!(expr, "{label} must be {min:.2}-{max:.2}"))
2210}
2211
2212// Note on allows:
2213// - macOS CI is behind on Rust version.
2214// - Clippy bug in new lint of Rust v1.86.
2215#[allow(unknown_lints)]
2216#[allow(clippy::manual_ok_err)]
2217fn parse_non_zero_u16(expr: &SExpr, s: &ParserState, label: &str) -> Result<u16> {
2218    expr.atom(s.vars())
2219        .map(str::parse::<u16>)
2220        .and_then(|u| match u {
2221            Ok(u @ 1..) => Some(u),
2222            _ => None,
2223        })
2224        .ok_or_else(|| anyhow_expr!(expr, "{label} must be 1-65535"))
2225}
2226
2227fn parse_key_list(expr: &SExpr, s: &ParserState, label: &str) -> Result<Vec<OsCode>> {
2228    expr.list(s.vars())
2229        .map(|keys| {
2230            keys.iter().try_fold(vec![], |mut keys, key| {
2231                key.atom(s.vars())
2232                    .map(|a| -> Result<()> {
2233                        keys.push(str_to_oscode(a).ok_or_else(|| {
2234                            anyhow_expr!(key, "string of a known key is expected")
2235                        })?);
2236                        Ok(())
2237                    })
2238                    .ok_or_else(|| {
2239                        anyhow_expr!(key, "string of a known key is expected, found list instead")
2240                    })??;
2241                Ok(keys)
2242            })
2243        })
2244        .ok_or_else(|| anyhow_expr!(expr, "{label} must be a list of keys"))?
2245}
2246
2247fn parse_multi(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2248    if ac_params.is_empty() {
2249        bail!("multi expects at least one item after it")
2250    }
2251    s.multi_action_nest_count
2252        .replace(s.multi_action_nest_count.get().saturating_add(1));
2253    let mut actions = Vec::new();
2254    let mut custom_actions: Vec<&'static CustomAction> = Vec::new();
2255    for expr in ac_params {
2256        let ac = parse_action(expr, s)?;
2257        match ac {
2258            Action::Custom(acs) => {
2259                for ac in acs.iter() {
2260                    custom_actions.push(ac);
2261                }
2262            }
2263            // Flatten multi actions
2264            Action::MultipleActions(acs) => {
2265                for ac in acs.iter() {
2266                    match ac {
2267                        Action::Custom(acs) => {
2268                            for ac in acs.iter() {
2269                                custom_actions.push(ac);
2270                            }
2271                        }
2272                        _ => actions.push(*ac),
2273                    }
2274                }
2275            }
2276            _ => actions.push(*ac),
2277        }
2278    }
2279
2280    if !custom_actions.is_empty() {
2281        actions.push(Action::Custom(s.a.sref(s.a.sref_vec(custom_actions))));
2282    }
2283
2284    if actions
2285        .iter()
2286        .filter(|ac| {
2287            matches!(
2288                ac,
2289                Action::TapDance(TapDance {
2290                    config: TapDanceConfig::Lazy,
2291                    ..
2292                }) | Action::HoldTap { .. }
2293                    | Action::Chords { .. }
2294            )
2295        })
2296        .count()
2297        > 1
2298    {
2299        bail!("Cannot combine multiple tap-hold/tap-dance/chord");
2300    }
2301
2302    s.multi_action_nest_count
2303        .replace(s.multi_action_nest_count.get().saturating_sub(1));
2304    Ok(s.a.sref(Action::MultipleActions(s.a.sref(s.a.sref_vec(actions)))))
2305}
2306
2307const MACRO_ERR: &str = "Action macro only accepts delays, keys, chords, chorded sub-macros, and a subset of special actions.\nThe macro section of the documentation describes this in more detail:\nhttps://github.com/jtroo/kanata/blob/main/docs/config.adoc#macro";
2308enum RepeatMacro {
2309    Yes,
2310    No,
2311}
2312
2313fn parse_macro(
2314    ac_params: &[SExpr],
2315    s: &ParserState,
2316    repeat: RepeatMacro,
2317) -> Result<&'static KanataAction> {
2318    if ac_params.is_empty() {
2319        bail!("macro expects at least one item after it")
2320    }
2321    let mut all_events = Vec::with_capacity(256);
2322    let mut params_remainder = ac_params;
2323    while !params_remainder.is_empty() {
2324        let mut events;
2325        (events, params_remainder) = parse_macro_item(params_remainder, s)?;
2326        all_events.append(&mut events);
2327    }
2328    if all_events.iter().any(|e| match e {
2329        SequenceEvent::Tap(kc) | SequenceEvent::Press(kc) | SequenceEvent::Release(kc) => {
2330            *kc == KEY_OVERLAP
2331        }
2332        _ => false,
2333    }) {
2334        bail!("macro contains O- which is only valid within defseq")
2335    }
2336    all_events.push(SequenceEvent::Complete);
2337    all_events.shrink_to_fit();
2338    match repeat {
2339        RepeatMacro::No => Ok(s.a.sref(Action::Sequence {
2340            events: s.a.sref(s.a.sref(s.a.sref_vec(all_events))),
2341        })),
2342        RepeatMacro::Yes => Ok(s.a.sref(Action::RepeatableSequence {
2343            events: s.a.sref(s.a.sref(s.a.sref_vec(all_events))),
2344        })),
2345    }
2346}
2347
2348fn parse_macro_release_cancel(
2349    ac_params: &[SExpr],
2350    s: &ParserState,
2351    repeat: RepeatMacro,
2352) -> Result<&'static KanataAction> {
2353    let macro_action = parse_macro(ac_params, s, repeat)?;
2354    Ok(s.a.sref(Action::MultipleActions(s.a.sref(s.a.sref_vec(vec![
2355        *macro_action,
2356        Action::Custom(s.a.sref(s.a.sref_slice(CustomAction::CancelMacroOnRelease))),
2357    ])))))
2358}
2359
2360fn parse_macro_cancel_on_next_press(
2361    ac_params: &[SExpr],
2362    s: &ParserState,
2363    repeat: RepeatMacro,
2364) -> Result<&'static KanataAction> {
2365    let macro_action = parse_macro(ac_params, s, repeat)?;
2366    let macro_duration = match macro_action {
2367        Action::RepeatableSequence { events } | Action::Sequence { events } => {
2368            macro_sequence_event_total_duration(events)
2369        }
2370        _ => unreachable!("parse_macro should return sequence action"),
2371    };
2372    Ok(s.a.sref(Action::MultipleActions(s.a.sref(s.a.sref_vec(vec![
2373        *macro_action,
2374        Action::Custom(
2375            s.a.sref(s.a.sref_slice(CustomAction::CancelMacroOnNextPress(macro_duration))),
2376        ),
2377    ])))))
2378}
2379
2380fn parse_macro_cancel_on_next_press_cancel_on_release(
2381    ac_params: &[SExpr],
2382    s: &ParserState,
2383    repeat: RepeatMacro,
2384) -> Result<&'static KanataAction> {
2385    let macro_action = parse_macro(ac_params, s, repeat)?;
2386    let macro_duration = match macro_action {
2387        Action::RepeatableSequence { events } | Action::Sequence { events } => {
2388            macro_sequence_event_total_duration(events)
2389        }
2390        _ => unreachable!("parse_macro should return sequence action"),
2391    };
2392    Ok(s.a.sref(Action::MultipleActions(s.a.sref(s.a.sref_vec(vec![
2393        *macro_action,
2394        Action::Custom(s.a.sref(s.a.sref_vec(vec![
2395            &CustomAction::CancelMacroOnRelease,
2396            s.a.sref(CustomAction::CancelMacroOnNextPress(macro_duration)),
2397        ]))),
2398    ])))))
2399}
2400
2401fn macro_sequence_event_total_duration<T>(events: &[SequenceEvent<T>]) -> u32 {
2402    events.iter().fold(0, |duration, event| {
2403        duration.saturating_add(match event {
2404            SequenceEvent::Delay { duration: d } => *d,
2405            _ => 1,
2406        })
2407    })
2408}
2409
2410#[derive(PartialEq)]
2411enum MacroNumberParseMode {
2412    Delay,
2413    Action,
2414}
2415
2416#[allow(clippy::type_complexity)] // return type is not pub
2417fn parse_macro_item<'a>(
2418    acs: &'a [SExpr],
2419    s: &ParserState,
2420) -> Result<(
2421    Vec<SequenceEvent<'static, &'static &'static [&'static CustomAction]>>,
2422    &'a [SExpr],
2423)> {
2424    parse_macro_item_impl(acs, s, MacroNumberParseMode::Delay)
2425}
2426
2427#[allow(clippy::type_complexity)] // return type is not pub
2428fn parse_macro_item_impl<'a>(
2429    acs: &'a [SExpr],
2430    s: &ParserState,
2431    num_parse_mode: MacroNumberParseMode,
2432) -> Result<(
2433    Vec<SequenceEvent<'static, &'static &'static [&'static CustomAction]>>,
2434    &'a [SExpr],
2435)> {
2436    if num_parse_mode == MacroNumberParseMode::Delay {
2437        if let Some(a) = acs[0].atom(s.vars()) {
2438            match parse_non_zero_u16(&acs[0], s, "delay") {
2439                Ok(duration) => {
2440                    let duration = u32::from(duration);
2441                    return Ok((vec![SequenceEvent::Delay { duration }], &acs[1..]));
2442                }
2443                Err(e) => {
2444                    if a.chars().all(|c| c.is_ascii_digit()) {
2445                        return Err(e);
2446                    }
2447                }
2448            }
2449        }
2450    }
2451    match parse_action(&acs[0], s) {
2452        Ok(Action::KeyCode(kc)) => {
2453            // Should note that I tried `SequenceEvent::Tap` initially but it seems to be buggy
2454            // so I changed the code to use individual press and release. The SequenceEvent
2455            // code is from a PR that (at the time of this writing) hasn't yet been merged into
2456            // keyberon master and doesn't have tests written for it yet. This seems to work as
2457            // expected right now though.
2458            Ok((
2459                vec![SequenceEvent::Press(*kc), SequenceEvent::Release(*kc)],
2460                &acs[1..],
2461            ))
2462        }
2463        Ok(Action::MultipleKeyCodes(kcs)) => {
2464            // chord - press in order then release in the reverse order
2465            let mut events = vec![];
2466            for kc in kcs.iter() {
2467                events.push(SequenceEvent::Press(*kc));
2468            }
2469            for kc in kcs.iter().rev() {
2470                events.push(SequenceEvent::Release(*kc));
2471            }
2472            Ok((events, &acs[1..]))
2473        }
2474        Ok(Action::Custom(custom)) => Ok((vec![SequenceEvent::Custom(custom)], &acs[1..])),
2475        Ok(_) => bail_expr!(&acs[0], "{MACRO_ERR}"),
2476        Err(e) => {
2477            if let Some(submacro) = acs[0].list(s.vars()) {
2478                // If it's just a list that's not parsable as a usable action, try parsing the
2479                // content.
2480                let mut submacro_remainder = submacro;
2481                let mut all_events = vec![];
2482                while !submacro_remainder.is_empty() {
2483                    let mut events;
2484                    (events, submacro_remainder) =
2485                        parse_macro_item(submacro_remainder, s).map_err(|_e| e.clone())?;
2486                    all_events.append(&mut events);
2487                }
2488                return Ok((all_events, &acs[1..]));
2489            }
2490
2491            let (held_mods, unparsed_str) =
2492                parse_mods_held_for_submacro(&acs[0], s).map_err(|mut err| {
2493                    if err.msg == MACRO_ERR {
2494                        err.msg = format!("{}\n{MACRO_ERR}", &e.msg);
2495                    }
2496                    err
2497                })?;
2498            let mut all_events = vec![];
2499
2500            // First, press all of the modifiers
2501            for kc in held_mods.iter().copied() {
2502                all_events.push(SequenceEvent::Press(kc));
2503            }
2504
2505            let mut rem_start = 1;
2506            let maybe_list_var = SExpr::Atom(Spanned::new(unparsed_str.into(), acs[0].span()));
2507            let submacro = match maybe_list_var.list(s.vars()) {
2508                Some(l) => l,
2509                None => {
2510                    // Ensure that the unparsed text is empty since otherwise it means there is
2511                    // invalid text there
2512                    if !unparsed_str.is_empty() {
2513                        bail_expr!(&acs[0], "{}\n{MACRO_ERR}", &e.msg)
2514                    }
2515                    // Check for a follow-up list
2516                    rem_start = 2;
2517                    if acs.len() < 2 {
2518                        bail_expr!(&acs[0], "{}\n{MACRO_ERR}", &e.msg)
2519                    }
2520                    acs[1]
2521                        .list(s.vars())
2522                        .ok_or_else(|| anyhow_expr!(&acs[1], "{MACRO_ERR}"))?
2523                }
2524            };
2525            let mut submacro_remainder = submacro;
2526            let mut events;
2527            while !submacro_remainder.is_empty() {
2528                (events, submacro_remainder) = parse_macro_item(submacro_remainder, s)?;
2529                all_events.append(&mut events);
2530            }
2531
2532            // Lastly, release modifiers
2533            for kc in held_mods.iter().copied() {
2534                all_events.push(SequenceEvent::Release(kc));
2535            }
2536
2537            Ok((all_events, &acs[rem_start..]))
2538        }
2539    }
2540}
2541
2542/// Parses mod keys like `C-S-`. Returns the `KeyCode`s for the modifiers parsed and the unparsed
2543/// text after any parsed modifier prefixes.
2544fn parse_mods_held_for_submacro<'a>(
2545    held_mods: &'a SExpr,
2546    s: &'a ParserState,
2547) -> Result<(Vec<KeyCode>, &'a str)> {
2548    let mods = held_mods
2549        .atom(s.vars())
2550        .ok_or_else(|| anyhow_expr!(held_mods, "{MACRO_ERR}"))?;
2551    let (mod_keys, unparsed_str) = parse_mod_prefix(mods)?;
2552    if mod_keys.is_empty() {
2553        bail_expr!(held_mods, "{MACRO_ERR}");
2554    }
2555    Ok((mod_keys, unparsed_str))
2556}
2557
2558static KEYMODI: &[(&str, KeyCode)] = &[
2559    ("S-", KeyCode::LShift),
2560    ("‹⇧", KeyCode::LShift),
2561    ("⇧›", KeyCode::RShift),
2562    ("RS-", KeyCode::RShift),
2563    ("C-", KeyCode::LCtrl),
2564    ("‹⎈", KeyCode::LCtrl),
2565    ("‹⌃", KeyCode::LCtrl),
2566    ("⎈›", KeyCode::RCtrl),
2567    ("⌃›", KeyCode::RCtrl),
2568    ("RC-", KeyCode::RCtrl),
2569    ("M-", KeyCode::LGui),
2570    ("‹◆", KeyCode::LGui),
2571    ("‹⌘", KeyCode::LGui),
2572    ("‹❖", KeyCode::LGui),
2573    ("◆›", KeyCode::RGui),
2574    ("⌘›", KeyCode::RGui),
2575    ("❖›", KeyCode::RGui),
2576    ("RM-", KeyCode::RGui),
2577    ("‹⎇", KeyCode::LAlt),
2578    ("A-", KeyCode::LAlt),
2579    ("‹⌥", KeyCode::LAlt),
2580    ("AG-", KeyCode::RAlt),
2581    ("RA-", KeyCode::RAlt),
2582    ("⎇›", KeyCode::RAlt),
2583    ("⌥›", KeyCode::RAlt),
2584    ("⎈", KeyCode::LCtrl), // Shorter indicators should be at the end to only get matched after
2585    // indicators with sides have had a chance
2586    ("⌥", KeyCode::LAlt),
2587    ("⎇", KeyCode::LAlt),
2588    ("◆", KeyCode::LGui),
2589    ("⌘", KeyCode::LGui),
2590    ("❖", KeyCode::LGui),
2591    ("O-", KEY_OVERLAP),
2592];
2593
2594/// Parses mod keys like `C-S-`. Returns the `KeyCode`s for the modifiers parsed and the unparsed
2595/// text after any parsed modifier prefixes.
2596pub fn parse_mod_prefix(mods: &str) -> Result<(Vec<KeyCode>, &str)> {
2597    let mut key_stack = Vec::new();
2598    let mut rem = mods;
2599    loop {
2600        let mut found_none = true;
2601        for (key_s, key_code) in KEYMODI {
2602            if let Some(rest) = rem.strip_prefix(key_s) {
2603                if key_stack.contains(key_code) {
2604                    bail!("Redundant \"{key_code:?}\" in {mods:?}");
2605                }
2606                key_stack.push(*key_code);
2607                rem = rest;
2608                found_none = false;
2609            }
2610        }
2611        if found_none {
2612            break;
2613        }
2614    }
2615    Ok((key_stack, rem))
2616}
2617
2618fn parse_unicode(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2619    const ERR_STR: &str = "unicode expects exactly one (not combos looking like one) unicode character as an argument\nor a unicode hex number, prefixed by U+. Example: U+1F686.";
2620    if ac_params.len() != 1 {
2621        bail!(ERR_STR)
2622    }
2623    ac_params[0]
2624        .atom(s.vars())
2625        .map(|a| {
2626            let a = a.trim_atom_quotes();
2627            let unicode_char = match a.chars().count() {
2628                0 => bail_expr!(&ac_params[0], "{ERR_STR}"),
2629                1 => a.chars().next().expect("1 char"),
2630                _ => {
2631                    let normalized = a.to_uppercase();
2632                    let Some(hexnum) = normalized.strip_prefix("U+") else {
2633                        bail_expr!(&ac_params[0], "{ERR_STR}.\nMust begin with U+")
2634                    };
2635                    let Ok(u_val) = u32::from_str_radix(hexnum, 16) else {
2636                        bail_expr!(&ac_params[0], "{ERR_STR}.\nInvalid number after U+")
2637                    };
2638                    match char::from_u32(u_val) {
2639                        Some(v) => v,
2640                        None => bail_expr!(&ac_params[0], "{ERR_STR}.\nInvalid char."),
2641                    }
2642                }
2643            };
2644            Ok(s.a.sref(Action::Custom(
2645                s.a.sref(s.a.sref_slice(CustomAction::Unicode(unicode_char))),
2646            )))
2647        })
2648        .ok_or_else(|| anyhow_expr!(&ac_params[0], "{ERR_STR}"))?
2649}
2650
2651enum CmdType {
2652    /// Execute command in own thread.
2653    Standard,
2654    /// Execute command synchronously and output stdout as macro-like SExpr.
2655    OutputKeys,
2656    /// Execute command and set clipboard to output. Clipboard content is passed as stdin to the
2657    /// command.
2658    ClipboardSet,
2659    /// Execute command and set clipboard save id to output.
2660    /// Clipboard save id content is passed as stdin to the command.
2661    ClipboardSaveSet,
2662}
2663
2664// Parse cmd, but there are 2 arguments before specifying normal log and error log
2665fn parse_cmd_log(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2666    const ERR_STR: &str =
2667        "cmd-log expects at least 3 strings, <log-level> <error-log-level> <cmd...>";
2668    if !s.is_cmd_enabled {
2669        bail!(
2670            "cmd is not enabled for this kanata executable (did you use 'cmd_allowed' variants?), but is set in the configuration"
2671        );
2672    }
2673    if ac_params.len() < 3 {
2674        bail!(ERR_STR);
2675    }
2676    let mut cmd = vec![];
2677    let log_level =
2678        if let Some(Ok(input_mode)) = ac_params[0].atom(s.vars()).map(LogLevel::try_from_str) {
2679            input_mode
2680        } else {
2681            bail_expr!(&ac_params[0], "{ERR_STR}\n{}", LogLevel::err_msg());
2682        };
2683    let error_log_level =
2684        if let Some(Ok(input_mode)) = ac_params[1].atom(s.vars()).map(LogLevel::try_from_str) {
2685            input_mode
2686        } else {
2687            bail_expr!(&ac_params[1], "{ERR_STR}\n{}", LogLevel::err_msg());
2688        };
2689    collect_strings(&ac_params[2..], &mut cmd, s);
2690    if cmd.is_empty() {
2691        bail!(ERR_STR);
2692    }
2693    Ok(s.a.sref(Action::Custom(s.a.sref(
2694        s.a.sref_slice(CustomAction::CmdLog(log_level, error_log_level, cmd)),
2695    ))))
2696}
2697
2698#[allow(unused_variables)]
2699fn parse_cmd(
2700    ac_params: &[SExpr],
2701    s: &ParserState,
2702    cmd_type: CmdType,
2703) -> Result<&'static KanataAction> {
2704    #[cfg(not(feature = "cmd"))]
2705    {
2706        bail!(
2707            "cmd is not enabled for this kanata executable. Use a cmd_allowed prebuilt executable or compile with the feature: cmd."
2708        );
2709    }
2710    #[cfg(feature = "cmd")]
2711    {
2712        if matches!(cmd_type, CmdType::ClipboardSaveSet) {
2713            const ERR_STR: &str = "expects a save ID and at least one string";
2714            if !s.is_cmd_enabled {
2715                bail!("To use cmd you must put in defcfg: danger-enable-cmd yes.");
2716            }
2717            if ac_params.len() < 2 {
2718                bail!("{CLIPBOARD_SAVE_CMD_SET} {ERR_STR}");
2719            }
2720            let mut cmd = vec![];
2721            let save_id = parse_u16(&ac_params[0], s, "clipboard save ID")?;
2722            collect_strings(&ac_params[1..], &mut cmd, s);
2723            if cmd.is_empty() {
2724                bail_expr!(&ac_params[1], "{CLIPBOARD_SAVE_CMD_SET} {ERR_STR}");
2725            }
2726            return Ok(s.a.sref(Action::Custom(
2727                s.a.sref(s.a.sref_slice(CustomAction::ClipboardSaveCmdSet(save_id, cmd))),
2728            )));
2729        }
2730
2731        const ERR_STR: &str = "cmd expects at least one string";
2732        if !s.is_cmd_enabled {
2733            bail!("To use cmd you must put in defcfg: danger-enable-cmd yes.");
2734        }
2735        let mut cmd = vec![];
2736        collect_strings(ac_params, &mut cmd, s);
2737        if cmd.is_empty() {
2738            bail!(ERR_STR);
2739        }
2740        Ok(s.a
2741            .sref(Action::Custom(s.a.sref(s.a.sref_slice(match cmd_type {
2742                CmdType::Standard => CustomAction::Cmd(cmd),
2743                CmdType::OutputKeys => CustomAction::CmdOutputKeys(cmd),
2744                CmdType::ClipboardSet => CustomAction::ClipboardCmdSet(cmd),
2745                CmdType::ClipboardSaveSet => unreachable!(),
2746            })))))
2747    }
2748}
2749
2750/// Recurse through all levels of list nesting and collect into a flat list of strings.
2751/// Recursion is DFS, which matches left-to-right reading of the strings as they appear,
2752/// if everything was on a single line.
2753fn collect_strings(params: &[SExpr], strings: &mut Vec<String>, s: &ParserState) {
2754    for param in params {
2755        if let Some(a) = param.atom(s.vars()) {
2756            strings.push(a.trim_atom_quotes().to_owned());
2757        } else {
2758            // unwrap: this must be a list, since it's not an atom.
2759            let l = param.list(s.vars()).unwrap();
2760            collect_strings(l, strings, s);
2761        }
2762    }
2763}
2764
2765#[test]
2766fn test_collect_strings() {
2767    let params = r#"(gah (squish "squash" (splish splosh) "bah mah") dah)"#;
2768    let params = sexpr::parse(params, "noexist").unwrap();
2769    let mut strings = vec![];
2770    collect_strings(&params[0].t, &mut strings, &ParserState::default());
2771    assert_eq!(
2772        &strings,
2773        &[
2774            "gah", "squish", "squash", "splish", "splosh", "bah mah", "dah"
2775        ]
2776    );
2777}
2778
2779fn parse_push_message(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2780    if ac_params.is_empty() {
2781        bail!(
2782            "{PUSH_MESSAGE} expects at least one item, an item can be a list or an atom, found 0, none"
2783        );
2784    }
2785    let message = to_simple_expr(ac_params, s);
2786    custom(CustomAction::PushMessage(message), &s.a)
2787}
2788
2789fn to_simple_expr(params: &[SExpr], s: &ParserState) -> Vec<SimpleSExpr> {
2790    let mut result: Vec<SimpleSExpr> = Vec::new();
2791    for param in params {
2792        if let Some(a) = param.atom(s.vars()) {
2793            result.push(SimpleSExpr::Atom(a.trim_atom_quotes().to_owned()));
2794        } else {
2795            // unwrap: this must be a list, since it's not an atom.
2796            let sexps = param.list(s.vars()).unwrap();
2797            let value = to_simple_expr(sexps, s);
2798            let list = SimpleSExpr::List(value);
2799            result.push(list);
2800        }
2801    }
2802    result
2803}
2804
2805#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2806pub enum SimpleSExpr {
2807    Atom(String),
2808    List(Vec<SimpleSExpr>),
2809}
2810
2811fn parse_one_shot(
2812    ac_params: &[SExpr],
2813    s: &ParserState,
2814    end_config: OneShotEndConfig,
2815) -> Result<&'static KanataAction> {
2816    const ERR_MSG: &str = "one-shot expects a timeout followed by a key or action";
2817    if ac_params.len() != 2 {
2818        bail!(ERR_MSG);
2819    }
2820
2821    let timeout = parse_non_zero_u16(&ac_params[0], s, "timeout")?;
2822    let action = parse_action(&ac_params[1], s)?;
2823    if !matches!(
2824        action,
2825        Action::Layer(..) | Action::KeyCode(..) | Action::MultipleKeyCodes(..)
2826    ) {
2827        bail!("one-shot is only allowed to contain layer-while-held, a keycode, or a chord");
2828    }
2829
2830    Ok(s.a.sref(Action::OneShot(s.a.sref(OneShot {
2831        timeout,
2832        action,
2833        end_config,
2834    }))))
2835}
2836
2837fn parse_one_shot_pause_processing(
2838    ac_params: &[SExpr],
2839    s: &ParserState,
2840) -> Result<&'static KanataAction> {
2841    const ERR_MSG: &str = "one-shot-pause-processing expects a time";
2842    if ac_params.len() != 1 {
2843        bail!(ERR_MSG);
2844    }
2845    let timeout = parse_non_zero_u16(&ac_params[0], s, "time (milliseconds)")?;
2846    Ok(s.a.sref(Action::OneShotIgnoreEventsTicks(timeout)))
2847}
2848
2849fn parse_tap_dance(
2850    ac_params: &[SExpr],
2851    s: &ParserState,
2852    config: TapDanceConfig,
2853) -> Result<&'static KanataAction> {
2854    const ERR_MSG: &str = "tap-dance expects a timeout (number) followed by a list of actions";
2855    if ac_params.len() != 2 {
2856        bail!(ERR_MSG);
2857    }
2858
2859    let timeout = parse_non_zero_u16(&ac_params[0], s, "timeout")?;
2860    let actions = ac_params[1]
2861        .list(s.vars())
2862        .map(|tap_dance_actions| -> Result<Vec<&'static KanataAction>> {
2863            let mut actions = Vec::new();
2864            for expr in tap_dance_actions {
2865                let ac = parse_action(expr, s)?;
2866                actions.push(ac);
2867            }
2868            Ok(actions)
2869        })
2870        .ok_or_else(|| anyhow_expr!(&ac_params[1], "{ERR_MSG}: expected a list"))??;
2871
2872    Ok(s.a.sref(Action::TapDance(s.a.sref(TapDance {
2873        timeout,
2874        actions: s.a.sref_vec(actions),
2875        config,
2876    }))))
2877}
2878
2879fn parse_chord(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2880    const ERR_MSG: &str = "Action chord expects a chords group name followed by an identifier";
2881    if ac_params.len() != 2 {
2882        bail!(ERR_MSG);
2883    }
2884
2885    let name = ac_params[0]
2886        .atom(s.vars())
2887        .ok_or_else(|| anyhow_expr!(&ac_params[0], "{ERR_MSG}"))?;
2888    let group = match s.chord_groups.get(name) {
2889        Some(t) => t,
2890        None => bail_expr!(&ac_params[0], "Referenced unknown chord group: {}.", name),
2891    };
2892    let chord_key_index = ac_params[1]
2893        .atom(s.vars())
2894        .map(|s| match group.keys.iter().position(|e| e == s) {
2895            Some(i) => Ok(i),
2896            None => err_expr!(
2897                &ac_params[1],
2898                r#"Identifier "{}" is not used in chord group "{}"."#,
2899                &s,
2900                name,
2901            ),
2902        })
2903        .ok_or_else(|| anyhow_expr!(&ac_params[0], "{ERR_MSG}"))??;
2904    let chord_keys: u128 = 1 << chord_key_index;
2905
2906    // We don't yet know at this point what the entire chords group will look like nor at which
2907    // coords this action will end up. So instead we store a dummy action which will be properly
2908    // resolved in `resolve_chord_groups`.
2909    Ok(s.a.sref(Action::Chords(s.a.sref(ChordsGroup {
2910        timeout: group.timeout,
2911        coords: s.a.sref_vec(vec![((0, group.id), chord_keys)]),
2912        chords: s.a.sref_vec(vec![]),
2913    }))))
2914}
2915
2916fn parse_release_key(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2917    const ERR_MSG: &str = "release-key expects exactly one keycode (e.g. lalt)";
2918    if ac_params.len() != 1 {
2919        bail!("{ERR_MSG}: found {} items", ac_params.len());
2920    }
2921    let ac = parse_action(&ac_params[0], s)?;
2922    match ac {
2923        Action::KeyCode(kc) => {
2924            Ok(s.a.sref(Action::ReleaseState(ReleasableState::KeyCode(*kc))))
2925        }
2926        _ => err_expr!(&ac_params[0], "{}", ERR_MSG),
2927    }
2928}
2929
2930fn parse_release_layer(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2931    Ok(s.a
2932        .sref(Action::ReleaseState(ReleasableState::Layer(layer_idx(
2933            ac_params,
2934            &s.layer_idxs,
2935            s,
2936        )?))))
2937}
2938
2939fn create_defsrc_layer() -> [KanataAction; KEYS_IN_ROW] {
2940    let mut layer = [KanataAction::NoOp; KEYS_IN_ROW];
2941
2942    for (i, ac) in layer.iter_mut().enumerate() {
2943        *ac = OsCode::from_u16(i as u16)
2944            .map(|osc| Action::KeyCode(osc.into()))
2945            .unwrap_or(Action::NoOp);
2946    }
2947    // Ensure 0-index is no-op.
2948    layer[0] = KanataAction::NoOp;
2949    layer
2950}
2951
2952fn parse_chord_groups(exprs: &[&Spanned<Vec<SExpr>>], s: &mut ParserState) -> Result<()> {
2953    const MSG: &str = "Incorrect number of elements found in defchords.\nThere should be the group name, followed by timeout, followed by keys-action pairs";
2954    for expr in exprs {
2955        let mut subexprs = check_first_expr(expr.t.iter(), "defchords")?;
2956        let name = subexprs
2957            .next()
2958            .and_then(|e| e.atom(s.vars()))
2959            .ok_or_else(|| anyhow_span!(expr, "{MSG}"))?
2960            .to_owned();
2961        let timeout = match subexprs.next() {
2962            Some(e) => parse_non_zero_u16(e, s, "timeout")?,
2963            None => bail_span!(expr, "{MSG}"),
2964        };
2965        let id = match s.chord_groups.len().try_into() {
2966            Ok(id) => id,
2967            Err(_) => bail_span!(expr, "Maximum number of chord groups exceeded."),
2968        };
2969        let mut group = ChordGroup {
2970            id,
2971            name: name.clone(),
2972            keys: Vec::new(),
2973            coords: Vec::new(),
2974            chords: HashMap::default(),
2975            timeout,
2976        };
2977        // Read k-v pairs from the configuration
2978        while let Some(keys_expr) = subexprs.next() {
2979            let action = match subexprs.next() {
2980                Some(v) => v,
2981                None => bail_expr!(
2982                    keys_expr,
2983                    "Key list found without action - add an action for this chord"
2984                ),
2985            };
2986            let mut keys = keys_expr
2987                .list(s.vars())
2988                .map(|keys| {
2989                    keys.iter().map(|key| {
2990                        key.atom(s.vars()).ok_or_else(|| {
2991                            anyhow_expr!(
2992                                key,
2993                                "Chord keys cannot be lists. Invalid key name: {:?}",
2994                                key
2995                            )
2996                        })
2997                    })
2998                })
2999                .ok_or_else(|| anyhow_expr!(keys_expr, "Chord must be a list/set of keys"))?;
3000            let mask: u128 = keys.try_fold(0, |mask, key| {
3001                let key = key?;
3002                let index = match group.keys.iter().position(|k| k == key) {
3003                    Some(i) => i,
3004                    None => {
3005                        let i = group.keys.len();
3006                        if i + 1 > MAX_CHORD_KEYS {
3007                            bail_expr!(keys_expr, "Maximum number of keys in a chords group ({MAX_CHORD_KEYS}) exceeded - found {}", i + 1);
3008                        }
3009                        group.keys.push(key.to_owned());
3010                        i
3011                    }
3012                };
3013                Ok(mask | (1 << index))
3014            })?;
3015            if group.chords.insert(mask, action.clone()).is_some() {
3016                bail_expr!(keys_expr, "Duplicate chord in group {name}");
3017            }
3018        }
3019        if s.chord_groups.insert(name.to_owned(), group).is_some() {
3020            bail_span!(expr, "Duplicate chords group: {}", name);
3021        }
3022    }
3023    Ok(())
3024}
3025
3026fn resolve_chord_groups(layers: &mut IntermediateLayers, s: &ParserState) -> Result<()> {
3027    let mut chord_groups = s.chord_groups.values().cloned().collect::<Vec<_>>();
3028    chord_groups.sort_by_key(|group| group.id);
3029
3030    for layer in layers.iter() {
3031        for (i, row) in layer.iter().enumerate() {
3032            for (j, cell) in row.iter().enumerate() {
3033                find_chords_coords(&mut chord_groups, (i as u8, j as u16), cell);
3034            }
3035        }
3036    }
3037
3038    let chord_groups = chord_groups.into_iter().map(|group| {
3039        // Check that all keys in the chord group have been assigned to some coordinate
3040        for (key_index, key) in group.keys.iter().enumerate() {
3041            let key_mask = 1 << key_index;
3042            if !group.coords.iter().any(|(_, keys)| keys & key_mask != 0) {
3043                bail!("coord group `{0}` defines unused key `{1}`, did you forget to bind `(chord {0} {1})`?", group.name, key)
3044            }
3045        }
3046
3047        let chords = group.chords.iter().map(|(mask, action)| {
3048            Ok((*mask, parse_action(action, s)?))
3049        }).collect::<Result<Vec<_>>>()?;
3050
3051        Ok(s.a.sref(ChordsGroup {
3052            coords: s.a.sref_vec(group.coords),
3053            chords: s.a.sref_vec(chords),
3054            timeout: group.timeout,
3055        }))
3056    }).collect::<Result<Vec<_>>>()?;
3057
3058    for layer in layers.iter_mut() {
3059        for row in layer.iter_mut() {
3060            for cell in row.iter_mut() {
3061                if let Some(action) = fill_chords(&chord_groups, cell, s) {
3062                    *cell = action;
3063                }
3064            }
3065        }
3066    }
3067
3068    Ok(())
3069}
3070
3071fn find_chords_coords(chord_groups: &mut [ChordGroup], coord: (u8, u16), action: &KanataAction) {
3072    match action {
3073        Action::Chords(ChordsGroup { coords, .. }) => {
3074            for ((_, group_id), chord_keys) in coords.iter() {
3075                let group = &mut chord_groups[*group_id as usize];
3076                group.coords.push((coord, *chord_keys));
3077            }
3078        }
3079        Action::NoOp
3080        | Action::Trans
3081        | Action::Src
3082        | Action::Repeat
3083        | Action::KeyCode(_)
3084        | Action::MultipleKeyCodes(_)
3085        | Action::Layer(_)
3086        | Action::DefaultLayer(_)
3087        | Action::Sequence { .. }
3088        | Action::RepeatableSequence { .. }
3089        | Action::CancelSequences
3090        | Action::ReleaseState(_)
3091        | Action::OneShotIgnoreEventsTicks(_)
3092        | Action::Custom(_) => {}
3093        Action::HoldTap(HoldTapAction { tap, hold, .. }) => {
3094            find_chords_coords(chord_groups, coord, tap);
3095            find_chords_coords(chord_groups, coord, hold);
3096        }
3097        Action::OneShot(OneShot { action: ac, .. }) => {
3098            find_chords_coords(chord_groups, coord, ac);
3099        }
3100        Action::MultipleActions(actions) => {
3101            for ac in actions.iter() {
3102                find_chords_coords(chord_groups, coord, ac);
3103            }
3104        }
3105        Action::TapDance(TapDance { actions, .. }) => {
3106            for ac in actions.iter() {
3107                find_chords_coords(chord_groups, coord, ac);
3108            }
3109        }
3110        Action::Fork(ForkConfig { left, right, .. }) => {
3111            find_chords_coords(chord_groups, coord, left);
3112            find_chords_coords(chord_groups, coord, right);
3113        }
3114        Action::Switch(Switch { cases }) => {
3115            for case in cases.iter() {
3116                find_chords_coords(chord_groups, coord, case.1);
3117            }
3118        }
3119    }
3120}
3121
3122fn fill_chords(
3123    chord_groups: &[&'static ChordsGroup<&&[&CustomAction]>],
3124    action: &KanataAction,
3125    s: &ParserState,
3126) -> Option<KanataAction> {
3127    match action {
3128        Action::Chords(ChordsGroup { coords, .. }) => {
3129            let ((_, group_id), _) = coords
3130                .iter()
3131                .next()
3132                .expect("unresolved chords should have exactly one entry");
3133            Some(Action::Chords(chord_groups[*group_id as usize]))
3134        }
3135        Action::NoOp
3136        | Action::Trans
3137        | Action::Repeat
3138        | Action::Src
3139        | Action::KeyCode(_)
3140        | Action::MultipleKeyCodes(_)
3141        | Action::Layer(_)
3142        | Action::DefaultLayer(_)
3143        | Action::Sequence { .. }
3144        | Action::RepeatableSequence { .. }
3145        | Action::CancelSequences
3146        | Action::ReleaseState(_)
3147        | Action::OneShotIgnoreEventsTicks(_)
3148        | Action::Custom(_) => None,
3149        Action::HoldTap(&hta @ HoldTapAction { tap, hold, .. }) => {
3150            let new_tap = fill_chords(chord_groups, &tap, s);
3151            let new_hold = fill_chords(chord_groups, &hold, s);
3152            if new_tap.is_some() || new_hold.is_some() {
3153                Some(Action::HoldTap(s.a.sref(HoldTapAction {
3154                    hold: new_hold.unwrap_or(hold),
3155                    tap: new_tap.unwrap_or(tap),
3156                    ..hta
3157                })))
3158            } else {
3159                None
3160            }
3161        }
3162        Action::OneShot(&os @ OneShot { action: ac, .. }) => {
3163            fill_chords(chord_groups, ac, s).map(|ac| {
3164                Action::OneShot(s.a.sref(OneShot {
3165                    action: s.a.sref(ac),
3166                    ..os
3167                }))
3168            })
3169        }
3170        Action::MultipleActions(actions) => {
3171            let new_actions = actions
3172                .iter()
3173                .map(|ac| fill_chords(chord_groups, ac, s))
3174                .collect::<Vec<_>>();
3175            if new_actions.iter().any(|it| it.is_some()) {
3176                let new_actions = new_actions
3177                    .iter()
3178                    .zip(**actions)
3179                    .map(|(new_ac, ac)| new_ac.unwrap_or(*ac))
3180                    .collect::<Vec<_>>();
3181                Some(Action::MultipleActions(s.a.sref(s.a.sref_vec(new_actions))))
3182            } else {
3183                None
3184            }
3185        }
3186        Action::TapDance(&td @ TapDance { actions, .. }) => {
3187            let new_actions = actions
3188                .iter()
3189                .map(|ac| fill_chords(chord_groups, ac, s))
3190                .collect::<Vec<_>>();
3191            if new_actions.iter().any(|it| it.is_some()) {
3192                let new_actions = new_actions
3193                    .iter()
3194                    .zip(actions)
3195                    .map(|(new_ac, ac)| new_ac.map(|v| s.a.sref(v)).unwrap_or(*ac))
3196                    .collect::<Vec<_>>();
3197                Some(Action::TapDance(s.a.sref(TapDance {
3198                    actions: s.a.sref_vec(new_actions),
3199                    ..td
3200                })))
3201            } else {
3202                None
3203            }
3204        }
3205        Action::Fork(&fcfg @ ForkConfig { left, right, .. }) => {
3206            let new_left = fill_chords(chord_groups, &left, s);
3207            let new_right = fill_chords(chord_groups, &right, s);
3208            if new_left.is_some() || new_right.is_some() {
3209                Some(Action::Fork(s.a.sref(ForkConfig {
3210                    left: new_left.unwrap_or(left),
3211                    right: new_right.unwrap_or(right),
3212                    ..fcfg
3213                })))
3214            } else {
3215                None
3216            }
3217        }
3218        Action::Switch(Switch { cases }) => {
3219            let mut new_cases = vec![];
3220            for case in cases.iter() {
3221                new_cases.push((
3222                    case.0,
3223                    fill_chords(chord_groups, case.1, s)
3224                        .map(|ac| s.a.sref(ac))
3225                        .unwrap_or(case.1),
3226                    case.2,
3227                ));
3228            }
3229            Some(Action::Switch(s.a.sref(Switch {
3230                cases: s.a.sref_vec(new_cases),
3231            })))
3232        }
3233    }
3234}
3235
3236fn parse_fake_keys(exprs: &[&Vec<SExpr>], s: &mut ParserState) -> Result<()> {
3237    for expr in exprs {
3238        let mut subexprs = check_first_expr(expr.iter(), "deffakekeys")?;
3239        // Read k-v pairs from the configuration
3240        while let Some(key_name_expr) = subexprs.next() {
3241            let key_name = key_name_expr
3242                .atom(s.vars())
3243                .ok_or_else(|| anyhow_expr!(key_name_expr, "Fake key name must not be a list."))?
3244                .to_owned();
3245            let action = match subexprs.next() {
3246                Some(v) => v,
3247                None => bail_expr!(
3248                    key_name_expr,
3249                    "Fake key name has no action - you should add an action."
3250                ),
3251            };
3252            let action = parse_action(action, s)?;
3253            let idx = s.virtual_keys.len();
3254            log::trace!("inserting {key_name}->{idx}:{action:?}");
3255            if s.virtual_keys
3256                .insert(key_name.clone(), (idx, action))
3257                .is_some()
3258            {
3259                bail_expr!(key_name_expr, "Duplicate fake key: {}", key_name);
3260            }
3261            #[cfg(feature = "lsp")]
3262            s.lsp_hints
3263                .borrow_mut()
3264                .definition_locations
3265                .virtual_key
3266                .insert(key_name, key_name_expr.span());
3267        }
3268    }
3269    if s.virtual_keys.len() > KEYS_IN_ROW {
3270        bail!(
3271            "Maximum number of fake keys is {KEYS_IN_ROW}, found {}",
3272            s.virtual_keys.len()
3273        );
3274    }
3275    Ok(())
3276}
3277
3278fn parse_virtual_keys(exprs: &[&Vec<SExpr>], s: &mut ParserState) -> Result<()> {
3279    s.pctx.is_within_defvirtualkeys = true;
3280    for expr in exprs {
3281        let mut subexprs = check_first_expr(expr.iter(), "defvirtualkeys")?;
3282        // Read k-v pairs from the configuration
3283        while let Some(key_name_expr) = subexprs.next() {
3284            let key_name = key_name_expr
3285                .atom(s.vars())
3286                .ok_or_else(|| anyhow_expr!(key_name_expr, "Virtual key name must not be a list."))?
3287                .to_owned();
3288            let action = match subexprs.next() {
3289                Some(v) => v,
3290                None => bail_expr!(
3291                    key_name_expr,
3292                    "Virtual key name has no action - you must add an action."
3293                ),
3294            };
3295            let action = parse_action(action, s)?;
3296            let idx = s.virtual_keys.len();
3297            log::trace!("inserting {key_name}->{idx}:{action:?}");
3298            if s.virtual_keys
3299                .insert(key_name.clone(), (idx, action))
3300                .is_some()
3301            {
3302                bail_expr!(key_name_expr, "Duplicate virtual key: {}", key_name);
3303            };
3304            #[cfg(feature = "lsp")]
3305            s.lsp_hints
3306                .borrow_mut()
3307                .definition_locations
3308                .virtual_key
3309                .insert(key_name, key_name_expr.span());
3310        }
3311    }
3312    s.pctx.is_within_defvirtualkeys = false;
3313    if s.virtual_keys.len() > KEYS_IN_ROW {
3314        bail!(
3315            "Maximum number of virtual keys is {KEYS_IN_ROW}, found {}",
3316            s.virtual_keys.len()
3317        );
3318    }
3319    Ok(())
3320}
3321
3322fn parse_distance(expr: &SExpr, s: &ParserState, label: &str) -> Result<u16> {
3323    expr.atom(s.vars())
3324        .map(str::parse::<u16>)
3325        .and_then(|d| d.ok())
3326        .ok_or_else(|| anyhow_expr!(expr, "{label} must be 1-30000"))
3327}
3328
3329fn parse_mwheel(
3330    ac_params: &[SExpr],
3331    direction: MWheelDirection,
3332    s: &ParserState,
3333) -> Result<&'static KanataAction> {
3334    const ERR_MSG: &str = "mwheel expects 2 parameters: <interval (ms)> <distance>";
3335    if ac_params.len() != 2 {
3336        bail!("{ERR_MSG}, found {}", ac_params.len());
3337    }
3338    let interval = parse_non_zero_u16(&ac_params[0], s, "interval")?;
3339    let distance = parse_distance(&ac_params[1], s, "distance")?;
3340    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3341        CustomAction::MWheel {
3342            direction,
3343            interval,
3344            distance,
3345            inertial_scroll_params: None,
3346        },
3347    )))))
3348}
3349
3350fn parse_mwheel_accel(
3351    ac_params: &[SExpr],
3352    direction: MWheelDirection,
3353    s: &ParserState,
3354) -> Result<&'static KanataAction> {
3355    const ERR_MSG: &str = "mwheel-accel expects 4 float32 parameters:\n\
3356                           - initial velocity\n- maximum velocity\n\
3357                           - acceleration multiplier\n- deceleration multiplier";
3358    if ac_params.len() != 4 {
3359        bail!("{ERR_MSG}, found {}", ac_params.len());
3360    }
3361    let initial_velocity = parse_f32(&ac_params[0], s, "initial velocity", 1.0, 12000.0)?;
3362    let maximum_velocity = parse_f32(&ac_params[1], s, "maximum velocity", 1.0, 12000.0)?;
3363    let acceleration_multiplier =
3364        parse_f32(&ac_params[2], s, "acceleration multiplier", 1.0, 1000.0)?;
3365    let deceleration_multiplier =
3366        parse_f32(&ac_params[3], s, "deceleration multiplier", 0.0, 0.99)?;
3367    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3368        CustomAction::MWheel {
3369            direction,
3370            interval: 16,
3371            distance: 1,
3372            inertial_scroll_params: Some(Box::new(MWheelInertial {
3373                initial_velocity,
3374                maximum_velocity,
3375                acceleration_multiplier,
3376                deceleration_multiplier,
3377            })),
3378        },
3379    )))))
3380}
3381
3382fn parse_move_mouse(
3383    ac_params: &[SExpr],
3384    direction: MoveDirection,
3385    s: &ParserState,
3386) -> Result<&'static KanataAction> {
3387    const ERR_MSG: &str = "movemouse expects 2 parameters: <interval (ms)> <distance (px)>";
3388    if ac_params.len() != 2 {
3389        bail!("{ERR_MSG}, found {}", ac_params.len());
3390    }
3391    let interval = parse_non_zero_u16(&ac_params[0], s, "interval")?;
3392    let distance = parse_distance(&ac_params[1], s, "distance")?;
3393    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3394        CustomAction::MoveMouse {
3395            direction,
3396            interval,
3397            distance,
3398        },
3399    )))))
3400}
3401
3402fn parse_move_mouse_accel(
3403    ac_params: &[SExpr],
3404    direction: MoveDirection,
3405    s: &ParserState,
3406) -> Result<&'static KanataAction> {
3407    if ac_params.len() != 4 {
3408        bail!(
3409            "movemouse-accel expects four parameters, found {}\n<interval (ms)> <acceleration time (ms)> <min_distance> <max_distance>",
3410            ac_params.len()
3411        );
3412    }
3413    let interval = parse_non_zero_u16(&ac_params[0], s, "interval")?;
3414    let accel_time = parse_non_zero_u16(&ac_params[1], s, "acceleration time")?;
3415    let min_distance = parse_distance(&ac_params[2], s, "min distance")?;
3416    let max_distance = parse_distance(&ac_params[3], s, "max distance")?;
3417    if min_distance > max_distance {
3418        bail!("min distance should be less than max distance")
3419    }
3420    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3421        CustomAction::MoveMouseAccel {
3422            direction,
3423            interval,
3424            accel_time,
3425            min_distance,
3426            max_distance,
3427        },
3428    )))))
3429}
3430
3431fn parse_move_mouse_speed(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3432    if ac_params.len() != 1 {
3433        bail!(
3434            "movemouse-speed expects one parameter, found {}\n<speed scaling % (1-65535)>",
3435            ac_params.len()
3436        );
3437    }
3438    let speed = parse_non_zero_u16(&ac_params[0], s, "speed scaling %")?;
3439    Ok(s.a.sref(Action::Custom(
3440        s.a.sref(s.a.sref_slice(CustomAction::MoveMouseSpeed { speed })),
3441    )))
3442}
3443
3444fn parse_set_mouse(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3445    if ac_params.len() != 2 {
3446        bail!(
3447            "movemouse-accel expects two parameters, found {}: <x> <y>",
3448            ac_params.len()
3449        );
3450    }
3451    let x = parse_u16(&ac_params[0], s, "x")?;
3452    let y = parse_u16(&ac_params[1], s, "y")?;
3453    Ok(s.a.sref(Action::Custom(
3454        s.a.sref(s.a.sref_slice(CustomAction::SetMouse { x, y })),
3455    )))
3456}
3457
3458fn parse_dynamic_macro_record(
3459    ac_params: &[SExpr],
3460    s: &ParserState,
3461) -> Result<&'static KanataAction> {
3462    const ERR_MSG: &str = "dynamic-macro-record expects 1 parameter: <macro ID (0-65535)>";
3463    if ac_params.len() != 1 {
3464        bail!("{ERR_MSG}, found {}", ac_params.len());
3465    }
3466    let key = parse_u16(&ac_params[0], s, "macro ID")?;
3467    Ok(s.a.sref(Action::Custom(
3468        s.a.sref(s.a.sref_slice(CustomAction::DynamicMacroRecord(key))),
3469    )))
3470}
3471
3472fn parse_dynamic_macro_play(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3473    const ERR_MSG: &str = "dynamic-macro-play expects 1 parameter: <macro ID (number 0-65535)>";
3474    if ac_params.len() != 1 {
3475        bail!("{ERR_MSG}, found {}", ac_params.len());
3476    }
3477    let key = parse_u16(&ac_params[0], s, "macro ID")?;
3478    Ok(s.a.sref(Action::Custom(
3479        s.a.sref(s.a.sref_slice(CustomAction::DynamicMacroPlay(key))),
3480    )))
3481}
3482
3483fn parse_live_reload_num(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3484    const ERR_MSG: &str = "expects 1 parameter: <config argument position (1-65535)>";
3485    if ac_params.len() != 1 {
3486        bail!("{LIVE_RELOAD_NUM} {ERR_MSG}, found {}", ac_params.len());
3487    }
3488    let num = parse_non_zero_u16(&ac_params[0], s, "config argument position")?;
3489    Ok(s.a.sref(Action::Custom(
3490        // Note: for user-friendliness (hopefully), begin at 1 for parsing.
3491        // But for use as an index when stored as data, subtract 1 for 0-based indexing.
3492        s.a.sref(s.a.sref_slice(CustomAction::LiveReloadNum(num - 1))),
3493    )))
3494}
3495
3496fn parse_live_reload_file(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3497    const ERR_MSG: &str = "expects 1 parameter: <config argument (exact path)>";
3498    if ac_params.len() != 1 {
3499        bail!("{LIVE_RELOAD_FILE} {ERR_MSG}, found {}", ac_params.len());
3500    }
3501    let expr = &ac_params[0];
3502    let spanned_filepath = match expr {
3503        SExpr::Atom(filepath) => filepath,
3504        SExpr::List(_) => {
3505            bail_expr!(&expr, "Filepath cannot be a list")
3506        }
3507    };
3508    let lrld_file_path = spanned_filepath.t.trim_atom_quotes();
3509    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3510        CustomAction::LiveReloadFile(lrld_file_path.to_string()),
3511    )))))
3512}
3513
3514fn parse_clipboard_set(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3515    const ERR_MSG: &str = "expects 1 parameter: <clipboard string>";
3516    if ac_params.len() != 1 {
3517        bail!("{CLIPBOARD_SET} {ERR_MSG}, found {}", ac_params.len());
3518    }
3519    let expr = &ac_params[0];
3520    let clip_string = match expr {
3521        SExpr::Atom(filepath) => filepath,
3522        SExpr::List(_) => {
3523            bail_expr!(&expr, "Clipboard string cannot be a list")
3524        }
3525    };
3526    let clip_string = clip_string.t.trim_atom_quotes();
3527    Ok(s.a.sref(Action::Custom(s.a.sref(
3528        s.a.sref_slice(CustomAction::ClipboardSet(clip_string.to_string())),
3529    ))))
3530}
3531
3532fn parse_clipboard_save(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3533    const ERR_MSG: &str = "expects 1 parameter: <clipboard save id (0-65535)>";
3534    if ac_params.len() != 1 {
3535        bail!("{CLIPBOARD_SAVE} {ERR_MSG}, found {}", ac_params.len());
3536    }
3537    let id = parse_u16(&ac_params[0], s, "clipboard save ID")?;
3538    Ok(s.a.sref(Action::Custom(
3539        s.a.sref(s.a.sref_slice(CustomAction::ClipboardSave(id))),
3540    )))
3541}
3542
3543fn parse_clipboard_restore(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3544    const ERR_MSG: &str = "expects 1 parameter: <clipboard save id (0-65535)>";
3545    if ac_params.len() != 1 {
3546        bail!("{CLIPBOARD_RESTORE} {ERR_MSG}, found {}", ac_params.len());
3547    }
3548    let id = parse_u16(&ac_params[0], s, "clipboard save ID")?;
3549    Ok(s.a.sref(Action::Custom(
3550        s.a.sref(s.a.sref_slice(CustomAction::ClipboardRestore(id))),
3551    )))
3552}
3553
3554fn parse_clipboard_save_swap(
3555    ac_params: &[SExpr],
3556    s: &ParserState,
3557) -> Result<&'static KanataAction> {
3558    const ERR_MSG: &str =
3559        "expects 2 parameters: <clipboard save id (0-65535)> <clipboard save id #2>";
3560    if ac_params.len() != 2 {
3561        bail!("{CLIPBOARD_SAVE_SWAP} {ERR_MSG}, found {}", ac_params.len());
3562    }
3563    let id1 = parse_u16(&ac_params[0], s, "clipboard save ID")?;
3564    let id2 = parse_u16(&ac_params[1], s, "clipboard save ID")?;
3565    Ok(s.a.sref(Action::Custom(
3566        s.a.sref(s.a.sref_slice(CustomAction::ClipboardSaveSwap(id1, id2))),
3567    )))
3568}
3569
3570fn parse_clipboard_save_set(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3571    const ERR_MSG: &str = "expects 2 parameters: <clipboard save id (0-65535)> <save content>";
3572    if ac_params.len() != 2 {
3573        bail!("{CLIPBOARD_SAVE_SET} {ERR_MSG}, found {}", ac_params.len());
3574    }
3575    let id = parse_u16(&ac_params[0], s, "clipboard save ID")?;
3576    let save_content = ac_params[1]
3577        .atom(s.vars())
3578        .ok_or_else(|| anyhow_expr!(&ac_params[1], "save content must be a string"))?;
3579    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3580        CustomAction::ClipboardSaveSet(id, save_content.into()),
3581    )))))
3582}
3583
3584fn parse_layers(
3585    s: &ParserState,
3586    mapped_keys: &mut MappedKeys,
3587    defcfg: &CfgOptions,
3588) -> Result<IntermediateLayers> {
3589    let mut layers_cfg = new_layers(s.layer_exprs.len());
3590    if s.layer_exprs.len() > MAX_LAYERS {
3591        bail!("Maximum number of layers ({}) exceeded.", MAX_LAYERS);
3592    }
3593    let mut defsrc_layer = s.defsrc_layer;
3594    for (layer_level, layer) in s.layer_exprs.iter().enumerate() {
3595        match layer {
3596            // The skip is done to skip the `deflayer` and layer name tokens.
3597            LayerExprs::DefsrcMapping(layer) => {
3598                // Parse actions in the layer and place them appropriately according
3599                // to defsrc mapping order.
3600                for (i, ac) in layer.iter().skip(2).enumerate() {
3601                    let ac = parse_action(ac, s)?;
3602                    layers_cfg[layer_level][0][s.mapping_order[i]] = *ac;
3603                }
3604            }
3605            LayerExprs::CustomMapping(layer) => {
3606                // Parse actions as input output pairs
3607                let mut pairs = layer[2..].chunks_exact(2);
3608                let mut layer_mapped_keys = HashSet::default();
3609                let mut defsrc_anykey_used = false;
3610                let mut unmapped_anykey_used = false;
3611                let mut both_anykey_used = false;
3612                for pair in pairs.by_ref() {
3613                    let input = &pair[0];
3614                    let action = &pair[1];
3615
3616                    let action = parse_action(action, s)?;
3617                    if input.atom(s.vars()).is_some_and(|x| x == "_") {
3618                        if defsrc_anykey_used {
3619                            bail_expr!(input, "must have only one use of _ within a layer")
3620                        }
3621                        if both_anykey_used {
3622                            bail_expr!(input, "must either use _ or ___ within a layer, not both")
3623                        }
3624                        for i in 0..s.mapping_order.len() {
3625                            if layers_cfg[layer_level][0][s.mapping_order[i]] == DEFAULT_ACTION {
3626                                layers_cfg[layer_level][0][s.mapping_order[i]] = *action;
3627                            }
3628                        }
3629                        defsrc_anykey_used = true;
3630                    } else if input.atom(s.vars()).is_some_and(|x| x == "__") {
3631                        if unmapped_anykey_used {
3632                            bail_expr!(input, "must have only one use of __ within a layer")
3633                        }
3634                        if !defcfg.process_unmapped_keys {
3635                            bail_expr!(
3636                                input,
3637                                "must set process-unmapped-keys to yes to use __ to map unmapped keys"
3638                            );
3639                        }
3640                        if both_anykey_used {
3641                            bail_expr!(input, "must either use __ or ___ within a layer, not both")
3642                        }
3643                        for i in 0..layers_cfg[0][0].len() {
3644                            if layers_cfg[layer_level][0][i] == DEFAULT_ACTION
3645                                && !s.mapping_order.contains(&i)
3646                            {
3647                                layers_cfg[layer_level][0][i] = *action;
3648                            }
3649                        }
3650                        unmapped_anykey_used = true;
3651                    } else if input.atom(s.vars()).is_some_and(|x| x == "___") {
3652                        if both_anykey_used {
3653                            bail_expr!(input, "must have only one use of ___ within a layer")
3654                        }
3655                        if defsrc_anykey_used {
3656                            bail_expr!(input, "must either use _ or ___ within a layer, not both")
3657                        }
3658                        if unmapped_anykey_used {
3659                            bail_expr!(input, "must either use __ or ___ within a layer, not both")
3660                        }
3661                        if !defcfg.process_unmapped_keys {
3662                            bail_expr!(
3663                                input,
3664                                "must set process-unmapped-keys to yes to use ___ to also map unmapped keys"
3665                            );
3666                        }
3667                        for i in 0..layers_cfg[0][0].len() {
3668                            if layers_cfg[layer_level][0][i] == DEFAULT_ACTION {
3669                                layers_cfg[layer_level][0][i] = *action;
3670                            }
3671                        }
3672                        both_anykey_used = true;
3673                    } else {
3674                        let input_key = input
3675                            .atom(s.vars())
3676                            .and_then(str_to_oscode)
3677                            .ok_or_else(|| anyhow_expr!(input, "input must be a key name"))?;
3678                        mapped_keys.insert(input_key);
3679                        if !layer_mapped_keys.insert(input_key) {
3680                            bail_expr!(input, "input key must not be repeated within a layer")
3681                        }
3682                        layers_cfg[layer_level][0][usize::from(input_key)] = *action;
3683                    }
3684                }
3685                let rem = pairs.remainder();
3686                if !rem.is_empty() {
3687                    bail_expr!(&rem[0], "input must by followed by an action");
3688                }
3689            }
3690        }
3691        for (osc, layer_action) in layers_cfg[layer_level][0].iter_mut().enumerate() {
3692            if *layer_action == DEFAULT_ACTION {
3693                *layer_action = match s.block_unmapped_keys && !is_a_button(osc as u16) {
3694                    true => Action::NoOp,
3695                    false => Action::Trans,
3696                };
3697            }
3698        }
3699
3700        // Set fake keys on every layer.
3701        for (y, action) in s.virtual_keys.values() {
3702            let (x, y) = get_fake_key_coords(*y);
3703            layers_cfg[layer_level][x as usize][y as usize] = **action;
3704        }
3705
3706        // If the user has configured delegation to the first (default) layer for transparent keys,
3707        // (as opposed to delegation to defsrc), replace the defsrc actions with the actions from
3708        // the first layer.
3709        if layer_level == 0 && s.delegate_to_first_layer {
3710            for (defsrc_ac, default_layer_ac) in defsrc_layer.iter_mut().zip(layers_cfg[0][0]) {
3711                if default_layer_ac != Action::Trans {
3712                    *defsrc_ac = default_layer_ac;
3713                }
3714            }
3715        }
3716
3717        // Very last thing - ensure index 0 is always no-op. This shouldn't have any way to be
3718        // physically activated. This enable other code to rely on there always being a no-op key.
3719        layers_cfg[layer_level][0][0] = Action::NoOp;
3720    }
3721    Ok(layers_cfg)
3722}
3723
3724const SEQ_ERR: &str = "defseq expects pairs of parameters: <virtual_key_name> <key_list>";
3725
3726fn parse_sequences(exprs: &[&Vec<SExpr>], s: &ParserState) -> Result<KeySeqsToFKeys> {
3727    let mut sequences = Trie::new();
3728    for expr in exprs {
3729        let mut subexprs = check_first_expr(expr.iter(), "defseq")?.peekable();
3730
3731        while let Some(vkey_expr) = subexprs.next() {
3732            let vkey = vkey_expr.atom(s.vars()).ok_or_else(|| {
3733                anyhow_expr!(vkey_expr, "{SEQ_ERR}\nvirtual_key_name must not be a list")
3734            })?;
3735            #[cfg(feature = "lsp")]
3736            s.lsp_hints
3737                .borrow_mut()
3738                .reference_locations
3739                .virtual_key
3740                .push(vkey, vkey_expr.span());
3741            if !s.virtual_keys.contains_key(vkey) {
3742                bail_expr!(
3743                    vkey_expr,
3744                    "{SEQ_ERR}\nThe referenced key does not exist: {vkey}"
3745                );
3746            }
3747            let key_seq_expr = subexprs
3748                .next()
3749                .ok_or_else(|| anyhow_expr!(vkey_expr, "{SEQ_ERR}\nMissing key_list for {vkey}"))?;
3750            let key_seq = key_seq_expr.list(s.vars()).ok_or_else(|| {
3751                anyhow_expr!(key_seq_expr, "{SEQ_ERR}\nGot a non-list for key_list")
3752            })?;
3753            if key_seq.is_empty() {
3754                bail_expr!(key_seq_expr, "{SEQ_ERR}\nkey_list cannot be empty");
3755            }
3756
3757            let keycode_seq = parse_sequence_keys(key_seq, s)?;
3758
3759            // Generate permutations of sequences for overlapping keys.
3760            let mut permutations = vec![vec![]];
3761            let mut vals = keycode_seq.iter().copied();
3762            while let Some(val) = vals.next() {
3763                if val & KEY_OVERLAP_MARKER == 0 {
3764                    for p in permutations.iter_mut() {
3765                        p.push(val);
3766                    }
3767                    continue;
3768                }
3769
3770                if val == 0x0400 {
3771                    bail_expr!(
3772                        key_seq_expr,
3773                        "O-(...) lists must have a minimum of 2 elements"
3774                    );
3775                }
3776                let mut values_to_permute = vec![val];
3777                for val in vals.by_ref() {
3778                    if val == 0x0400 {
3779                        break;
3780                    }
3781                    values_to_permute.push(val);
3782                }
3783
3784                let ps = match values_to_permute.len() {
3785                    0 | 1 => bail_expr!(
3786                        key_seq_expr,
3787                        "O-(...) lists must have a minimum of 2 elements"
3788                    ),
3789                    2..=6 => gen_permutations(&values_to_permute[..]),
3790                    _ => bail_expr!(
3791                        key_seq_expr,
3792                        "O-(...) lists must have a maximum of 6 elements"
3793                    ),
3794                };
3795
3796                let mut new_permutations: Vec<Vec<u16>> = vec![];
3797                for p in permutations.iter() {
3798                    for p2 in ps.iter() {
3799                        new_permutations.push(
3800                            p.iter()
3801                                .copied()
3802                                .chain(p2.iter().copied().chain([KEY_OVERLAP_MARKER]))
3803                                .collect(),
3804                        );
3805                    }
3806                }
3807                permutations = new_permutations;
3808            }
3809
3810            for p in permutations.into_iter() {
3811                if sequences.ancestor_exists(&p) {
3812                    bail_expr!(
3813                        key_seq_expr,
3814                        "Sequence has a conflict: its sequence contains an earlier defined sequence"
3815                    );
3816                }
3817                if sequences.descendant_exists(&p) {
3818                    bail_expr!(
3819                        key_seq_expr,
3820                        "Sequence has a conflict: its sequence is contained within an earlier defined seqence"
3821                    );
3822                }
3823                sequences.insert(
3824                    p,
3825                    s.virtual_keys
3826                        .get(vkey)
3827                        .map(|(y, _)| get_fake_key_coords(*y))
3828                        .expect("vk exists, checked earlier"),
3829                );
3830            }
3831        }
3832    }
3833    Ok(sequences)
3834}
3835
3836fn parse_sequence_keys(exprs: &[SExpr], s: &ParserState) -> Result<Vec<u16>> {
3837    use SequenceEvent::*;
3838
3839    // Reuse macro parsing but do some other processing since sequences don't support everything
3840    // that can go in a macro, and also change error messages of course.
3841    let mut exprs_remaining = exprs;
3842    let mut all_keys = Vec::new();
3843    while !exprs_remaining.is_empty() {
3844        let (mut keys, exprs_remaining_tmp) =
3845            match parse_macro_item_impl(exprs_remaining, s, MacroNumberParseMode::Action) {
3846                Ok(res) => {
3847                    if res.0.iter().any(|k| !matches!(k, Press(..) | Release(..))) {
3848                        // Determine the bad expression depending on how many expressions were consumed
3849                        // by parse_macro_item_impl.
3850                        let bad_expr = if exprs_remaining.len() - res.1.len() == 1 {
3851                            &exprs_remaining[0]
3852                        } else {
3853                            // This error message will have an imprecise span since it will take the
3854                            // whole chorded list instead of the single element inside that's not a
3855                            // standard key. Oh well, should still be helpful. I'm too lazy to write
3856                            // the code to find the exact expr to use right now.
3857                            &exprs_remaining[1]
3858                        };
3859                        bail_expr!(bad_expr, "{SEQ_ERR}\nFound invalid key/chord in key_list");
3860                    }
3861
3862                    // The keys are currenty in the form of SequenceEvent::{Press, Release}. This is
3863                    // not what we want.
3864                    //
3865                    // The trivial and incorrect way to parse this would be to just take all of the
3866                    // presses. However, we need to transform chorded keys/lists like S-a or S-(a b) to
3867                    // have the upper bits set, to be able to differentiate (S-a b) from (S-(a b)).
3868                    //
3869                    // The order of presses and releases reveals whether or not a key is chorded with
3870                    // some modifier. When a chord starts, there are multiple presses in a row, whereas
3871                    // non-chords will always be a press followed by a release. Likewise, a chord
3872                    // ending is marked by multiple releases in a row.
3873                    let mut mods_currently_held = vec![];
3874                    let mut key_actions = res.0.iter().peekable();
3875                    let mut seq = vec![];
3876                    let mut do_release_mod = false;
3877                    while let Some(action) = key_actions.next() {
3878                        match action {
3879                            Press(pressed) => {
3880                                if matches!(key_actions.peek(), Some(Press(..))) {
3881                                    // press->press: current press is mod
3882                                    mods_currently_held.push(*pressed);
3883                                }
3884                                let mut seq_num = u16::from(OsCode::from(pressed));
3885                                for modk in mods_currently_held.iter().copied() {
3886                                    seq_num |= mod_mask_for_keycode(modk);
3887                                }
3888                                if seq_num & KEY_OVERLAP_MARKER == KEY_OVERLAP_MARKER
3889                                    && seq_num & MASK_MODDED != KEY_OVERLAP_MARKER
3890                                {
3891                                    bail_expr!(
3892                                        &exprs_remaining[0],
3893                                        "O-(...) lists cannot be combined with other modifiers."
3894                                    );
3895                                }
3896                                if *pressed != KEY_OVERLAP {
3897                                    // Note: key overlap item is special and goes at the end,
3898                                    // not the beginning
3899                                    seq.push(seq_num);
3900                                }
3901                            }
3902                            Release(released) => {
3903                                if *released == KEY_OVERLAP {
3904                                    seq.push(KEY_OVERLAP_MARKER);
3905                                }
3906                                if do_release_mod {
3907                                    mods_currently_held.remove(
3908                                        mods_currently_held
3909                                            .iter()
3910                                            .position(|modk| modk == released)
3911                                            .expect("had to be pressed to be released"),
3912                                    );
3913                                }
3914                                // release->release: next release is mod
3915                                do_release_mod = matches!(key_actions.peek(), Some(Release(..)));
3916                            }
3917                            _ => unreachable!("should be filtered out"),
3918                        }
3919                    }
3920
3921                    (seq, res.1)
3922                }
3923                Err(mut e) => {
3924                    e.msg = format!("{SEQ_ERR}\nFound invalid key/chord in key_list");
3925                    return Err(e);
3926                }
3927            };
3928        all_keys.append(&mut keys);
3929        exprs_remaining = exprs_remaining_tmp;
3930    }
3931    Ok(all_keys)
3932}
3933
3934fn parse_arbitrary_code(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3935    const ERR_MSG: &str = "arbitrary code expects one parameter: <code: 0-767>";
3936    if ac_params.len() != 1 {
3937        bail!("{ERR_MSG}");
3938    }
3939    let code = ac_params[0]
3940        .atom(s.vars())
3941        .map(str::parse::<u16>)
3942        .and_then(|c| c.ok())
3943        .ok_or_else(|| anyhow!("{ERR_MSG}: got {:?}", ac_params[0]))?;
3944    Ok(s.a.sref(Action::Custom(
3945        s.a.sref(s.a.sref_slice(CustomAction::SendArbitraryCode(code))),
3946    )))
3947}
3948
3949fn parse_overrides(exprs: &[SExpr], s: &ParserState) -> Result<Overrides> {
3950    const ERR_MSG: &str =
3951        "defoverrides expects pairs of parameters: <input key list> <output key list>";
3952    let mut subexprs = check_first_expr(exprs.iter(), "defoverrides")?;
3953
3954    let mut overrides = Vec::<Override>::new();
3955    while let Some(in_keys_expr) = subexprs.next() {
3956        let in_keys = in_keys_expr
3957            .list(s.vars())
3958            .ok_or_else(|| anyhow_expr!(in_keys_expr, "Input keys must be a list"))?;
3959        let out_keys_expr = subexprs
3960            .next()
3961            .ok_or_else(|| anyhow_expr!(in_keys_expr, "Missing output keys for input keys"))?;
3962        let out_keys = out_keys_expr
3963            .list(s.vars())
3964            .ok_or_else(|| anyhow_expr!(out_keys_expr, "Output keys must be a list"))?;
3965        let in_keys =
3966            in_keys
3967                .iter()
3968                .try_fold(vec![], |mut keys, key_expr| -> Result<Vec<OsCode>> {
3969                    let key = key_expr
3970                        .atom(s.vars())
3971                        .and_then(str_to_oscode)
3972                        .ok_or_else(|| {
3973                            anyhow_expr!(key_expr, "Unknown input key name, must use known keys")
3974                        })?;
3975                    keys.push(key);
3976                    Ok(keys)
3977                })?;
3978        let out_keys =
3979            out_keys
3980                .iter()
3981                .try_fold(vec![], |mut keys, key_expr| -> Result<Vec<OsCode>> {
3982                    let key = key_expr
3983                        .atom(s.vars())
3984                        .and_then(str_to_oscode)
3985                        .ok_or_else(|| {
3986                            anyhow_expr!(key_expr, "Unknown output key name, must use known keys")
3987                        })?;
3988                    keys.push(key);
3989                    Ok(keys)
3990                })?;
3991        overrides
3992            .push(Override::try_new(&in_keys, &out_keys).map_err(|e| anyhow!("{ERR_MSG}: {e}"))?);
3993    }
3994    log::debug!("All overrides:\n{overrides:#?}");
3995    Ok(Overrides::new(&overrides))
3996}
3997
3998fn parse_fork(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3999    const ERR_STR: &str =
4000        "fork expects 3 params: <left-action> <right-action> <right-trigger-keys>";
4001    if ac_params.len() != 3 {
4002        bail!("{ERR_STR}\nFound {} params instead of 3", ac_params.len());
4003    }
4004    let left = *parse_action(&ac_params[0], s)?;
4005    let right = *parse_action(&ac_params[1], s)?;
4006    let right_triggers = s.a.sref_vec(
4007        parse_key_list(&ac_params[2], s, "right-trigger-keys")?
4008            .into_iter()
4009            .map(KeyCode::from)
4010            .collect::<Vec<_>>(),
4011    );
4012    Ok(s.a.sref(Action::Fork(s.a.sref(ForkConfig {
4013        left,
4014        right,
4015        right_triggers,
4016    }))))
4017}
4018
4019fn parse_caps_word(
4020    ac_params: &[SExpr],
4021    repress_behaviour: CapsWordRepressBehaviour,
4022    s: &ParserState,
4023) -> Result<&'static KanataAction> {
4024    const ERR_STR: &str = "caps-word expects 1 param: <timeout>";
4025    if ac_params.len() != 1 {
4026        bail!("{ERR_STR}\nFound {} params instead of 1", ac_params.len());
4027    }
4028    let timeout = parse_non_zero_u16(&ac_params[0], s, "timeout")?;
4029    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
4030        CustomAction::CapsWord(CapsWordCfg {
4031            repress_behaviour,
4032            keys_to_capitalize: &[
4033                KeyCode::A,
4034                KeyCode::B,
4035                KeyCode::C,
4036                KeyCode::D,
4037                KeyCode::E,
4038                KeyCode::F,
4039                KeyCode::G,
4040                KeyCode::H,
4041                KeyCode::I,
4042                KeyCode::J,
4043                KeyCode::K,
4044                KeyCode::L,
4045                KeyCode::M,
4046                KeyCode::N,
4047                KeyCode::O,
4048                KeyCode::P,
4049                KeyCode::Q,
4050                KeyCode::R,
4051                KeyCode::S,
4052                KeyCode::T,
4053                KeyCode::U,
4054                KeyCode::V,
4055                KeyCode::W,
4056                KeyCode::X,
4057                KeyCode::Y,
4058                KeyCode::Z,
4059                KeyCode::Minus,
4060            ],
4061            keys_nonterminal: &[
4062                KeyCode::Kb0,
4063                KeyCode::Kb1,
4064                KeyCode::Kb2,
4065                KeyCode::Kb3,
4066                KeyCode::Kb4,
4067                KeyCode::Kb5,
4068                KeyCode::Kb6,
4069                KeyCode::Kb7,
4070                KeyCode::Kb8,
4071                KeyCode::Kb9,
4072                KeyCode::Kp0,
4073                KeyCode::Kp1,
4074                KeyCode::Kp2,
4075                KeyCode::Kp3,
4076                KeyCode::Kp4,
4077                KeyCode::Kp5,
4078                KeyCode::Kp6,
4079                KeyCode::Kp7,
4080                KeyCode::Kp8,
4081                KeyCode::Kp9,
4082                KeyCode::BSpace,
4083                KeyCode::Delete,
4084                KeyCode::Up,
4085                KeyCode::Down,
4086                KeyCode::Left,
4087                KeyCode::Right,
4088            ],
4089            timeout,
4090        }),
4091    )))))
4092}
4093
4094fn parse_caps_word_custom(
4095    ac_params: &[SExpr],
4096    repress_behaviour: CapsWordRepressBehaviour,
4097    s: &ParserState,
4098) -> Result<&'static KanataAction> {
4099    const ERR_STR: &str = "caps-word-custom expects 3 param: <timeout> <keys-to-capitalize> <extra-non-terminal-keys>";
4100    if ac_params.len() != 3 {
4101        bail!("{ERR_STR}\nFound {} params instead of 3", ac_params.len());
4102    }
4103    let timeout = parse_non_zero_u16(&ac_params[0], s, "timeout")?;
4104    Ok(s.a.sref(Action::Custom(
4105        s.a.sref(
4106            s.a.sref_slice(CustomAction::CapsWord(CapsWordCfg {
4107                repress_behaviour,
4108                keys_to_capitalize: s.a.sref_vec(
4109                    parse_key_list(&ac_params[1], s, "keys-to-capitalize")?
4110                        .into_iter()
4111                        .map(KeyCode::from)
4112                        .collect(),
4113                ),
4114                keys_nonterminal: s.a.sref_vec(
4115                    parse_key_list(&ac_params[2], s, "extra-non-terminal-keys")?
4116                        .into_iter()
4117                        .map(KeyCode::from)
4118                        .collect(),
4119                ),
4120                timeout,
4121            })),
4122        ),
4123    )))
4124}
4125
4126fn parse_macro_record_stop_truncate(
4127    ac_params: &[SExpr],
4128    s: &ParserState,
4129) -> Result<&'static KanataAction> {
4130    const ERR_STR: &str =
4131        "dynamic-macro-record-stop-truncate expects 1 param: <num-keys-to-truncate>";
4132    if ac_params.len() != 1 {
4133        bail!("{ERR_STR}\nFound {} params instead of 1", ac_params.len());
4134    }
4135    let num_to_truncate = parse_u16(&ac_params[0], s, "num-keys-to-truncate")?;
4136    Ok(s.a.sref(Action::Custom(s.a.sref(
4137        s.a.sref_slice(CustomAction::DynamicMacroRecordStop(num_to_truncate)),
4138    ))))
4139}
4140
4141fn parse_sequence_start(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
4142    const ERR_MSG: &str =
4143        "sequence expects one or two params: <timeout-override> <?input-mode-override>";
4144    if !matches!(ac_params.len(), 1 | 2) {
4145        bail!("{ERR_MSG}\nfound {} items", ac_params.len());
4146    }
4147    let timeout = parse_non_zero_u16(&ac_params[0], s, "timeout-override")?;
4148    let input_mode = if ac_params.len() > 1 {
4149        if let Some(Ok(input_mode)) = ac_params[1]
4150            .atom(s.vars())
4151            .map(SequenceInputMode::try_from_str)
4152        {
4153            input_mode
4154        } else {
4155            bail_expr!(&ac_params[1], "{ERR_MSG}\n{}", SequenceInputMode::err_msg());
4156        }
4157    } else {
4158        s.default_sequence_input_mode
4159    };
4160    Ok(s.a.sref(Action::Custom(s.a.sref(
4161        s.a.sref_slice(CustomAction::SequenceLeader(timeout, input_mode)),
4162    ))))
4163}
4164
4165fn parse_sequence_noerase(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
4166    const ERR_MSG: &str = "sequence-noerase expects one: <noerase-count>";
4167    if ac_params.len() != 1 {
4168        bail!("{ERR_MSG}\nfound {} items", ac_params.len());
4169    }
4170    let count = parse_non_zero_u16(&ac_params[0], s, "noerase-count")?;
4171    Ok(s.a.sref(Action::Custom(
4172        s.a.sref(s.a.sref_slice(CustomAction::SequenceNoerase(count))),
4173    )))
4174}
4175
4176fn parse_unmod(
4177    unmod_type: &str,
4178    ac_params: &[SExpr],
4179    s: &ParserState,
4180) -> Result<&'static KanataAction> {
4181    const ERR_MSG: &str = "expects expects at least one key name";
4182    if ac_params.is_empty() {
4183        bail!("{unmod_type} {ERR_MSG}\nfound {} items", ac_params.len());
4184    }
4185
4186    let mut mods = UnmodMods::all();
4187    let mut params = ac_params;
4188    // Parse the optional first-list that specifies the mod keys to use.
4189    if let Some(mod_list) = ac_params[0].list(s.vars()) {
4190        if unmod_type != UNMOD {
4191            bail_expr!(
4192                &ac_params[0],
4193                "{unmod_type} only expects key names but found a list"
4194            );
4195        }
4196        mods = mod_list
4197            .iter()
4198            .try_fold(UnmodMods::empty(), |mod_flags, mod_key| {
4199                let flag = mod_key
4200                    .atom(s.vars())
4201                    .and_then(str_to_oscode)
4202                    .and_then(|osc| match osc {
4203                        OsCode::KEY_LEFTSHIFT => Some(UnmodMods::LSft),
4204                        OsCode::KEY_RIGHTSHIFT => Some(UnmodMods::RSft),
4205                        OsCode::KEY_LEFTCTRL => Some(UnmodMods::LCtl),
4206                        OsCode::KEY_RIGHTCTRL => Some(UnmodMods::RCtl),
4207                        OsCode::KEY_LEFTMETA => Some(UnmodMods::LMet),
4208                        OsCode::KEY_RIGHTMETA => Some(UnmodMods::RMet),
4209                        OsCode::KEY_LEFTALT => Some(UnmodMods::LAlt),
4210                        OsCode::KEY_RIGHTALT => Some(UnmodMods::RAlt),
4211                        _ => None,
4212                    })
4213                    .ok_or_else(|| {
4214                        anyhow_expr!(
4215                            mod_key,
4216                            "{UNMOD} expects modifier key names within the modifier list."
4217                        )
4218                    })?;
4219                if !(mod_flags & flag).is_empty() {
4220                    bail_expr!(
4221                        mod_key,
4222                        "Duplicate key name in modifier key list is not allowed."
4223                    );
4224                }
4225                Ok::<_, ParseError>(mod_flags | flag)
4226            })?;
4227        if mods.is_empty() {
4228            bail_expr!(&ac_params[0], "an empty modifier key list is invalid");
4229        }
4230        if ac_params[1..].is_empty() {
4231            bail!("at least one key is required after the modifier key list");
4232        }
4233        params = &ac_params[1..];
4234    }
4235
4236    let keys: Vec<KeyCode> = params.iter().try_fold(Vec::new(), |mut keys, param| {
4237        keys.push(
4238            param
4239                .atom(s.vars())
4240                .and_then(str_to_oscode)
4241                .ok_or_else(|| {
4242                    anyhow_expr!(
4243                        &ac_params[0],
4244                        "{unmod_type} {ERR_MSG}\nfound invalid key name"
4245                    )
4246                })?
4247                .into(),
4248        );
4249        Ok::<_, ParseError>(keys)
4250    })?;
4251    let keys = keys.into_boxed_slice();
4252    match unmod_type {
4253        UNMOD => Ok(s.a.sref(Action::Custom(
4254            s.a.sref(s.a.sref_slice(CustomAction::Unmodded { keys, mods })),
4255        ))),
4256        UNSHIFT => Ok(s.a.sref(Action::Custom(
4257            s.a.sref(s.a.sref_slice(CustomAction::Unshifted { keys })),
4258        ))),
4259        _ => panic!("Unknown unmod type {unmod_type}"),
4260    }
4261}