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