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