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