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        _ => {}
1695    };
1696    if let Some(oscode) = str_to_oscode(ac) {
1697        if matches!(ac, "comp" | "cmp") {
1698            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");
1699        }
1700        return Ok(s.a.sref(k(oscode.into())));
1701    }
1702    if let Some(alias) = ac.strip_prefix('@') {
1703        return match s.aliases.get(alias) {
1704            Some(ac) => {
1705                #[cfg(feature = "lsp")]
1706                s.lsp_hints
1707                    .borrow_mut()
1708                    .reference_locations
1709                    .alias
1710                    .push(alias, ac_span.span.clone());
1711                Ok(*ac)
1712            }
1713            None => match s.pctx.is_within_defvirtualkeys {
1714                true => bail_span!(
1715                    ac_span,
1716                    "Aliases are not usable within defvirtualkeys. You may use vars or templates.",
1717                ),
1718                false => bail_span!(
1719                    ac_span,
1720                    "Referenced unknown alias {}. Note that order of declarations matter.",
1721                    alias
1722                ),
1723            },
1724        };
1725    }
1726    if let Some(unisym) = ac.strip_prefix('🔣') {
1727        // TODO: when unicode accepts multiple chars, change this to feed the whole string, not just the first char
1728        return custom(
1729            CustomAction::Unicode(unisym.chars().next().expect("1 char")),
1730            &s.a,
1731        );
1732    }
1733    // Parse a sequence like `C-S-v` or `C-A-del`
1734    let (mut keys, unparsed_str) = parse_mod_prefix(ac)?;
1735    keys.push(
1736        str_to_oscode(unparsed_str)
1737            .ok_or_else(|| {
1738                // check aliases
1739                if s.aliases.contains_key(ac) {
1740                    anyhow!("Unknown key/action: {ac}. If you meant to use an alias, prefix it with '@' symbol: @{ac}")
1741                } else if s.vars.contains_key(ac) {
1742                    anyhow!("Unknown key/action: {ac}. If you meant to use a variable, prefix it with '$' symbol: ${ac}")
1743                } else {
1744                    anyhow!("Unknown key/action: {ac}")
1745                }
1746            })?
1747            .into(),
1748    );
1749    if keys.contains(&KEY_OVERLAP) {
1750        bail!("O- is only valid in sequences for lists of keys");
1751    }
1752    Ok(s.a.sref(Action::MultipleKeyCodes(s.a.sref(s.a.sref_vec(keys)))))
1753}
1754
1755/// Parse a `kanata_keyberon::action::Action` from a `SExpr::List`.
1756fn parse_action_list(ac: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
1757    if ac.is_empty() {
1758        return Ok(s.a.sref(Action::NoOp));
1759    }
1760    let ac_type = match &ac[0] {
1761        SExpr::Atom(a) => &a.t,
1762        _ => bail!("All list actions must start with string and not a list"),
1763    };
1764    if !is_list_action(ac_type) {
1765        bail_expr!(&ac[0], "Unknown action type: {ac_type}");
1766    }
1767    match ac_type.as_str() {
1768        LAYER_SWITCH => parse_layer_base(&ac[1..], s),
1769        LAYER_TOGGLE | LAYER_WHILE_HELD => parse_layer_toggle(&ac[1..], s),
1770        TAP_HOLD => parse_tap_hold(&ac[1..], s, HoldTapConfig::Default),
1771        TAP_HOLD_PRESS | TAP_HOLD_PRESS_A => {
1772            parse_tap_hold(&ac[1..], s, HoldTapConfig::HoldOnOtherKeyPress)
1773        }
1774        TAP_HOLD_RELEASE | TAP_HOLD_RELEASE_A => {
1775            parse_tap_hold(&ac[1..], s, HoldTapConfig::PermissiveHold)
1776        }
1777        TAP_HOLD_PRESS_TIMEOUT | TAP_HOLD_PRESS_TIMEOUT_A => {
1778            parse_tap_hold_timeout(&ac[1..], s, HoldTapConfig::HoldOnOtherKeyPress)
1779        }
1780        TAP_HOLD_RELEASE_TIMEOUT | TAP_HOLD_RELEASE_TIMEOUT_A => {
1781            parse_tap_hold_timeout(&ac[1..], s, HoldTapConfig::PermissiveHold)
1782        }
1783        TAP_HOLD_RELEASE_KEYS | TAP_HOLD_RELEASE_KEYS_A => {
1784            parse_tap_hold_keys(&ac[1..], s, "release", custom_tap_hold_release)
1785        }
1786        TAP_HOLD_EXCEPT_KEYS | TAP_HOLD_EXCEPT_KEYS_A => {
1787            parse_tap_hold_keys(&ac[1..], s, "except", custom_tap_hold_except)
1788        }
1789        MULTI => parse_multi(&ac[1..], s),
1790        MACRO => parse_macro(&ac[1..], s, RepeatMacro::No),
1791        MACRO_REPEAT | MACRO_REPEAT_A => parse_macro(&ac[1..], s, RepeatMacro::Yes),
1792        MACRO_RELEASE_CANCEL | MACRO_RELEASE_CANCEL_A => {
1793            parse_macro_release_cancel(&ac[1..], s, RepeatMacro::No)
1794        }
1795        MACRO_REPEAT_RELEASE_CANCEL | MACRO_REPEAT_RELEASE_CANCEL_A => {
1796            parse_macro_release_cancel(&ac[1..], s, RepeatMacro::Yes)
1797        }
1798        MACRO_CANCEL_ON_NEXT_PRESS => {
1799            parse_macro_cancel_on_next_press(&ac[1..], s, RepeatMacro::No)
1800        }
1801        MACRO_REPEAT_CANCEL_ON_NEXT_PRESS => {
1802            parse_macro_cancel_on_next_press(&ac[1..], s, RepeatMacro::Yes)
1803        }
1804        MACRO_CANCEL_ON_NEXT_PRESS_CANCEL_ON_RELEASE => {
1805            parse_macro_cancel_on_next_press_cancel_on_release(&ac[1..], s, RepeatMacro::No)
1806        }
1807        MACRO_REPEAT_CANCEL_ON_NEXT_PRESS_CANCEL_ON_RELEASE => {
1808            parse_macro_cancel_on_next_press_cancel_on_release(&ac[1..], s, RepeatMacro::Yes)
1809        }
1810        UNICODE | SYM => parse_unicode(&ac[1..], s),
1811        ONE_SHOT | ONE_SHOT_PRESS | ONE_SHOT_PRESS_A => {
1812            parse_one_shot(&ac[1..], s, OneShotEndConfig::EndOnFirstPress)
1813        }
1814        ONE_SHOT_RELEASE | ONE_SHOT_RELEASE_A => {
1815            parse_one_shot(&ac[1..], s, OneShotEndConfig::EndOnFirstRelease)
1816        }
1817        ONE_SHOT_PRESS_PCANCEL | ONE_SHOT_PRESS_PCANCEL_A => {
1818            parse_one_shot(&ac[1..], s, OneShotEndConfig::EndOnFirstPressOrRepress)
1819        }
1820        ONE_SHOT_RELEASE_PCANCEL | ONE_SHOT_RELEASE_PCANCEL_A => {
1821            parse_one_shot(&ac[1..], s, OneShotEndConfig::EndOnFirstReleaseOrRepress)
1822        }
1823        ONE_SHOT_PAUSE_PROCESSING => parse_one_shot_pause_processing(&ac[1..], s),
1824        TAP_DANCE => parse_tap_dance(&ac[1..], s, TapDanceConfig::Lazy),
1825        TAP_DANCE_EAGER => parse_tap_dance(&ac[1..], s, TapDanceConfig::Eager),
1826        CHORD => parse_chord(&ac[1..], s),
1827        RELEASE_KEY | RELEASE_KEY_A => parse_release_key(&ac[1..], s),
1828        RELEASE_LAYER | RELEASE_LAYER_A => parse_release_layer(&ac[1..], s),
1829        ON_PRESS_FAKEKEY | ON_PRESS_FAKEKEY_A => parse_on_press_fake_key_op(&ac[1..], s),
1830        ON_RELEASE_FAKEKEY | ON_RELEASE_FAKEKEY_A => parse_on_release_fake_key_op(&ac[1..], s),
1831        ON_PRESS_DELAY | ON_PRESS_FAKEKEY_DELAY | ON_PRESS_FAKEKEY_DELAY_A => {
1832            parse_fake_key_delay(&ac[1..], s)
1833        }
1834        ON_RELEASE_DELAY | ON_RELEASE_FAKEKEY_DELAY | ON_RELEASE_FAKEKEY_DELAY_A => {
1835            parse_on_release_fake_key_delay(&ac[1..], s)
1836        }
1837        ON_IDLE_FAKEKEY => parse_on_idle_fakekey(&ac[1..], s),
1838        ON_PRESS | ON_PRESS_A => parse_on_press(&ac[1..], s),
1839        ON_RELEASE | ON_RELEASE_A => parse_on_release(&ac[1..], s),
1840        ON_IDLE => parse_on_idle(&ac[1..], s),
1841        HOLD_FOR_DURATION => parse_hold_for_duration(&ac[1..], s),
1842        MWHEEL_UP | MWHEEL_UP_A => parse_mwheel(&ac[1..], MWheelDirection::Up, s),
1843        MWHEEL_DOWN | MWHEEL_DOWN_A => parse_mwheel(&ac[1..], MWheelDirection::Down, s),
1844        MWHEEL_LEFT | MWHEEL_LEFT_A => parse_mwheel(&ac[1..], MWheelDirection::Left, s),
1845        MWHEEL_RIGHT | MWHEEL_RIGHT_A => parse_mwheel(&ac[1..], MWheelDirection::Right, s),
1846        MOVEMOUSE_UP | MOVEMOUSE_UP_A => parse_move_mouse(&ac[1..], MoveDirection::Up, s),
1847        MOVEMOUSE_DOWN | MOVEMOUSE_DOWN_A => parse_move_mouse(&ac[1..], MoveDirection::Down, s),
1848        MOVEMOUSE_LEFT | MOVEMOUSE_LEFT_A => parse_move_mouse(&ac[1..], MoveDirection::Left, s),
1849        MOVEMOUSE_RIGHT | MOVEMOUSE_RIGHT_A => parse_move_mouse(&ac[1..], MoveDirection::Right, s),
1850        MOVEMOUSE_ACCEL_UP | MOVEMOUSE_ACCEL_UP_A => {
1851            parse_move_mouse_accel(&ac[1..], MoveDirection::Up, s)
1852        }
1853        MOVEMOUSE_ACCEL_DOWN | MOVEMOUSE_ACCEL_DOWN_A => {
1854            parse_move_mouse_accel(&ac[1..], MoveDirection::Down, s)
1855        }
1856        MOVEMOUSE_ACCEL_LEFT | MOVEMOUSE_ACCEL_LEFT_A => {
1857            parse_move_mouse_accel(&ac[1..], MoveDirection::Left, s)
1858        }
1859        MOVEMOUSE_ACCEL_RIGHT | MOVEMOUSE_ACCEL_RIGHT_A => {
1860            parse_move_mouse_accel(&ac[1..], MoveDirection::Right, s)
1861        }
1862        MOVEMOUSE_SPEED | MOVEMOUSE_SPEED_A => parse_move_mouse_speed(&ac[1..], s),
1863        SETMOUSE | SETMOUSE_A => parse_set_mouse(&ac[1..], s),
1864        DYNAMIC_MACRO_RECORD => parse_dynamic_macro_record(&ac[1..], s),
1865        DYNAMIC_MACRO_PLAY => parse_dynamic_macro_play(&ac[1..], s),
1866        ARBITRARY_CODE => parse_arbitrary_code(&ac[1..], s),
1867        CMD => parse_cmd(&ac[1..], s, CmdType::Standard),
1868        CMD_OUTPUT_KEYS => parse_cmd(&ac[1..], s, CmdType::OutputKeys),
1869        CMD_LOG => parse_cmd_log(&ac[1..], s),
1870        PUSH_MESSAGE => parse_push_message(&ac[1..], s),
1871        FORK => parse_fork(&ac[1..], s),
1872        CAPS_WORD | CAPS_WORD_A => {
1873            parse_caps_word(&ac[1..], CapsWordRepressBehaviour::Overwrite, s)
1874        }
1875        CAPS_WORD_CUSTOM | CAPS_WORD_CUSTOM_A => {
1876            parse_caps_word_custom(&ac[1..], CapsWordRepressBehaviour::Overwrite, s)
1877        }
1878        CAPS_WORD_TOGGLE | CAPS_WORD_TOGGLE_A => {
1879            parse_caps_word(&ac[1..], CapsWordRepressBehaviour::Toggle, s)
1880        }
1881        CAPS_WORD_CUSTOM_TOGGLE | CAPS_WORD_CUSTOM_TOGGLE_A => {
1882            parse_caps_word_custom(&ac[1..], CapsWordRepressBehaviour::Toggle, s)
1883        }
1884        DYNAMIC_MACRO_RECORD_STOP_TRUNCATE => parse_macro_record_stop_truncate(&ac[1..], s),
1885        SWITCH => parse_switch(&ac[1..], s),
1886        SEQUENCE => parse_sequence_start(&ac[1..], s),
1887        SEQUENCE_NOERASE => parse_sequence_noerase(&ac[1..], s),
1888        UNMOD => parse_unmod(UNMOD, &ac[1..], s),
1889        UNSHIFT | UNSHIFT_A => parse_unmod(UNSHIFT, &ac[1..], s),
1890        LIVE_RELOAD_NUM => parse_live_reload_num(&ac[1..], s),
1891        LIVE_RELOAD_FILE => parse_live_reload_file(&ac[1..], s),
1892        CLIPBOARD_SET => parse_clipboard_set(&ac[1..], s),
1893        CLIPBOARD_CMD_SET => parse_cmd(&ac[1..], s, CmdType::ClipboardSet),
1894        CLIPBOARD_SAVE => parse_clipboard_save(&ac[1..], s),
1895        CLIPBOARD_RESTORE => parse_clipboard_restore(&ac[1..], s),
1896        CLIPBOARD_SAVE_SET => parse_clipboard_save_set(&ac[1..], s),
1897        CLIPBOARD_SAVE_CMD_SET => parse_cmd(&ac[1..], s, CmdType::ClipboardSaveSet),
1898        CLIPBOARD_SAVE_SWAP => parse_clipboard_save_swap(&ac[1..], s),
1899        _ => unreachable!(),
1900    }
1901}
1902
1903fn parse_layer_base(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
1904    let idx = layer_idx(ac_params, &s.layer_idxs, s)?;
1905    set_layer_change_lsp_hint(&ac_params[0], &mut s.lsp_hints.borrow_mut());
1906    Ok(s.a.sref(Action::DefaultLayer(idx)))
1907}
1908
1909fn parse_layer_toggle(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
1910    let idx = layer_idx(ac_params, &s.layer_idxs, s)?;
1911    set_layer_change_lsp_hint(&ac_params[0], &mut s.lsp_hints.borrow_mut());
1912    Ok(s.a.sref(Action::Layer(idx)))
1913}
1914
1915#[allow(unused_variables)]
1916fn set_layer_change_lsp_hint(layer_name_expr: &SExpr, lsp_hints: &mut LspHints) {
1917    #[cfg(feature = "lsp")]
1918    {
1919        let layer_name_atom = match layer_name_expr {
1920            SExpr::Atom(x) => x,
1921            SExpr::List(_) => unreachable!("checked in layer_idx"),
1922        };
1923        lsp_hints
1924            .reference_locations
1925            .layer
1926            .push_from_atom(layer_name_atom);
1927    }
1928}
1929
1930fn layer_idx(ac_params: &[SExpr], layers: &LayerIndexes, s: &ParserState) -> Result<usize> {
1931    if ac_params.len() != 1 {
1932        bail!(
1933            "Layer actions expect one item: the layer name, found {} items",
1934            ac_params.len()
1935        )
1936    }
1937    let layer_name = ac_params[0]
1938        .atom(s.vars())
1939        .ok_or_else(|| anyhow_expr!(&ac_params[0], "layer name should be a string not a list",))?;
1940    match layers.get(layer_name) {
1941        Some(i) => Ok(*i),
1942        None => err_expr!(
1943            &ac_params[0],
1944            "layer name is not declared in any deflayer: {layer_name}"
1945        ),
1946    }
1947}
1948
1949fn parse_tap_hold(
1950    ac_params: &[SExpr],
1951    s: &ParserState,
1952    config: HoldTapConfig<'static>,
1953) -> Result<&'static KanataAction> {
1954    if ac_params.len() != 4 {
1955        bail!(
1956            r"tap-hold expects 4 items after it, got {}.
1957Params in order:
1958<tap-timeout> <hold-timeout> <tap-action> <hold-action>",
1959            ac_params.len(),
1960        )
1961    }
1962    let tap_timeout = parse_u16(&ac_params[0], s, "tap timeout")?;
1963    let hold_timeout = parse_non_zero_u16(&ac_params[1], s, "hold timeout")?;
1964    let tap_action = parse_action(&ac_params[2], s)?;
1965    let hold_action = parse_action(&ac_params[3], s)?;
1966    if matches!(tap_action, Action::HoldTap { .. }) {
1967        bail!("tap-hold does not work in the tap-action of tap-hold")
1968    }
1969    Ok(s.a.sref(Action::HoldTap(s.a.sref(HoldTapAction {
1970        config,
1971        tap_hold_interval: tap_timeout,
1972        timeout: hold_timeout,
1973        tap: *tap_action,
1974        hold: *hold_action,
1975        timeout_action: *hold_action,
1976    }))))
1977}
1978
1979fn parse_tap_hold_timeout(
1980    ac_params: &[SExpr],
1981    s: &ParserState,
1982    config: HoldTapConfig<'static>,
1983) -> Result<&'static KanataAction> {
1984    if ac_params.len() != 5 {
1985        bail!(
1986            r"tap-hold-(press|release)-timeout expects 5 items after it, got {}.
1987Params in order:
1988<tap-timeout> <hold-timeout> <tap-action> <hold-action> <timeout-action>",
1989            ac_params.len(),
1990        )
1991    }
1992    let tap_timeout = parse_u16(&ac_params[0], s, "tap timeout")?;
1993    let hold_timeout = parse_non_zero_u16(&ac_params[1], s, "hold timeout")?;
1994    let tap_action = parse_action(&ac_params[2], s)?;
1995    let hold_action = parse_action(&ac_params[3], s)?;
1996    let timeout_action = parse_action(&ac_params[4], s)?;
1997    if matches!(tap_action, Action::HoldTap { .. }) {
1998        bail!("tap-hold does not work in the tap-action of tap-hold")
1999    }
2000    Ok(s.a.sref(Action::HoldTap(s.a.sref(HoldTapAction {
2001        config,
2002        tap_hold_interval: tap_timeout,
2003        timeout: hold_timeout,
2004        tap: *tap_action,
2005        hold: *hold_action,
2006        timeout_action: *timeout_action,
2007    }))))
2008}
2009
2010fn parse_tap_hold_keys(
2011    ac_params: &[SExpr],
2012    s: &ParserState,
2013    custom_name: &str,
2014    custom_func: TapHoldCustomFunc,
2015) -> Result<&'static KanataAction> {
2016    if ac_params.len() != 5 {
2017        bail!(
2018            r"tap-hold-{}-keys expects 5 items after it, got {}.
2019Params in order:
2020<tap-timeout> <hold-timeout> <tap-action> <hold-action> <tap-trigger-keys>",
2021            custom_name,
2022            ac_params.len(),
2023        )
2024    }
2025    let tap_timeout = parse_u16(&ac_params[0], s, "tap timeout")?;
2026    let hold_timeout = parse_non_zero_u16(&ac_params[1], s, "hold timeout")?;
2027    let tap_action = parse_action(&ac_params[2], s)?;
2028    let hold_action = parse_action(&ac_params[3], s)?;
2029    let tap_trigger_keys = parse_key_list(&ac_params[4], s, "tap-trigger-keys")?;
2030    if matches!(tap_action, Action::HoldTap { .. }) {
2031        bail!("tap-hold does not work in the tap-action of tap-hold")
2032    }
2033    Ok(s.a.sref(Action::HoldTap(s.a.sref(HoldTapAction {
2034        config: HoldTapConfig::Custom(custom_func(&tap_trigger_keys, &s.a)),
2035        tap_hold_interval: tap_timeout,
2036        timeout: hold_timeout,
2037        tap: *tap_action,
2038        hold: *hold_action,
2039        timeout_action: *hold_action,
2040    }))))
2041}
2042
2043fn parse_u8_with_range(expr: &SExpr, s: &ParserState, label: &str, min: u8, max: u8) -> Result<u8> {
2044    expr.atom(s.vars())
2045        .map(str::parse::<u8>)
2046        .and_then(|u| u.ok())
2047        .and_then(|u| {
2048            assert!(min <= max);
2049            if u >= min && u <= max {
2050                Some(u)
2051            } else {
2052                None
2053            }
2054        })
2055        .ok_or_else(|| anyhow_expr!(expr, "{label} must be {min}-{max}"))
2056}
2057
2058fn parse_u16(expr: &SExpr, s: &ParserState, label: &str) -> Result<u16> {
2059    expr.atom(s.vars())
2060        .map(str::parse::<u16>)
2061        .and_then(|u| u.ok())
2062        .ok_or_else(|| anyhow_expr!(expr, "{label} must be 0-65535"))
2063}
2064
2065fn parse_non_zero_u16(expr: &SExpr, s: &ParserState, label: &str) -> Result<u16> {
2066    expr.atom(s.vars())
2067        .map(str::parse::<u16>)
2068        .and_then(|u| match u {
2069            Ok(u @ 1..) => Some(u),
2070            _ => None,
2071        })
2072        .ok_or_else(|| anyhow_expr!(expr, "{label} must be 1-65535"))
2073}
2074
2075fn parse_key_list(expr: &SExpr, s: &ParserState, label: &str) -> Result<Vec<OsCode>> {
2076    expr.list(s.vars())
2077        .map(|keys| {
2078            keys.iter().try_fold(vec![], |mut keys, key| {
2079                key.atom(s.vars())
2080                    .map(|a| -> Result<()> {
2081                        keys.push(str_to_oscode(a).ok_or_else(|| {
2082                            anyhow_expr!(key, "string of a known key is expected")
2083                        })?);
2084                        Ok(())
2085                    })
2086                    .ok_or_else(|| {
2087                        anyhow_expr!(key, "string of a known key is expected, found list instead")
2088                    })??;
2089                Ok(keys)
2090            })
2091        })
2092        .ok_or_else(|| anyhow_expr!(expr, "{label} must be a list of keys"))?
2093}
2094
2095fn parse_multi(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2096    if ac_params.is_empty() {
2097        bail!("multi expects at least one item after it")
2098    }
2099    s.multi_action_nest_count
2100        .replace(s.multi_action_nest_count.get().saturating_add(1));
2101    let mut actions = Vec::new();
2102    let mut custom_actions: Vec<&'static CustomAction> = Vec::new();
2103    for expr in ac_params {
2104        let ac = parse_action(expr, s)?;
2105        match ac {
2106            Action::Custom(acs) => {
2107                for ac in acs.iter() {
2108                    custom_actions.push(ac);
2109                }
2110            }
2111            // Flatten multi actions
2112            Action::MultipleActions(acs) => {
2113                for ac in acs.iter() {
2114                    match ac {
2115                        Action::Custom(acs) => {
2116                            for ac in acs.iter() {
2117                                custom_actions.push(ac);
2118                            }
2119                        }
2120                        _ => actions.push(*ac),
2121                    }
2122                }
2123            }
2124            _ => actions.push(*ac),
2125        }
2126    }
2127
2128    if !custom_actions.is_empty() {
2129        actions.push(Action::Custom(s.a.sref(s.a.sref_vec(custom_actions))));
2130    }
2131
2132    if actions
2133        .iter()
2134        .filter(|ac| {
2135            matches!(
2136                ac,
2137                Action::TapDance(TapDance {
2138                    config: TapDanceConfig::Lazy,
2139                    ..
2140                }) | Action::HoldTap { .. }
2141                    | Action::Chords { .. }
2142            )
2143        })
2144        .count()
2145        > 1
2146    {
2147        bail!("Cannot combine multiple tap-hold/tap-dance/chord");
2148    }
2149
2150    s.multi_action_nest_count
2151        .replace(s.multi_action_nest_count.get().saturating_sub(1));
2152    Ok(s.a.sref(Action::MultipleActions(s.a.sref(s.a.sref_vec(actions)))))
2153}
2154
2155const 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";
2156enum RepeatMacro {
2157    Yes,
2158    No,
2159}
2160
2161fn parse_macro(
2162    ac_params: &[SExpr],
2163    s: &ParserState,
2164    repeat: RepeatMacro,
2165) -> Result<&'static KanataAction> {
2166    if ac_params.is_empty() {
2167        bail!("macro expects at least one item after it")
2168    }
2169    let mut all_events = Vec::with_capacity(256);
2170    let mut params_remainder = ac_params;
2171    while !params_remainder.is_empty() {
2172        let mut events;
2173        (events, params_remainder) = parse_macro_item(params_remainder, s)?;
2174        all_events.append(&mut events);
2175    }
2176    if all_events.iter().any(|e| match e {
2177        SequenceEvent::Tap(kc) | SequenceEvent::Press(kc) | SequenceEvent::Release(kc) => {
2178            *kc == KEY_OVERLAP
2179        }
2180        _ => false,
2181    }) {
2182        bail!("macro contains O- which is only valid within defseq")
2183    }
2184    all_events.push(SequenceEvent::Complete);
2185    all_events.shrink_to_fit();
2186    match repeat {
2187        RepeatMacro::No => Ok(s.a.sref(Action::Sequence {
2188            events: s.a.sref(s.a.sref(s.a.sref_vec(all_events))),
2189        })),
2190        RepeatMacro::Yes => Ok(s.a.sref(Action::RepeatableSequence {
2191            events: s.a.sref(s.a.sref(s.a.sref_vec(all_events))),
2192        })),
2193    }
2194}
2195
2196fn parse_macro_release_cancel(
2197    ac_params: &[SExpr],
2198    s: &ParserState,
2199    repeat: RepeatMacro,
2200) -> Result<&'static KanataAction> {
2201    let macro_action = parse_macro(ac_params, s, repeat)?;
2202    Ok(s.a.sref(Action::MultipleActions(s.a.sref(s.a.sref_vec(vec![
2203        *macro_action,
2204        Action::Custom(s.a.sref(s.a.sref_slice(CustomAction::CancelMacroOnRelease))),
2205    ])))))
2206}
2207
2208fn parse_macro_cancel_on_next_press(
2209    ac_params: &[SExpr],
2210    s: &ParserState,
2211    repeat: RepeatMacro,
2212) -> Result<&'static KanataAction> {
2213    let macro_action = parse_macro(ac_params, s, repeat)?;
2214    let macro_duration = match macro_action {
2215        Action::RepeatableSequence { events } | Action::Sequence { events } => {
2216            macro_sequence_event_total_duration(events)
2217        }
2218        _ => unreachable!("parse_macro should return sequence action"),
2219    };
2220    Ok(s.a.sref(Action::MultipleActions(s.a.sref(s.a.sref_vec(vec![
2221        *macro_action,
2222        Action::Custom(
2223            s.a.sref(s.a.sref_slice(CustomAction::CancelMacroOnNextPress(macro_duration))),
2224        ),
2225    ])))))
2226}
2227
2228fn parse_macro_cancel_on_next_press_cancel_on_release(
2229    ac_params: &[SExpr],
2230    s: &ParserState,
2231    repeat: RepeatMacro,
2232) -> Result<&'static KanataAction> {
2233    let macro_action = parse_macro(ac_params, s, repeat)?;
2234    let macro_duration = match macro_action {
2235        Action::RepeatableSequence { events } | Action::Sequence { events } => {
2236            macro_sequence_event_total_duration(events)
2237        }
2238        _ => unreachable!("parse_macro should return sequence action"),
2239    };
2240    Ok(s.a.sref(Action::MultipleActions(s.a.sref(s.a.sref_vec(vec![
2241        *macro_action,
2242        Action::Custom(s.a.sref(s.a.sref_vec(vec![
2243            &CustomAction::CancelMacroOnRelease,
2244            s.a.sref(CustomAction::CancelMacroOnNextPress(macro_duration)),
2245        ]))),
2246    ])))))
2247}
2248
2249fn macro_sequence_event_total_duration<T>(events: &[SequenceEvent<T>]) -> u32 {
2250    events.iter().fold(0, |duration, event| {
2251        duration.saturating_add(match event {
2252            SequenceEvent::Delay { duration: d } => *d,
2253            _ => 1,
2254        })
2255    })
2256}
2257
2258#[derive(PartialEq)]
2259enum MacroNumberParseMode {
2260    Delay,
2261    Action,
2262}
2263
2264#[allow(clippy::type_complexity)] // return type is not pub
2265fn parse_macro_item<'a>(
2266    acs: &'a [SExpr],
2267    s: &ParserState,
2268) -> Result<(
2269    Vec<SequenceEvent<'static, &'static &'static [&'static CustomAction]>>,
2270    &'a [SExpr],
2271)> {
2272    parse_macro_item_impl(acs, s, MacroNumberParseMode::Delay)
2273}
2274
2275#[allow(clippy::type_complexity)] // return type is not pub
2276fn parse_macro_item_impl<'a>(
2277    acs: &'a [SExpr],
2278    s: &ParserState,
2279    num_parse_mode: MacroNumberParseMode,
2280) -> Result<(
2281    Vec<SequenceEvent<'static, &'static &'static [&'static CustomAction]>>,
2282    &'a [SExpr],
2283)> {
2284    if num_parse_mode == MacroNumberParseMode::Delay {
2285        if let Some(a) = acs[0].atom(s.vars()) {
2286            match parse_non_zero_u16(&acs[0], s, "delay") {
2287                Ok(duration) => {
2288                    let duration = u32::from(duration);
2289                    return Ok((vec![SequenceEvent::Delay { duration }], &acs[1..]));
2290                }
2291                Err(e) => {
2292                    if a.chars().all(|c| c.is_ascii_digit()) {
2293                        return Err(e);
2294                    }
2295                }
2296            }
2297        }
2298    }
2299    match parse_action(&acs[0], s) {
2300        Ok(Action::KeyCode(kc)) => {
2301            // Should note that I tried `SequenceEvent::Tap` initially but it seems to be buggy
2302            // so I changed the code to use individual press and release. The SequenceEvent
2303            // code is from a PR that (at the time of this writing) hasn't yet been merged into
2304            // keyberon master and doesn't have tests written for it yet. This seems to work as
2305            // expected right now though.
2306            Ok((
2307                vec![SequenceEvent::Press(*kc), SequenceEvent::Release(*kc)],
2308                &acs[1..],
2309            ))
2310        }
2311        Ok(Action::MultipleKeyCodes(kcs)) => {
2312            // chord - press in order then release in the reverse order
2313            let mut events = vec![];
2314            for kc in kcs.iter() {
2315                events.push(SequenceEvent::Press(*kc));
2316            }
2317            for kc in kcs.iter().rev() {
2318                events.push(SequenceEvent::Release(*kc));
2319            }
2320            Ok((events, &acs[1..]))
2321        }
2322        Ok(Action::Custom(custom)) => Ok((vec![SequenceEvent::Custom(custom)], &acs[1..])),
2323        Ok(_) => bail_expr!(&acs[0], "{MACRO_ERR}"),
2324        Err(e) => {
2325            if let Some(submacro) = acs[0].list(s.vars()) {
2326                // If it's just a list that's not parsable as a usable action, try parsing the
2327                // content.
2328                let mut submacro_remainder = submacro;
2329                let mut all_events = vec![];
2330                while !submacro_remainder.is_empty() {
2331                    let mut events;
2332                    (events, submacro_remainder) =
2333                        parse_macro_item(submacro_remainder, s).map_err(|_e| e.clone())?;
2334                    all_events.append(&mut events);
2335                }
2336                return Ok((all_events, &acs[1..]));
2337            }
2338
2339            let (held_mods, unparsed_str) =
2340                parse_mods_held_for_submacro(&acs[0], s).map_err(|mut err| {
2341                    if err.msg == MACRO_ERR {
2342                        err.msg = format!("{}\n{MACRO_ERR}", &e.msg);
2343                    }
2344                    err
2345                })?;
2346            let mut all_events = vec![];
2347
2348            // First, press all of the modifiers
2349            for kc in held_mods.iter().copied() {
2350                all_events.push(SequenceEvent::Press(kc));
2351            }
2352
2353            let mut rem_start = 1;
2354            let maybe_list_var = SExpr::Atom(Spanned::new(unparsed_str.into(), acs[0].span()));
2355            let submacro = match maybe_list_var.list(s.vars()) {
2356                Some(l) => l,
2357                None => {
2358                    // Ensure that the unparsed text is empty since otherwise it means there is
2359                    // invalid text there
2360                    if !unparsed_str.is_empty() {
2361                        bail_expr!(&acs[0], "{}\n{MACRO_ERR}", &e.msg)
2362                    }
2363                    // Check for a follow-up list
2364                    rem_start = 2;
2365                    if acs.len() < 2 {
2366                        bail_expr!(&acs[0], "{}\n{MACRO_ERR}", &e.msg)
2367                    }
2368                    acs[1]
2369                        .list(s.vars())
2370                        .ok_or_else(|| anyhow_expr!(&acs[1], "{MACRO_ERR}"))?
2371                }
2372            };
2373            let mut submacro_remainder = submacro;
2374            let mut events;
2375            while !submacro_remainder.is_empty() {
2376                (events, submacro_remainder) = parse_macro_item(submacro_remainder, s)?;
2377                all_events.append(&mut events);
2378            }
2379
2380            // Lastly, release modifiers
2381            for kc in held_mods.iter().copied() {
2382                all_events.push(SequenceEvent::Release(kc));
2383            }
2384
2385            Ok((all_events, &acs[rem_start..]))
2386        }
2387    }
2388}
2389
2390/// Parses mod keys like `C-S-`. Returns the `KeyCode`s for the modifiers parsed and the unparsed
2391/// text after any parsed modifier prefixes.
2392fn parse_mods_held_for_submacro<'a>(
2393    held_mods: &'a SExpr,
2394    s: &'a ParserState,
2395) -> Result<(Vec<KeyCode>, &'a str)> {
2396    let mods = held_mods
2397        .atom(s.vars())
2398        .ok_or_else(|| anyhow_expr!(held_mods, "{MACRO_ERR}"))?;
2399    let (mod_keys, unparsed_str) = parse_mod_prefix(mods)?;
2400    if mod_keys.is_empty() {
2401        bail_expr!(held_mods, "{MACRO_ERR}");
2402    }
2403    Ok((mod_keys, unparsed_str))
2404}
2405
2406static KEYMODI: &[(&str, KeyCode)] = &[
2407    ("S-", KeyCode::LShift),
2408    ("‹⇧", KeyCode::LShift),
2409    ("⇧›", KeyCode::RShift),
2410    ("RS-", KeyCode::RShift),
2411    ("C-", KeyCode::LCtrl),
2412    ("‹⎈", KeyCode::LCtrl),
2413    ("‹⌃", KeyCode::LCtrl),
2414    ("⎈›", KeyCode::RCtrl),
2415    ("⌃›", KeyCode::RCtrl),
2416    ("RC-", KeyCode::RCtrl),
2417    ("M-", KeyCode::LGui),
2418    ("‹◆", KeyCode::LGui),
2419    ("‹⌘", KeyCode::LGui),
2420    ("‹❖", KeyCode::LGui),
2421    ("◆›", KeyCode::RGui),
2422    ("⌘›", KeyCode::RGui),
2423    ("❖›", KeyCode::RGui),
2424    ("RM-", KeyCode::RGui),
2425    ("‹⎇", KeyCode::LAlt),
2426    ("A-", KeyCode::LAlt),
2427    ("‹⌥", KeyCode::LAlt),
2428    ("AG-", KeyCode::RAlt),
2429    ("RA-", KeyCode::RAlt),
2430    ("⎇›", KeyCode::RAlt),
2431    ("⌥›", KeyCode::RAlt),
2432    ("⎈", KeyCode::LCtrl), // Shorter indicators should be at the end to only get matched after
2433    // indicators with sides have had a chance
2434    ("⌥", KeyCode::LAlt),
2435    ("⎇", KeyCode::LAlt),
2436    ("◆", KeyCode::LGui),
2437    ("⌘", KeyCode::LGui),
2438    ("❖", KeyCode::LGui),
2439    ("O-", KEY_OVERLAP),
2440];
2441
2442/// Parses mod keys like `C-S-`. Returns the `KeyCode`s for the modifiers parsed and the unparsed
2443/// text after any parsed modifier prefixes.
2444pub fn parse_mod_prefix(mods: &str) -> Result<(Vec<KeyCode>, &str)> {
2445    let mut key_stack = Vec::new();
2446    let mut rem = mods;
2447    loop {
2448        let mut found_none = true;
2449        for (key_s, key_code) in KEYMODI {
2450            if let Some(rest) = rem.strip_prefix(key_s) {
2451                if key_stack.contains(key_code) {
2452                    bail!("Redundant \"{key_code:?}\" in {mods:?}");
2453                }
2454                key_stack.push(*key_code);
2455                rem = rest;
2456                found_none = false;
2457            }
2458        }
2459        if found_none {
2460            break;
2461        }
2462    }
2463    Ok((key_stack, rem))
2464}
2465
2466fn parse_unicode(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2467    const ERR_STR: &str = "unicode expects exactly one (not combos looking like one) unicode character as an argument";
2468    if ac_params.len() != 1 {
2469        bail!(ERR_STR)
2470    }
2471    ac_params[0]
2472        .atom(s.vars())
2473        .map(|a| {
2474            let a = a.trim_atom_quotes();
2475            if a.chars().count() != 1 {
2476                bail_expr!(&ac_params[0], "{ERR_STR}")
2477            }
2478            Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
2479                CustomAction::Unicode(a.chars().next().expect("1 char")),
2480            )))))
2481        })
2482        .ok_or_else(|| anyhow_expr!(&ac_params[0], "{ERR_STR}"))?
2483}
2484
2485enum CmdType {
2486    /// Execute command in own thread.
2487    Standard,
2488    /// Execute command synchronously and output stdout as macro-like SExpr.
2489    OutputKeys,
2490    /// Execute command and set clipboard to output. Clipboard content is passed as stdin to the
2491    /// command.
2492    ClipboardSet,
2493    /// Execute command and set clipboard save id to output.
2494    /// Clipboard save id content is passed as stdin to the command.
2495    ClipboardSaveSet,
2496}
2497
2498// Parse cmd, but there are 2 arguments before specifying normal log and error log
2499fn parse_cmd_log(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2500    const ERR_STR: &str =
2501        "cmd-log expects at least 3 strings, <log-level> <error-log-level> <cmd...>";
2502    if !s.is_cmd_enabled {
2503        bail!("cmd is not enabled for this kanata executable (did you use 'cmd_allowed' variants?), but is set in the configuration");
2504    }
2505    if ac_params.len() < 3 {
2506        bail!(ERR_STR);
2507    }
2508    let mut cmd = vec![];
2509    let log_level =
2510        if let Some(Ok(input_mode)) = ac_params[0].atom(s.vars()).map(LogLevel::try_from_str) {
2511            input_mode
2512        } else {
2513            bail_expr!(&ac_params[0], "{ERR_STR}\n{}", LogLevel::err_msg());
2514        };
2515    let error_log_level =
2516        if let Some(Ok(input_mode)) = ac_params[1].atom(s.vars()).map(LogLevel::try_from_str) {
2517            input_mode
2518        } else {
2519            bail_expr!(&ac_params[1], "{ERR_STR}\n{}", LogLevel::err_msg());
2520        };
2521    collect_strings(&ac_params[2..], &mut cmd, s);
2522    if cmd.is_empty() {
2523        bail!(ERR_STR);
2524    }
2525    Ok(s.a.sref(Action::Custom(s.a.sref(
2526        s.a.sref_slice(CustomAction::CmdLog(log_level, error_log_level, cmd)),
2527    ))))
2528}
2529
2530#[allow(unused_variables)]
2531fn parse_cmd(
2532    ac_params: &[SExpr],
2533    s: &ParserState,
2534    cmd_type: CmdType,
2535) -> Result<&'static KanataAction> {
2536    #[cfg(not(feature = "cmd"))]
2537    {
2538        bail!(
2539            "cmd is not enabled for this kanata executable. Use a cmd_allowed prebuilt executable or compile with the feature: cmd."
2540        );
2541    }
2542    #[cfg(feature = "cmd")]
2543    {
2544        if matches!(cmd_type, CmdType::ClipboardSaveSet) {
2545            const ERR_STR: &str = "expects a save ID and at least one string";
2546            if !s.is_cmd_enabled {
2547                bail!("To use cmd you must put in defcfg: danger-enable-cmd yes.");
2548            }
2549            if ac_params.len() < 2 {
2550                bail!("{CLIPBOARD_SAVE_CMD_SET} {ERR_STR}");
2551            }
2552            let mut cmd = vec![];
2553            let save_id = parse_u16(&ac_params[0], s, "clipboard save ID")?;
2554            collect_strings(&ac_params[1..], &mut cmd, s);
2555            if cmd.is_empty() {
2556                bail_expr!(&ac_params[1], "{CLIPBOARD_SAVE_CMD_SET} {ERR_STR}");
2557            }
2558            return Ok(s.a.sref(Action::Custom(
2559                s.a.sref(s.a.sref_slice(CustomAction::ClipboardSaveCmdSet(save_id, cmd))),
2560            )));
2561        }
2562
2563        const ERR_STR: &str = "cmd expects at least one string";
2564        if !s.is_cmd_enabled {
2565            bail!("To use cmd you must put in defcfg: danger-enable-cmd yes.");
2566        }
2567        let mut cmd = vec![];
2568        collect_strings(ac_params, &mut cmd, s);
2569        if cmd.is_empty() {
2570            bail!(ERR_STR);
2571        }
2572        Ok(s.a
2573            .sref(Action::Custom(s.a.sref(s.a.sref_slice(match cmd_type {
2574                CmdType::Standard => CustomAction::Cmd(cmd),
2575                CmdType::OutputKeys => CustomAction::CmdOutputKeys(cmd),
2576                CmdType::ClipboardSet => CustomAction::ClipboardCmdSet(cmd),
2577                CmdType::ClipboardSaveSet => unreachable!(),
2578            })))))
2579    }
2580}
2581
2582/// Recurse through all levels of list nesting and collect into a flat list of strings.
2583/// Recursion is DFS, which matches left-to-right reading of the strings as they appear,
2584/// if everything was on a single line.
2585fn collect_strings(params: &[SExpr], strings: &mut Vec<String>, s: &ParserState) {
2586    for param in params {
2587        if let Some(a) = param.atom(s.vars()) {
2588            strings.push(a.trim_atom_quotes().to_owned());
2589        } else {
2590            // unwrap: this must be a list, since it's not an atom.
2591            let l = param.list(s.vars()).unwrap();
2592            collect_strings(l, strings, s);
2593        }
2594    }
2595}
2596
2597#[test]
2598fn test_collect_strings() {
2599    let params = r#"(gah (squish "squash" (splish splosh) "bah mah") dah)"#;
2600    let params = sexpr::parse(params, "noexist").unwrap();
2601    let mut strings = vec![];
2602    collect_strings(&params[0].t, &mut strings, &ParserState::default());
2603    assert_eq!(
2604        &strings,
2605        &["gah", "squish", "squash", "splish", "splosh", "bah mah", "dah"]
2606    );
2607}
2608
2609fn parse_push_message(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2610    if ac_params.is_empty() {
2611        bail!(
2612             "{PUSH_MESSAGE} expects at least one item, an item can be a list or an atom, found 0, none"
2613        );
2614    }
2615    let message = to_simple_expr(ac_params, s);
2616    custom(CustomAction::PushMessage(message), &s.a)
2617}
2618
2619fn to_simple_expr(params: &[SExpr], s: &ParserState) -> Vec<SimpleSExpr> {
2620    let mut result: Vec<SimpleSExpr> = Vec::new();
2621    for param in params {
2622        if let Some(a) = param.atom(s.vars()) {
2623            result.push(SimpleSExpr::Atom(a.trim_atom_quotes().to_owned()));
2624        } else {
2625            // unwrap: this must be a list, since it's not an atom.
2626            let sexps = param.list(s.vars()).unwrap();
2627            let value = to_simple_expr(sexps, s);
2628            let list = SimpleSExpr::List(value);
2629            result.push(list);
2630        }
2631    }
2632    result
2633}
2634
2635#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2636pub enum SimpleSExpr {
2637    Atom(String),
2638    List(Vec<SimpleSExpr>),
2639}
2640
2641fn parse_one_shot(
2642    ac_params: &[SExpr],
2643    s: &ParserState,
2644    end_config: OneShotEndConfig,
2645) -> Result<&'static KanataAction> {
2646    const ERR_MSG: &str = "one-shot expects a timeout followed by a key or action";
2647    if ac_params.len() != 2 {
2648        bail!(ERR_MSG);
2649    }
2650
2651    let timeout = parse_non_zero_u16(&ac_params[0], s, "timeout")?;
2652    let action = parse_action(&ac_params[1], s)?;
2653    if !matches!(
2654        action,
2655        Action::Layer(..) | Action::KeyCode(..) | Action::MultipleKeyCodes(..)
2656    ) {
2657        bail!("one-shot is only allowed to contain layer-while-held, a keycode, or a chord");
2658    }
2659
2660    Ok(s.a.sref(Action::OneShot(s.a.sref(OneShot {
2661        timeout,
2662        action,
2663        end_config,
2664    }))))
2665}
2666
2667fn parse_one_shot_pause_processing(
2668    ac_params: &[SExpr],
2669    s: &ParserState,
2670) -> Result<&'static KanataAction> {
2671    const ERR_MSG: &str = "one-shot-pause-processing expects a time";
2672    if ac_params.len() != 1 {
2673        bail!(ERR_MSG);
2674    }
2675    let timeout = parse_non_zero_u16(&ac_params[0], s, "time (milliseconds)")?;
2676    Ok(s.a.sref(Action::OneShotIgnoreEventsTicks(timeout)))
2677}
2678
2679fn parse_tap_dance(
2680    ac_params: &[SExpr],
2681    s: &ParserState,
2682    config: TapDanceConfig,
2683) -> Result<&'static KanataAction> {
2684    const ERR_MSG: &str = "tap-dance expects a timeout (number) followed by a list of actions";
2685    if ac_params.len() != 2 {
2686        bail!(ERR_MSG);
2687    }
2688
2689    let timeout = parse_non_zero_u16(&ac_params[0], s, "timeout")?;
2690    let actions = ac_params[1]
2691        .list(s.vars())
2692        .map(|tap_dance_actions| -> Result<Vec<&'static KanataAction>> {
2693            let mut actions = Vec::new();
2694            for expr in tap_dance_actions {
2695                let ac = parse_action(expr, s)?;
2696                actions.push(ac);
2697            }
2698            Ok(actions)
2699        })
2700        .ok_or_else(|| anyhow_expr!(&ac_params[1], "{ERR_MSG}: expected a list"))??;
2701
2702    Ok(s.a.sref(Action::TapDance(s.a.sref(TapDance {
2703        timeout,
2704        actions: s.a.sref_vec(actions),
2705        config,
2706    }))))
2707}
2708
2709fn parse_chord(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2710    const ERR_MSG: &str = "Action chord expects a chords group name followed by an identifier";
2711    if ac_params.len() != 2 {
2712        bail!(ERR_MSG);
2713    }
2714
2715    let name = ac_params[0]
2716        .atom(s.vars())
2717        .ok_or_else(|| anyhow_expr!(&ac_params[0], "{ERR_MSG}"))?;
2718    let group = match s.chord_groups.get(name) {
2719        Some(t) => t,
2720        None => bail_expr!(&ac_params[0], "Referenced unknown chord group: {}.", name),
2721    };
2722    let chord_key_index = ac_params[1]
2723        .atom(s.vars())
2724        .map(|s| match group.keys.iter().position(|e| e == s) {
2725            Some(i) => Ok(i),
2726            None => err_expr!(
2727                &ac_params[1],
2728                r#"Identifier "{}" is not used in chord group "{}"."#,
2729                &s,
2730                name,
2731            ),
2732        })
2733        .ok_or_else(|| anyhow_expr!(&ac_params[0], "{ERR_MSG}"))??;
2734    let chord_keys: u128 = 1 << chord_key_index;
2735
2736    // We don't yet know at this point what the entire chords group will look like nor at which
2737    // coords this action will end up. So instead we store a dummy action which will be properly
2738    // resolved in `resolve_chord_groups`.
2739    Ok(s.a.sref(Action::Chords(s.a.sref(ChordsGroup {
2740        timeout: group.timeout,
2741        coords: s.a.sref_vec(vec![((0, group.id), chord_keys)]),
2742        chords: s.a.sref_vec(vec![]),
2743    }))))
2744}
2745
2746fn parse_release_key(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2747    const ERR_MSG: &str = "release-key expects exactly one keycode (e.g. lalt)";
2748    if ac_params.len() != 1 {
2749        bail!("{ERR_MSG}: found {} items", ac_params.len());
2750    }
2751    let ac = parse_action(&ac_params[0], s)?;
2752    match ac {
2753        Action::KeyCode(kc) => {
2754            Ok(s.a.sref(Action::ReleaseState(ReleasableState::KeyCode(*kc))))
2755        }
2756        _ => err_expr!(&ac_params[0], "{}", ERR_MSG),
2757    }
2758}
2759
2760fn parse_release_layer(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
2761    Ok(s.a
2762        .sref(Action::ReleaseState(ReleasableState::Layer(layer_idx(
2763            ac_params,
2764            &s.layer_idxs,
2765            s,
2766        )?))))
2767}
2768
2769fn create_defsrc_layer() -> [KanataAction; KEYS_IN_ROW] {
2770    let mut layer = [KanataAction::NoOp; KEYS_IN_ROW];
2771
2772    for (i, ac) in layer.iter_mut().enumerate() {
2773        *ac = OsCode::from_u16(i as u16)
2774            .map(|osc| Action::KeyCode(osc.into()))
2775            .unwrap_or(Action::NoOp);
2776    }
2777    // Ensure 0-index is no-op.
2778    layer[0] = KanataAction::NoOp;
2779    layer
2780}
2781
2782fn parse_chord_groups(exprs: &[&Spanned<Vec<SExpr>>], s: &mut ParserState) -> Result<()> {
2783    const MSG: &str = "Incorrect number of elements found in defchords.\nThere should be the group name, followed by timeout, followed by keys-action pairs";
2784    for expr in exprs {
2785        let mut subexprs = check_first_expr(expr.t.iter(), "defchords")?;
2786        let name = subexprs
2787            .next()
2788            .and_then(|e| e.atom(s.vars()))
2789            .ok_or_else(|| anyhow_span!(expr, "{MSG}"))?
2790            .to_owned();
2791        let timeout = match subexprs.next() {
2792            Some(e) => parse_non_zero_u16(e, s, "timeout")?,
2793            None => bail_span!(expr, "{MSG}"),
2794        };
2795        let id = match s.chord_groups.len().try_into() {
2796            Ok(id) => id,
2797            Err(_) => bail_span!(expr, "Maximum number of chord groups exceeded."),
2798        };
2799        let mut group = ChordGroup {
2800            id,
2801            name: name.clone(),
2802            keys: Vec::new(),
2803            coords: Vec::new(),
2804            chords: HashMap::default(),
2805            timeout,
2806        };
2807        // Read k-v pairs from the configuration
2808        while let Some(keys_expr) = subexprs.next() {
2809            let action = match subexprs.next() {
2810                Some(v) => v,
2811                None => bail_expr!(
2812                    keys_expr,
2813                    "Key list found without action - add an action for this chord"
2814                ),
2815            };
2816            let mut keys = keys_expr
2817                .list(s.vars())
2818                .map(|keys| {
2819                    keys.iter().map(|key| {
2820                        key.atom(s.vars()).ok_or_else(|| {
2821                            anyhow_expr!(
2822                                key,
2823                                "Chord keys cannot be lists. Invalid key name: {:?}",
2824                                key
2825                            )
2826                        })
2827                    })
2828                })
2829                .ok_or_else(|| anyhow_expr!(keys_expr, "Chord must be a list/set of keys"))?;
2830            let mask: u128 = keys.try_fold(0, |mask, key| {
2831                let key = key?;
2832                let index = match group.keys.iter().position(|k| k == key) {
2833                    Some(i) => i,
2834                    None => {
2835                        let i = group.keys.len();
2836                        if i + 1 > MAX_CHORD_KEYS {
2837                            bail_expr!(keys_expr, "Maximum number of keys in a chords group ({MAX_CHORD_KEYS}) exceeded - found {}", i + 1);
2838                        }
2839                        group.keys.push(key.to_owned());
2840                        i
2841                    }
2842                };
2843                Ok(mask | (1 << index))
2844            })?;
2845            if group.chords.insert(mask, action.clone()).is_some() {
2846                bail_expr!(keys_expr, "Duplicate chord in group {name}");
2847            }
2848        }
2849        if s.chord_groups.insert(name.to_owned(), group).is_some() {
2850            bail_span!(expr, "Duplicate chords group: {}", name);
2851        }
2852    }
2853    Ok(())
2854}
2855
2856fn resolve_chord_groups(layers: &mut IntermediateLayers, s: &ParserState) -> Result<()> {
2857    let mut chord_groups = s.chord_groups.values().cloned().collect::<Vec<_>>();
2858    chord_groups.sort_by_key(|group| group.id);
2859
2860    for layer in layers.iter() {
2861        for (i, row) in layer.iter().enumerate() {
2862            for (j, cell) in row.iter().enumerate() {
2863                find_chords_coords(&mut chord_groups, (i as u8, j as u16), cell);
2864            }
2865        }
2866    }
2867
2868    let chord_groups = chord_groups.into_iter().map(|group| {
2869        // Check that all keys in the chord group have been assigned to some coordinate
2870        for (key_index, key) in group.keys.iter().enumerate() {
2871            let key_mask = 1 << key_index;
2872            if !group.coords.iter().any(|(_, keys)| keys & key_mask != 0) {
2873                bail!("coord group `{0}` defines unused key `{1}`, did you forget to bind `(chord {0} {1})`?", group.name, key)
2874            }
2875        }
2876
2877        let chords = group.chords.iter().map(|(mask, action)| {
2878            Ok((*mask, parse_action(action, s)?))
2879        }).collect::<Result<Vec<_>>>()?;
2880
2881        Ok(s.a.sref(ChordsGroup {
2882            coords: s.a.sref_vec(group.coords),
2883            chords: s.a.sref_vec(chords),
2884            timeout: group.timeout,
2885        }))
2886    }).collect::<Result<Vec<_>>>()?;
2887
2888    for layer in layers.iter_mut() {
2889        for row in layer.iter_mut() {
2890            for cell in row.iter_mut() {
2891                if let Some(action) = fill_chords(&chord_groups, cell, s) {
2892                    *cell = action;
2893                }
2894            }
2895        }
2896    }
2897
2898    Ok(())
2899}
2900
2901fn find_chords_coords(chord_groups: &mut [ChordGroup], coord: (u8, u16), action: &KanataAction) {
2902    match action {
2903        Action::Chords(ChordsGroup { coords, .. }) => {
2904            for ((_, group_id), chord_keys) in coords.iter() {
2905                let group = &mut chord_groups[*group_id as usize];
2906                group.coords.push((coord, *chord_keys));
2907            }
2908        }
2909        Action::NoOp
2910        | Action::Trans
2911        | Action::Src
2912        | Action::Repeat
2913        | Action::KeyCode(_)
2914        | Action::MultipleKeyCodes(_)
2915        | Action::Layer(_)
2916        | Action::DefaultLayer(_)
2917        | Action::Sequence { .. }
2918        | Action::RepeatableSequence { .. }
2919        | Action::CancelSequences
2920        | Action::ReleaseState(_)
2921        | Action::OneShotIgnoreEventsTicks(_)
2922        | Action::Custom(_) => {}
2923        Action::HoldTap(HoldTapAction { tap, hold, .. }) => {
2924            find_chords_coords(chord_groups, coord, tap);
2925            find_chords_coords(chord_groups, coord, hold);
2926        }
2927        Action::OneShot(OneShot { action: ac, .. }) => {
2928            find_chords_coords(chord_groups, coord, ac);
2929        }
2930        Action::MultipleActions(actions) => {
2931            for ac in actions.iter() {
2932                find_chords_coords(chord_groups, coord, ac);
2933            }
2934        }
2935        Action::TapDance(TapDance { actions, .. }) => {
2936            for ac in actions.iter() {
2937                find_chords_coords(chord_groups, coord, ac);
2938            }
2939        }
2940        Action::Fork(ForkConfig { left, right, .. }) => {
2941            find_chords_coords(chord_groups, coord, left);
2942            find_chords_coords(chord_groups, coord, right);
2943        }
2944        Action::Switch(Switch { cases }) => {
2945            for case in cases.iter() {
2946                find_chords_coords(chord_groups, coord, case.1);
2947            }
2948        }
2949    }
2950}
2951
2952fn fill_chords(
2953    chord_groups: &[&'static ChordsGroup<&&[&CustomAction]>],
2954    action: &KanataAction,
2955    s: &ParserState,
2956) -> Option<KanataAction> {
2957    match action {
2958        Action::Chords(ChordsGroup { coords, .. }) => {
2959            let ((_, group_id), _) = coords
2960                .iter()
2961                .next()
2962                .expect("unresolved chords should have exactly one entry");
2963            Some(Action::Chords(chord_groups[*group_id as usize]))
2964        }
2965        Action::NoOp
2966        | Action::Trans
2967        | Action::Repeat
2968        | Action::Src
2969        | Action::KeyCode(_)
2970        | Action::MultipleKeyCodes(_)
2971        | Action::Layer(_)
2972        | Action::DefaultLayer(_)
2973        | Action::Sequence { .. }
2974        | Action::RepeatableSequence { .. }
2975        | Action::CancelSequences
2976        | Action::ReleaseState(_)
2977        | Action::OneShotIgnoreEventsTicks(_)
2978        | Action::Custom(_) => None,
2979        Action::HoldTap(&hta @ HoldTapAction { tap, hold, .. }) => {
2980            let new_tap = fill_chords(chord_groups, &tap, s);
2981            let new_hold = fill_chords(chord_groups, &hold, s);
2982            if new_tap.is_some() || new_hold.is_some() {
2983                Some(Action::HoldTap(s.a.sref(HoldTapAction {
2984                    hold: new_hold.unwrap_or(hold),
2985                    tap: new_tap.unwrap_or(tap),
2986                    ..hta
2987                })))
2988            } else {
2989                None
2990            }
2991        }
2992        Action::OneShot(&os @ OneShot { action: ac, .. }) => {
2993            fill_chords(chord_groups, ac, s).map(|ac| {
2994                Action::OneShot(s.a.sref(OneShot {
2995                    action: s.a.sref(ac),
2996                    ..os
2997                }))
2998            })
2999        }
3000        Action::MultipleActions(actions) => {
3001            let new_actions = actions
3002                .iter()
3003                .map(|ac| fill_chords(chord_groups, ac, s))
3004                .collect::<Vec<_>>();
3005            if new_actions.iter().any(|it| it.is_some()) {
3006                let new_actions = new_actions
3007                    .iter()
3008                    .zip(**actions)
3009                    .map(|(new_ac, ac)| new_ac.unwrap_or(*ac))
3010                    .collect::<Vec<_>>();
3011                Some(Action::MultipleActions(s.a.sref(s.a.sref_vec(new_actions))))
3012            } else {
3013                None
3014            }
3015        }
3016        Action::TapDance(&td @ TapDance { actions, .. }) => {
3017            let new_actions = actions
3018                .iter()
3019                .map(|ac| fill_chords(chord_groups, ac, s))
3020                .collect::<Vec<_>>();
3021            if new_actions.iter().any(|it| it.is_some()) {
3022                let new_actions = new_actions
3023                    .iter()
3024                    .zip(actions)
3025                    .map(|(new_ac, ac)| new_ac.map(|v| s.a.sref(v)).unwrap_or(*ac))
3026                    .collect::<Vec<_>>();
3027                Some(Action::TapDance(s.a.sref(TapDance {
3028                    actions: s.a.sref_vec(new_actions),
3029                    ..td
3030                })))
3031            } else {
3032                None
3033            }
3034        }
3035        Action::Fork(&fcfg @ ForkConfig { left, right, .. }) => {
3036            let new_left = fill_chords(chord_groups, &left, s);
3037            let new_right = fill_chords(chord_groups, &right, s);
3038            if new_left.is_some() || new_right.is_some() {
3039                Some(Action::Fork(s.a.sref(ForkConfig {
3040                    left: new_left.unwrap_or(left),
3041                    right: new_right.unwrap_or(right),
3042                    ..fcfg
3043                })))
3044            } else {
3045                None
3046            }
3047        }
3048        Action::Switch(Switch { cases }) => {
3049            let mut new_cases = vec![];
3050            for case in cases.iter() {
3051                new_cases.push((
3052                    case.0,
3053                    fill_chords(chord_groups, case.1, s)
3054                        .map(|ac| s.a.sref(ac))
3055                        .unwrap_or(case.1),
3056                    case.2,
3057                ));
3058            }
3059            Some(Action::Switch(s.a.sref(Switch {
3060                cases: s.a.sref_vec(new_cases),
3061            })))
3062        }
3063    }
3064}
3065
3066fn parse_fake_keys(exprs: &[&Vec<SExpr>], s: &mut ParserState) -> Result<()> {
3067    for expr in exprs {
3068        let mut subexprs = check_first_expr(expr.iter(), "deffakekeys")?;
3069        // Read k-v pairs from the configuration
3070        while let Some(key_name_expr) = subexprs.next() {
3071            let key_name = key_name_expr
3072                .atom(s.vars())
3073                .ok_or_else(|| anyhow_expr!(key_name_expr, "Fake key name must not be a list."))?
3074                .to_owned();
3075            let action = match subexprs.next() {
3076                Some(v) => v,
3077                None => bail_expr!(
3078                    key_name_expr,
3079                    "Fake key name has no action - you should add an action."
3080                ),
3081            };
3082            let action = parse_action(action, s)?;
3083            let idx = s.virtual_keys.len();
3084            log::trace!("inserting {key_name}->{idx}:{action:?}");
3085            if s.virtual_keys
3086                .insert(key_name.clone(), (idx, action))
3087                .is_some()
3088            {
3089                bail_expr!(key_name_expr, "Duplicate fake key: {}", key_name);
3090            }
3091            #[cfg(feature = "lsp")]
3092            s.lsp_hints
3093                .borrow_mut()
3094                .definition_locations
3095                .virtual_key
3096                .insert(key_name, key_name_expr.span());
3097        }
3098    }
3099    if s.virtual_keys.len() > KEYS_IN_ROW {
3100        bail!(
3101            "Maximum number of fake keys is {KEYS_IN_ROW}, found {}",
3102            s.virtual_keys.len()
3103        );
3104    }
3105    Ok(())
3106}
3107
3108fn parse_virtual_keys(exprs: &[&Vec<SExpr>], s: &mut ParserState) -> Result<()> {
3109    s.pctx.is_within_defvirtualkeys = true;
3110    for expr in exprs {
3111        let mut subexprs = check_first_expr(expr.iter(), "defvirtualkeys")?;
3112        // Read k-v pairs from the configuration
3113        while let Some(key_name_expr) = subexprs.next() {
3114            let key_name = key_name_expr
3115                .atom(s.vars())
3116                .ok_or_else(|| anyhow_expr!(key_name_expr, "Virtual key name must not be a list."))?
3117                .to_owned();
3118            let action = match subexprs.next() {
3119                Some(v) => v,
3120                None => bail_expr!(
3121                    key_name_expr,
3122                    "Virtual key name has no action - you must add an action."
3123                ),
3124            };
3125            let action = parse_action(action, s)?;
3126            let idx = s.virtual_keys.len();
3127            log::trace!("inserting {key_name}->{idx}:{action:?}");
3128            if s.virtual_keys
3129                .insert(key_name.clone(), (idx, action))
3130                .is_some()
3131            {
3132                bail_expr!(key_name_expr, "Duplicate virtual key: {}", key_name);
3133            };
3134            #[cfg(feature = "lsp")]
3135            s.lsp_hints
3136                .borrow_mut()
3137                .definition_locations
3138                .virtual_key
3139                .insert(key_name, key_name_expr.span());
3140        }
3141    }
3142    s.pctx.is_within_defvirtualkeys = false;
3143    if s.virtual_keys.len() > KEYS_IN_ROW {
3144        bail!(
3145            "Maximum number of virtual keys is {KEYS_IN_ROW}, found {}",
3146            s.virtual_keys.len()
3147        );
3148    }
3149    Ok(())
3150}
3151
3152fn parse_distance(expr: &SExpr, s: &ParserState, label: &str) -> Result<u16> {
3153    expr.atom(s.vars())
3154        .map(str::parse::<u16>)
3155        .and_then(|d| match d {
3156            Ok(dist @ 1..=30000) => Some(dist),
3157            _ => None,
3158        })
3159        .ok_or_else(|| anyhow_expr!(expr, "{label} must be 1-30000"))
3160}
3161
3162fn parse_mwheel(
3163    ac_params: &[SExpr],
3164    direction: MWheelDirection,
3165    s: &ParserState,
3166) -> Result<&'static KanataAction> {
3167    const ERR_MSG: &str = "mwheel expects 2 parameters: <interval (ms)> <distance>";
3168    if ac_params.len() != 2 {
3169        bail!("{ERR_MSG}, found {}", ac_params.len());
3170    }
3171    let interval = parse_non_zero_u16(&ac_params[0], s, "interval")?;
3172    let distance = parse_distance(&ac_params[1], s, "distance")?;
3173    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3174        CustomAction::MWheel {
3175            direction,
3176            interval,
3177            distance,
3178        },
3179    )))))
3180}
3181
3182fn parse_move_mouse(
3183    ac_params: &[SExpr],
3184    direction: MoveDirection,
3185    s: &ParserState,
3186) -> Result<&'static KanataAction> {
3187    const ERR_MSG: &str = "movemouse expects 2 parameters: <interval (ms)> <distance (px)>";
3188    if ac_params.len() != 2 {
3189        bail!("{ERR_MSG}, found {}", ac_params.len());
3190    }
3191    let interval = parse_non_zero_u16(&ac_params[0], s, "interval")?;
3192    let distance = parse_distance(&ac_params[1], s, "distance")?;
3193    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3194        CustomAction::MoveMouse {
3195            direction,
3196            interval,
3197            distance,
3198        },
3199    )))))
3200}
3201
3202fn parse_move_mouse_accel(
3203    ac_params: &[SExpr],
3204    direction: MoveDirection,
3205    s: &ParserState,
3206) -> Result<&'static KanataAction> {
3207    if ac_params.len() != 4 {
3208        bail!("movemouse-accel expects four parameters, found {}\n<interval (ms)> <acceleration time (ms)> <min_distance> <max_distance>", ac_params.len());
3209    }
3210    let interval = parse_non_zero_u16(&ac_params[0], s, "interval")?;
3211    let accel_time = parse_non_zero_u16(&ac_params[1], s, "acceleration time")?;
3212    let min_distance = parse_distance(&ac_params[2], s, "min distance")?;
3213    let max_distance = parse_distance(&ac_params[3], s, "max distance")?;
3214    if min_distance > max_distance {
3215        bail!("min distance should be less than max distance")
3216    }
3217    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3218        CustomAction::MoveMouseAccel {
3219            direction,
3220            interval,
3221            accel_time,
3222            min_distance,
3223            max_distance,
3224        },
3225    )))))
3226}
3227
3228fn parse_move_mouse_speed(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3229    if ac_params.len() != 1 {
3230        bail!(
3231            "movemouse-speed expects one parameter, found {}\n<speed scaling % (1-65535)>",
3232            ac_params.len()
3233        );
3234    }
3235    let speed = parse_non_zero_u16(&ac_params[0], s, "speed scaling %")?;
3236    Ok(s.a.sref(Action::Custom(
3237        s.a.sref(s.a.sref_slice(CustomAction::MoveMouseSpeed { speed })),
3238    )))
3239}
3240
3241fn parse_set_mouse(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3242    if ac_params.len() != 2 {
3243        bail!(
3244            "movemouse-accel expects two parameters, found {}: <x> <y>",
3245            ac_params.len()
3246        );
3247    }
3248    let x = parse_u16(&ac_params[0], s, "x")?;
3249    let y = parse_u16(&ac_params[1], s, "y")?;
3250    Ok(s.a.sref(Action::Custom(
3251        s.a.sref(s.a.sref_slice(CustomAction::SetMouse { x, y })),
3252    )))
3253}
3254
3255fn parse_dynamic_macro_record(
3256    ac_params: &[SExpr],
3257    s: &ParserState,
3258) -> Result<&'static KanataAction> {
3259    const ERR_MSG: &str = "dynamic-macro-record expects 1 parameter: <macro ID (0-65535)>";
3260    if ac_params.len() != 1 {
3261        bail!("{ERR_MSG}, found {}", ac_params.len());
3262    }
3263    let key = parse_u16(&ac_params[0], s, "macro ID")?;
3264    Ok(s.a.sref(Action::Custom(
3265        s.a.sref(s.a.sref_slice(CustomAction::DynamicMacroRecord(key))),
3266    )))
3267}
3268
3269fn parse_dynamic_macro_play(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3270    const ERR_MSG: &str = "dynamic-macro-play expects 1 parameter: <macro ID (number 0-65535)>";
3271    if ac_params.len() != 1 {
3272        bail!("{ERR_MSG}, found {}", ac_params.len());
3273    }
3274    let key = parse_u16(&ac_params[0], s, "macro ID")?;
3275    Ok(s.a.sref(Action::Custom(
3276        s.a.sref(s.a.sref_slice(CustomAction::DynamicMacroPlay(key))),
3277    )))
3278}
3279
3280fn parse_live_reload_num(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3281    const ERR_MSG: &str = "expects 1 parameter: <config argument position (1-65535)>";
3282    if ac_params.len() != 1 {
3283        bail!("{LIVE_RELOAD_NUM} {ERR_MSG}, found {}", ac_params.len());
3284    }
3285    let num = parse_non_zero_u16(&ac_params[0], s, "config argument position")?;
3286    Ok(s.a.sref(Action::Custom(
3287        // Note: for user-friendliness (hopefully), begin at 1 for parsing.
3288        // But for use as an index when stored as data, subtract 1 for 0-based indexing.
3289        s.a.sref(s.a.sref_slice(CustomAction::LiveReloadNum(num - 1))),
3290    )))
3291}
3292
3293fn parse_live_reload_file(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3294    const ERR_MSG: &str = "expects 1 parameter: <config argument (exact path)>";
3295    if ac_params.len() != 1 {
3296        bail!("{LIVE_RELOAD_FILE} {ERR_MSG}, found {}", ac_params.len());
3297    }
3298    let expr = &ac_params[0];
3299    let spanned_filepath = match expr {
3300        SExpr::Atom(filepath) => filepath,
3301        SExpr::List(_) => {
3302            bail_expr!(&expr, "Filepath cannot be a list")
3303        }
3304    };
3305    let lrld_file_path = spanned_filepath.t.trim_atom_quotes();
3306    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3307        CustomAction::LiveReloadFile(lrld_file_path.to_string()),
3308    )))))
3309}
3310
3311fn parse_clipboard_set(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3312    const ERR_MSG: &str = "expects 1 parameter: <clipboard string>";
3313    if ac_params.len() != 1 {
3314        bail!("{CLIPBOARD_SET} {ERR_MSG}, found {}", ac_params.len());
3315    }
3316    let expr = &ac_params[0];
3317    let clip_string = match expr {
3318        SExpr::Atom(filepath) => filepath,
3319        SExpr::List(_) => {
3320            bail_expr!(&expr, "Clipboard string cannot be a list")
3321        }
3322    };
3323    let clip_string = clip_string.t.trim_atom_quotes();
3324    Ok(s.a.sref(Action::Custom(s.a.sref(
3325        s.a.sref_slice(CustomAction::ClipboardSet(clip_string.to_string())),
3326    ))))
3327}
3328
3329fn parse_clipboard_save(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3330    const ERR_MSG: &str = "expects 1 parameter: <clipboard save id (0-65535)>";
3331    if ac_params.len() != 1 {
3332        bail!("{CLIPBOARD_SAVE} {ERR_MSG}, found {}", ac_params.len());
3333    }
3334    let id = parse_u16(&ac_params[0], s, "clipboard save ID")?;
3335    Ok(s.a.sref(Action::Custom(
3336        s.a.sref(s.a.sref_slice(CustomAction::ClipboardSave(id))),
3337    )))
3338}
3339
3340fn parse_clipboard_restore(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3341    const ERR_MSG: &str = "expects 1 parameter: <clipboard save id (0-65535)>";
3342    if ac_params.len() != 1 {
3343        bail!("{CLIPBOARD_RESTORE} {ERR_MSG}, found {}", ac_params.len());
3344    }
3345    let id = parse_u16(&ac_params[0], s, "clipboard save ID")?;
3346    Ok(s.a.sref(Action::Custom(
3347        s.a.sref(s.a.sref_slice(CustomAction::ClipboardRestore(id))),
3348    )))
3349}
3350
3351fn parse_clipboard_save_swap(
3352    ac_params: &[SExpr],
3353    s: &ParserState,
3354) -> Result<&'static KanataAction> {
3355    const ERR_MSG: &str =
3356        "expects 2 parameters: <clipboard save id (0-65535)> <clipboard save id #2>";
3357    if ac_params.len() != 2 {
3358        bail!("{CLIPBOARD_SAVE_SWAP} {ERR_MSG}, found {}", ac_params.len());
3359    }
3360    let id1 = parse_u16(&ac_params[0], s, "clipboard save ID")?;
3361    let id2 = parse_u16(&ac_params[1], s, "clipboard save ID")?;
3362    Ok(s.a.sref(Action::Custom(
3363        s.a.sref(s.a.sref_slice(CustomAction::ClipboardSaveSwap(id1, id2))),
3364    )))
3365}
3366
3367fn parse_clipboard_save_set(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3368    const ERR_MSG: &str = "expects 2 parameters: <clipboard save id (0-65535)> <save content>";
3369    if ac_params.len() != 2 {
3370        bail!("{CLIPBOARD_SAVE_SET} {ERR_MSG}, found {}", ac_params.len());
3371    }
3372    let id = parse_u16(&ac_params[0], s, "clipboard save ID")?;
3373    let save_content = ac_params[1]
3374        .atom(s.vars())
3375        .ok_or_else(|| anyhow_expr!(&ac_params[1], "save content must be a string"))?;
3376    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3377        CustomAction::ClipboardSaveSet(id, save_content.into()),
3378    )))))
3379}
3380
3381fn parse_layers(
3382    s: &ParserState,
3383    mapped_keys: &mut MappedKeys,
3384    defcfg: &CfgOptions,
3385) -> Result<IntermediateLayers> {
3386    let mut layers_cfg = new_layers(s.layer_exprs.len());
3387    if s.layer_exprs.len() > MAX_LAYERS {
3388        bail!("Maximum number of layers ({}) exceeded.", MAX_LAYERS);
3389    }
3390    let mut defsrc_layer = s.defsrc_layer;
3391    for (layer_level, layer) in s.layer_exprs.iter().enumerate() {
3392        match layer {
3393            // The skip is done to skip the `deflayer` and layer name tokens.
3394            LayerExprs::DefsrcMapping(layer) => {
3395                // Parse actions in the layer and place them appropriately according
3396                // to defsrc mapping order.
3397                for (i, ac) in layer.iter().skip(2).enumerate() {
3398                    let ac = parse_action(ac, s)?;
3399                    layers_cfg[layer_level][0][s.mapping_order[i]] = *ac;
3400                }
3401            }
3402            LayerExprs::CustomMapping(layer) => {
3403                // Parse actions as input output pairs
3404                let mut pairs = layer[2..].chunks_exact(2);
3405                let mut layer_mapped_keys = HashSet::default();
3406                let mut defsrc_anykey_used = false;
3407                let mut unmapped_anykey_used = false;
3408                let mut both_anykey_used = false;
3409                for pair in pairs.by_ref() {
3410                    let input = &pair[0];
3411                    let action = &pair[1];
3412
3413                    let action = parse_action(action, s)?;
3414                    if input.atom(s.vars()).is_some_and(|x| x == "_") {
3415                        if defsrc_anykey_used {
3416                            bail_expr!(input, "must have only one use of _ within a layer")
3417                        }
3418                        if both_anykey_used {
3419                            bail_expr!(input, "must either use _ or ___ within a layer, not both")
3420                        }
3421                        for i in 0..s.mapping_order.len() {
3422                            if layers_cfg[layer_level][0][s.mapping_order[i]] == DEFAULT_ACTION {
3423                                layers_cfg[layer_level][0][s.mapping_order[i]] = *action;
3424                            }
3425                        }
3426                        defsrc_anykey_used = true;
3427                    } else if input.atom(s.vars()).is_some_and(|x| x == "__") {
3428                        if unmapped_anykey_used {
3429                            bail_expr!(input, "must have only one use of __ within a layer")
3430                        }
3431                        if !defcfg.process_unmapped_keys {
3432                            bail_expr!(
3433                                input,
3434                                "must set process-unmapped-keys to yes to use __ to map unmapped keys"
3435                            );
3436                        }
3437                        if both_anykey_used {
3438                            bail_expr!(input, "must either use __ or ___ within a layer, not both")
3439                        }
3440                        for i in 0..layers_cfg[0][0].len() {
3441                            if layers_cfg[layer_level][0][i] == DEFAULT_ACTION
3442                                && !s.mapping_order.contains(&i)
3443                            {
3444                                layers_cfg[layer_level][0][i] = *action;
3445                            }
3446                        }
3447                        unmapped_anykey_used = true;
3448                    } else if input.atom(s.vars()).is_some_and(|x| x == "___") {
3449                        if both_anykey_used {
3450                            bail_expr!(input, "must have only one use of ___ within a layer")
3451                        }
3452                        if defsrc_anykey_used {
3453                            bail_expr!(input, "must either use _ or ___ within a layer, not both")
3454                        }
3455                        if unmapped_anykey_used {
3456                            bail_expr!(input, "must either use __ or ___ within a layer, not both")
3457                        }
3458                        if !defcfg.process_unmapped_keys {
3459                            bail_expr!(
3460                                input,
3461                                "must set process-unmapped-keys to yes to use ___ to also map unmapped keys"
3462                            );
3463                        }
3464                        for i in 0..layers_cfg[0][0].len() {
3465                            if layers_cfg[layer_level][0][i] == DEFAULT_ACTION {
3466                                layers_cfg[layer_level][0][i] = *action;
3467                            }
3468                        }
3469                        both_anykey_used = true;
3470                    } else {
3471                        let input_key = input
3472                            .atom(s.vars())
3473                            .and_then(str_to_oscode)
3474                            .ok_or_else(|| anyhow_expr!(input, "input must be a key name"))?;
3475                        mapped_keys.insert(input_key);
3476                        if !layer_mapped_keys.insert(input_key) {
3477                            bail_expr!(input, "input key must not be repeated within a layer")
3478                        }
3479                        layers_cfg[layer_level][0][usize::from(input_key)] = *action;
3480                    }
3481                }
3482                let rem = pairs.remainder();
3483                if !rem.is_empty() {
3484                    bail_expr!(&rem[0], "input must by followed by an action");
3485                }
3486            }
3487        }
3488        for (osc, layer_action) in layers_cfg[layer_level][0].iter_mut().enumerate() {
3489            if *layer_action == DEFAULT_ACTION {
3490                *layer_action = match s.block_unmapped_keys && !is_a_button(osc as u16) {
3491                    true => Action::NoOp,
3492                    false => Action::Trans,
3493                };
3494            }
3495        }
3496
3497        // Set fake keys on every layer.
3498        for (y, action) in s.virtual_keys.values() {
3499            let (x, y) = get_fake_key_coords(*y);
3500            layers_cfg[layer_level][x as usize][y as usize] = **action;
3501        }
3502
3503        // If the user has configured delegation to the first (default) layer for transparent keys,
3504        // (as opposed to delegation to defsrc), replace the defsrc actions with the actions from
3505        // the first layer.
3506        if layer_level == 0 && s.delegate_to_first_layer {
3507            for (defsrc_ac, default_layer_ac) in defsrc_layer.iter_mut().zip(layers_cfg[0][0]) {
3508                if default_layer_ac != Action::Trans {
3509                    *defsrc_ac = default_layer_ac;
3510                }
3511            }
3512        }
3513
3514        // Very last thing - ensure index 0 is always no-op. This shouldn't have any way to be
3515        // physically activated. This enable other code to rely on there always being a no-op key.
3516        layers_cfg[layer_level][0][0] = Action::NoOp;
3517    }
3518    Ok(layers_cfg)
3519}
3520
3521const SEQ_ERR: &str = "defseq expects pairs of parameters: <virtual_key_name> <key_list>";
3522
3523fn parse_sequences(exprs: &[&Vec<SExpr>], s: &ParserState) -> Result<KeySeqsToFKeys> {
3524    let mut sequences = Trie::new();
3525    for expr in exprs {
3526        let mut subexprs = check_first_expr(expr.iter(), "defseq")?.peekable();
3527
3528        while let Some(vkey_expr) = subexprs.next() {
3529            let vkey = vkey_expr.atom(s.vars()).ok_or_else(|| {
3530                anyhow_expr!(vkey_expr, "{SEQ_ERR}\nvirtual_key_name must not be a list")
3531            })?;
3532            #[cfg(feature = "lsp")]
3533            s.lsp_hints
3534                .borrow_mut()
3535                .reference_locations
3536                .virtual_key
3537                .push(vkey, vkey_expr.span());
3538            if !s.virtual_keys.contains_key(vkey) {
3539                bail_expr!(
3540                    vkey_expr,
3541                    "{SEQ_ERR}\nThe referenced key does not exist: {vkey}"
3542                );
3543            }
3544            let key_seq_expr = subexprs
3545                .next()
3546                .ok_or_else(|| anyhow_expr!(vkey_expr, "{SEQ_ERR}\nMissing key_list for {vkey}"))?;
3547            let key_seq = key_seq_expr.list(s.vars()).ok_or_else(|| {
3548                anyhow_expr!(key_seq_expr, "{SEQ_ERR}\nGot a non-list for key_list")
3549            })?;
3550            if key_seq.is_empty() {
3551                bail_expr!(key_seq_expr, "{SEQ_ERR}\nkey_list cannot be empty");
3552            }
3553
3554            let keycode_seq = parse_sequence_keys(key_seq, s)?;
3555
3556            // Generate permutations of sequences for overlapping keys.
3557            let mut permutations = vec![vec![]];
3558            let mut vals = keycode_seq.iter().copied();
3559            while let Some(val) = vals.next() {
3560                if val & KEY_OVERLAP_MARKER == 0 {
3561                    for p in permutations.iter_mut() {
3562                        p.push(val);
3563                    }
3564                    continue;
3565                }
3566
3567                if val == 0x0400 {
3568                    bail_expr!(
3569                        key_seq_expr,
3570                        "O-(...) lists must have a minimum of 2 elements"
3571                    );
3572                }
3573                let mut values_to_permute = vec![val];
3574                for val in vals.by_ref() {
3575                    if val == 0x0400 {
3576                        break;
3577                    }
3578                    values_to_permute.push(val);
3579                }
3580
3581                let ps = match values_to_permute.len() {
3582                    0 | 1 => bail_expr!(
3583                        key_seq_expr,
3584                        "O-(...) lists must have a minimum of 2 elements"
3585                    ),
3586                    2..=6 => gen_permutations(&values_to_permute[..]),
3587                    _ => bail_expr!(
3588                        key_seq_expr,
3589                        "O-(...) lists must have a maximum of 6 elements"
3590                    ),
3591                };
3592
3593                let mut new_permutations: Vec<Vec<u16>> = vec![];
3594                for p in permutations.iter() {
3595                    for p2 in ps.iter() {
3596                        new_permutations.push(
3597                            p.iter()
3598                                .copied()
3599                                .chain(p2.iter().copied().chain([KEY_OVERLAP_MARKER]))
3600                                .collect(),
3601                        );
3602                    }
3603                }
3604                permutations = new_permutations;
3605            }
3606
3607            for p in permutations.into_iter() {
3608                if sequences.ancestor_exists(&p) {
3609                    bail_expr!(
3610                        key_seq_expr,
3611                        "Sequence has a conflict: its sequence contains an earlier defined sequence"
3612                        );
3613                }
3614                if sequences.descendant_exists(&p) {
3615                    bail_expr!(key_seq_expr, "Sequence has a conflict: its sequence is contained within an earlier defined seqence");
3616                }
3617                sequences.insert(
3618                    p,
3619                    s.virtual_keys
3620                        .get(vkey)
3621                        .map(|(y, _)| get_fake_key_coords(*y))
3622                        .expect("vk exists, checked earlier"),
3623                );
3624            }
3625        }
3626    }
3627    Ok(sequences)
3628}
3629
3630fn parse_sequence_keys(exprs: &[SExpr], s: &ParserState) -> Result<Vec<u16>> {
3631    use SequenceEvent::*;
3632
3633    // Reuse macro parsing but do some other processing since sequences don't support everything
3634    // that can go in a macro, and also change error messages of course.
3635    let mut exprs_remaining = exprs;
3636    let mut all_keys = Vec::new();
3637    while !exprs_remaining.is_empty() {
3638        let (mut keys, exprs_remaining_tmp) =
3639            match parse_macro_item_impl(exprs_remaining, s, MacroNumberParseMode::Action) {
3640                Ok(res) => {
3641                    if res.0.iter().any(|k| !matches!(k, Press(..) | Release(..))) {
3642                        // Determine the bad expression depending on how many expressions were consumed
3643                        // by parse_macro_item_impl.
3644                        let bad_expr = if exprs_remaining.len() - res.1.len() == 1 {
3645                            &exprs_remaining[0]
3646                        } else {
3647                            // This error message will have an imprecise span since it will take the
3648                            // whole chorded list instead of the single element inside that's not a
3649                            // standard key. Oh well, should still be helpful. I'm too lazy to write
3650                            // the code to find the exact expr to use right now.
3651                            &exprs_remaining[1]
3652                        };
3653                        bail_expr!(bad_expr, "{SEQ_ERR}\nFound invalid key/chord in key_list");
3654                    }
3655
3656                    // The keys are currenty in the form of SequenceEvent::{Press, Release}. This is
3657                    // not what we want.
3658                    //
3659                    // The trivial and incorrect way to parse this would be to just take all of the
3660                    // presses. However, we need to transform chorded keys/lists like S-a or S-(a b) to
3661                    // have the upper bits set, to be able to differentiate (S-a b) from (S-(a b)).
3662                    //
3663                    // The order of presses and releases reveals whether or not a key is chorded with
3664                    // some modifier. When a chord starts, there are multiple presses in a row, whereas
3665                    // non-chords will always be a press followed by a release. Likewise, a chord
3666                    // ending is marked by multiple releases in a row.
3667                    let mut mods_currently_held = vec![];
3668                    let mut key_actions = res.0.iter().peekable();
3669                    let mut seq = vec![];
3670                    let mut do_release_mod = false;
3671                    while let Some(action) = key_actions.next() {
3672                        match action {
3673                            Press(pressed) => {
3674                                if matches!(key_actions.peek(), Some(Press(..))) {
3675                                    // press->press: current press is mod
3676                                    mods_currently_held.push(*pressed);
3677                                }
3678                                let mut seq_num = u16::from(OsCode::from(pressed));
3679                                for modk in mods_currently_held.iter().copied() {
3680                                    seq_num |= mod_mask_for_keycode(modk);
3681                                }
3682                                if seq_num & KEY_OVERLAP_MARKER == KEY_OVERLAP_MARKER
3683                                    && seq_num & MASK_MODDED != KEY_OVERLAP_MARKER
3684                                {
3685                                    bail_expr!(
3686                                        &exprs_remaining[0],
3687                                        "O-(...) lists cannot be combined with other modifiers."
3688                                    );
3689                                }
3690                                if *pressed != KEY_OVERLAP {
3691                                    // Note: key overlap item is special and goes at the end,
3692                                    // not the beginning
3693                                    seq.push(seq_num);
3694                                }
3695                            }
3696                            Release(released) => {
3697                                if *released == KEY_OVERLAP {
3698                                    seq.push(KEY_OVERLAP_MARKER);
3699                                }
3700                                if do_release_mod {
3701                                    mods_currently_held.remove(
3702                                        mods_currently_held
3703                                            .iter()
3704                                            .position(|modk| modk == released)
3705                                            .expect("had to be pressed to be released"),
3706                                    );
3707                                }
3708                                // release->release: next release is mod
3709                                do_release_mod = matches!(key_actions.peek(), Some(Release(..)));
3710                            }
3711                            _ => unreachable!("should be filtered out"),
3712                        }
3713                    }
3714
3715                    (seq, res.1)
3716                }
3717                Err(mut e) => {
3718                    e.msg = format!("{SEQ_ERR}\nFound invalid key/chord in key_list");
3719                    return Err(e);
3720                }
3721            };
3722        all_keys.append(&mut keys);
3723        exprs_remaining = exprs_remaining_tmp;
3724    }
3725    Ok(all_keys)
3726}
3727
3728fn parse_arbitrary_code(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3729    const ERR_MSG: &str = "arbitrary code expects one parameter: <code: 0-767>";
3730    if ac_params.len() != 1 {
3731        bail!("{ERR_MSG}");
3732    }
3733    let code = ac_params[0]
3734        .atom(s.vars())
3735        .map(str::parse::<u16>)
3736        .and_then(|c| match c {
3737            Ok(code @ 0..=767) => Some(code),
3738            _ => None,
3739        })
3740        .ok_or_else(|| anyhow!("{ERR_MSG}: got {:?}", ac_params[0]))?;
3741    Ok(s.a.sref(Action::Custom(
3742        s.a.sref(s.a.sref_slice(CustomAction::SendArbitraryCode(code))),
3743    )))
3744}
3745
3746fn parse_overrides(exprs: &[SExpr], s: &ParserState) -> Result<Overrides> {
3747    const ERR_MSG: &str =
3748        "defoverrides expects pairs of parameters: <input key list> <output key list>";
3749    let mut subexprs = check_first_expr(exprs.iter(), "defoverrides")?;
3750
3751    let mut overrides = Vec::<Override>::new();
3752    while let Some(in_keys_expr) = subexprs.next() {
3753        let in_keys = in_keys_expr
3754            .list(s.vars())
3755            .ok_or_else(|| anyhow_expr!(in_keys_expr, "Input keys must be a list"))?;
3756        let out_keys_expr = subexprs
3757            .next()
3758            .ok_or_else(|| anyhow_expr!(in_keys_expr, "Missing output keys for input keys"))?;
3759        let out_keys = out_keys_expr
3760            .list(s.vars())
3761            .ok_or_else(|| anyhow_expr!(out_keys_expr, "Output keys must be a list"))?;
3762        let in_keys =
3763            in_keys
3764                .iter()
3765                .try_fold(vec![], |mut keys, key_expr| -> Result<Vec<OsCode>> {
3766                    let key = key_expr
3767                        .atom(s.vars())
3768                        .and_then(str_to_oscode)
3769                        .ok_or_else(|| {
3770                            anyhow_expr!(key_expr, "Unknown input key name, must use known keys")
3771                        })?;
3772                    keys.push(key);
3773                    Ok(keys)
3774                })?;
3775        let out_keys =
3776            out_keys
3777                .iter()
3778                .try_fold(vec![], |mut keys, key_expr| -> Result<Vec<OsCode>> {
3779                    let key = key_expr
3780                        .atom(s.vars())
3781                        .and_then(str_to_oscode)
3782                        .ok_or_else(|| {
3783                            anyhow_expr!(key_expr, "Unknown output key name, must use known keys")
3784                        })?;
3785                    keys.push(key);
3786                    Ok(keys)
3787                })?;
3788        overrides
3789            .push(Override::try_new(&in_keys, &out_keys).map_err(|e| anyhow!("{ERR_MSG}: {e}"))?);
3790    }
3791    log::debug!("All overrides:\n{overrides:#?}");
3792    Ok(Overrides::new(&overrides))
3793}
3794
3795fn parse_fork(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3796    const ERR_STR: &str =
3797        "fork expects 3 params: <left-action> <right-action> <right-trigger-keys>";
3798    if ac_params.len() != 3 {
3799        bail!("{ERR_STR}\nFound {} params instead of 3", ac_params.len());
3800    }
3801    let left = *parse_action(&ac_params[0], s)?;
3802    let right = *parse_action(&ac_params[1], s)?;
3803    let right_triggers = s.a.sref_vec(
3804        parse_key_list(&ac_params[2], s, "right-trigger-keys")?
3805            .into_iter()
3806            .map(KeyCode::from)
3807            .collect::<Vec<_>>(),
3808    );
3809    Ok(s.a.sref(Action::Fork(s.a.sref(ForkConfig {
3810        left,
3811        right,
3812        right_triggers,
3813    }))))
3814}
3815
3816fn parse_caps_word(
3817    ac_params: &[SExpr],
3818    repress_behaviour: CapsWordRepressBehaviour,
3819    s: &ParserState,
3820) -> Result<&'static KanataAction> {
3821    const ERR_STR: &str = "caps-word expects 1 param: <timeout>";
3822    if ac_params.len() != 1 {
3823        bail!("{ERR_STR}\nFound {} params instead of 1", ac_params.len());
3824    }
3825    let timeout = parse_non_zero_u16(&ac_params[0], s, "timeout")?;
3826    Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
3827        CustomAction::CapsWord(CapsWordCfg {
3828            repress_behaviour,
3829            keys_to_capitalize: &[
3830                KeyCode::A,
3831                KeyCode::B,
3832                KeyCode::C,
3833                KeyCode::D,
3834                KeyCode::E,
3835                KeyCode::F,
3836                KeyCode::G,
3837                KeyCode::H,
3838                KeyCode::I,
3839                KeyCode::J,
3840                KeyCode::K,
3841                KeyCode::L,
3842                KeyCode::M,
3843                KeyCode::N,
3844                KeyCode::O,
3845                KeyCode::P,
3846                KeyCode::Q,
3847                KeyCode::R,
3848                KeyCode::S,
3849                KeyCode::T,
3850                KeyCode::U,
3851                KeyCode::V,
3852                KeyCode::W,
3853                KeyCode::X,
3854                KeyCode::Y,
3855                KeyCode::Z,
3856                KeyCode::Minus,
3857            ],
3858            keys_nonterminal: &[
3859                KeyCode::Kb0,
3860                KeyCode::Kb1,
3861                KeyCode::Kb2,
3862                KeyCode::Kb3,
3863                KeyCode::Kb4,
3864                KeyCode::Kb5,
3865                KeyCode::Kb6,
3866                KeyCode::Kb7,
3867                KeyCode::Kb8,
3868                KeyCode::Kb9,
3869                KeyCode::Kp0,
3870                KeyCode::Kp1,
3871                KeyCode::Kp2,
3872                KeyCode::Kp3,
3873                KeyCode::Kp4,
3874                KeyCode::Kp5,
3875                KeyCode::Kp6,
3876                KeyCode::Kp7,
3877                KeyCode::Kp8,
3878                KeyCode::Kp9,
3879                KeyCode::BSpace,
3880                KeyCode::Delete,
3881                KeyCode::Up,
3882                KeyCode::Down,
3883                KeyCode::Left,
3884                KeyCode::Right,
3885            ],
3886            timeout,
3887        }),
3888    )))))
3889}
3890
3891fn parse_caps_word_custom(
3892    ac_params: &[SExpr],
3893    repress_behaviour: CapsWordRepressBehaviour,
3894    s: &ParserState,
3895) -> Result<&'static KanataAction> {
3896    const ERR_STR: &str = "caps-word-custom expects 3 param: <timeout> <keys-to-capitalize> <extra-non-terminal-keys>";
3897    if ac_params.len() != 3 {
3898        bail!("{ERR_STR}\nFound {} params instead of 3", ac_params.len());
3899    }
3900    let timeout = parse_non_zero_u16(&ac_params[0], s, "timeout")?;
3901    Ok(s.a.sref(Action::Custom(
3902        s.a.sref(
3903            s.a.sref_slice(CustomAction::CapsWord(CapsWordCfg {
3904                repress_behaviour,
3905                keys_to_capitalize: s.a.sref_vec(
3906                    parse_key_list(&ac_params[1], s, "keys-to-capitalize")?
3907                        .into_iter()
3908                        .map(KeyCode::from)
3909                        .collect(),
3910                ),
3911                keys_nonterminal: s.a.sref_vec(
3912                    parse_key_list(&ac_params[2], s, "extra-non-terminal-keys")?
3913                        .into_iter()
3914                        .map(KeyCode::from)
3915                        .collect(),
3916                ),
3917                timeout,
3918            })),
3919        ),
3920    )))
3921}
3922
3923fn parse_macro_record_stop_truncate(
3924    ac_params: &[SExpr],
3925    s: &ParserState,
3926) -> Result<&'static KanataAction> {
3927    const ERR_STR: &str =
3928        "dynamic-macro-record-stop-truncate expects 1 param: <num-keys-to-truncate>";
3929    if ac_params.len() != 1 {
3930        bail!("{ERR_STR}\nFound {} params instead of 1", ac_params.len());
3931    }
3932    let num_to_truncate = parse_u16(&ac_params[0], s, "num-keys-to-truncate")?;
3933    Ok(s.a.sref(Action::Custom(s.a.sref(
3934        s.a.sref_slice(CustomAction::DynamicMacroRecordStop(num_to_truncate)),
3935    ))))
3936}
3937
3938fn parse_sequence_start(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3939    const ERR_MSG: &str =
3940        "sequence expects one or two params: <timeout-override> <?input-mode-override>";
3941    if !matches!(ac_params.len(), 1 | 2) {
3942        bail!("{ERR_MSG}\nfound {} items", ac_params.len());
3943    }
3944    let timeout = parse_non_zero_u16(&ac_params[0], s, "timeout-override")?;
3945    let input_mode = if ac_params.len() > 1 {
3946        if let Some(Ok(input_mode)) = ac_params[1]
3947            .atom(s.vars())
3948            .map(SequenceInputMode::try_from_str)
3949        {
3950            input_mode
3951        } else {
3952            bail_expr!(&ac_params[1], "{ERR_MSG}\n{}", SequenceInputMode::err_msg());
3953        }
3954    } else {
3955        s.default_sequence_input_mode
3956    };
3957    Ok(s.a.sref(Action::Custom(s.a.sref(
3958        s.a.sref_slice(CustomAction::SequenceLeader(timeout, input_mode)),
3959    ))))
3960}
3961
3962fn parse_sequence_noerase(ac_params: &[SExpr], s: &ParserState) -> Result<&'static KanataAction> {
3963    const ERR_MSG: &str = "sequence-noerase expects one: <noerase-count>";
3964    if ac_params.len() != 1 {
3965        bail!("{ERR_MSG}\nfound {} items", ac_params.len());
3966    }
3967    let count = parse_non_zero_u16(&ac_params[0], s, "noerase-count")?;
3968    Ok(s.a.sref(Action::Custom(
3969        s.a.sref(s.a.sref_slice(CustomAction::SequenceNoerase(count))),
3970    )))
3971}
3972
3973fn parse_unmod(
3974    unmod_type: &str,
3975    ac_params: &[SExpr],
3976    s: &ParserState,
3977) -> Result<&'static KanataAction> {
3978    const ERR_MSG: &str = "expects expects at least one key name";
3979    if ac_params.is_empty() {
3980        bail!("{unmod_type} {ERR_MSG}\nfound {} items", ac_params.len());
3981    }
3982
3983    let mut mods = UnmodMods::all();
3984    let mut params = ac_params;
3985    // Parse the optional first-list that specifies the mod keys to use.
3986    if let Some(mod_list) = ac_params[0].list(s.vars()) {
3987        if unmod_type != UNMOD {
3988            bail_expr!(
3989                &ac_params[0],
3990                "{unmod_type} only expects key names but found a list"
3991            );
3992        }
3993        mods = mod_list
3994            .iter()
3995            .try_fold(UnmodMods::empty(), |mod_flags, mod_key| {
3996                let flag = mod_key
3997                    .atom(s.vars())
3998                    .and_then(str_to_oscode)
3999                    .and_then(|osc| match osc {
4000                        OsCode::KEY_LEFTSHIFT => Some(UnmodMods::LSft),
4001                        OsCode::KEY_RIGHTSHIFT => Some(UnmodMods::RSft),
4002                        OsCode::KEY_LEFTCTRL => Some(UnmodMods::LCtl),
4003                        OsCode::KEY_RIGHTCTRL => Some(UnmodMods::RCtl),
4004                        OsCode::KEY_LEFTMETA => Some(UnmodMods::LMet),
4005                        OsCode::KEY_RIGHTMETA => Some(UnmodMods::RMet),
4006                        OsCode::KEY_LEFTALT => Some(UnmodMods::LAlt),
4007                        OsCode::KEY_RIGHTALT => Some(UnmodMods::RAlt),
4008                        _ => None,
4009                    })
4010                    .ok_or_else(|| {
4011                        anyhow_expr!(
4012                            mod_key,
4013                            "{UNMOD} expects modifier key names within the modifier list."
4014                        )
4015                    })?;
4016                if !(mod_flags & flag).is_empty() {
4017                    bail_expr!(
4018                        mod_key,
4019                        "Duplicate key name in modifier key list is not allowed."
4020                    );
4021                }
4022                Ok::<_, ParseError>(mod_flags | flag)
4023            })?;
4024        if mods.is_empty() {
4025            bail_expr!(&ac_params[0], "an empty modifier key list is invalid");
4026        }
4027        if ac_params[1..].is_empty() {
4028            bail!("at least one key is required after the modifier key list");
4029        }
4030        params = &ac_params[1..];
4031    }
4032
4033    let keys: Vec<KeyCode> = params.iter().try_fold(Vec::new(), |mut keys, param| {
4034        keys.push(
4035            param
4036                .atom(s.vars())
4037                .and_then(str_to_oscode)
4038                .ok_or_else(|| {
4039                    anyhow_expr!(
4040                        &ac_params[0],
4041                        "{unmod_type} {ERR_MSG}\nfound invalid key name"
4042                    )
4043                })?
4044                .into(),
4045        );
4046        Ok::<_, ParseError>(keys)
4047    })?;
4048    let keys = keys.into_boxed_slice();
4049    match unmod_type {
4050        UNMOD => Ok(s.a.sref(Action::Custom(
4051            s.a.sref(s.a.sref_slice(CustomAction::Unmodded { keys, mods })),
4052        ))),
4053        UNSHIFT => Ok(s.a.sref(Action::Custom(
4054            s.a.sref(s.a.sref_slice(CustomAction::Unshifted { keys })),
4055        ))),
4056        _ => panic!("Unknown unmod type {unmod_type}"),
4057    }
4058}