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