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