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