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