Skip to main content

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