1use parking_lot::ReentrantMutex;
4use std::cell::RefCell;
5use std::collections::HashMap;
6use std::collections::VecDeque;
7use std::sync::Mutex;
8
9#[derive(Debug)]
11pub struct ZleState {
12 pub buffer: String,
14 pub cursor: usize,
16 pub mark: usize,
18 pub numeric_arg: Option<i32>,
20 pub insert_mode: bool,
22 pub last_find_char: Option<char>,
24 pub find_forward: bool,
26 undo_history: Vec<(String, usize)>,
28 pub undo_stack: Vec<(String, usize)>,
30 kill_ring: VecDeque<String>,
32 kill_ring_max: usize,
34 pub vi_cmd_mode: bool,
36 pub keymap: KeymapName,
38 last_yank_pos: Option<(usize, usize)>,
40 pub region_active: bool,
42}
43
44impl Default for ZleState {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50impl ZleState {
51 pub fn new() -> Self {
52 ZleState {
53 buffer: String::new(),
54 cursor: 0,
55 mark: 0,
56 numeric_arg: None,
57 insert_mode: true,
58 last_find_char: None,
59 find_forward: true,
60 undo_history: Vec::new(),
61 undo_stack: Vec::new(),
62 kill_ring: VecDeque::new(),
63 kill_ring_max: 8,
64 vi_cmd_mode: false,
65 keymap: KeymapName::Emacs,
66 last_yank_pos: None,
67 region_active: false,
68 }
69 }
70
71 pub fn save_undo(&mut self) {
73 self.undo_history.push((self.buffer.clone(), self.cursor));
74 if self.undo_history.len() > 100 {
75 self.undo_history.remove(0);
76 }
77 }
78
79 pub fn undo(&mut self) -> bool {
81 if let Some((buffer, cursor)) = self.undo_history.pop() {
82 self.undo_stack.push((self.buffer.clone(), self.cursor));
83 self.buffer = buffer;
84 self.cursor = cursor;
85 true
86 } else {
87 false
88 }
89 }
90
91 pub fn redo(&mut self) -> bool {
93 if let Some((buffer, cursor)) = self.undo_stack.pop() {
94 self.undo_history.push((self.buffer.clone(), self.cursor));
95 self.buffer = buffer;
96 self.cursor = cursor;
97 true
98 } else {
99 false
100 }
101 }
102
103 pub fn kill_add(&mut self, text: &str) {
105 self.kill_ring.push_front(text.to_string());
106 if self.kill_ring.len() > self.kill_ring_max {
107 self.kill_ring.pop_back();
108 }
109 }
110
111 pub fn yank(&mut self) -> Option<String> {
113 if let Some(text) = self.kill_ring.front().cloned() {
114 let start = self.cursor;
115 let chars: Vec<char> = self.buffer.chars().collect();
117 let mut new_buffer = String::new();
118 for (i, c) in chars.iter().enumerate() {
119 if i == self.cursor {
120 new_buffer.push_str(&text);
121 }
122 new_buffer.push(*c);
123 }
124 if self.cursor >= chars.len() {
125 new_buffer.push_str(&text);
126 }
127 self.buffer = new_buffer;
128 self.cursor += text.chars().count();
129 self.last_yank_pos = Some((start, self.cursor));
130 Some(text)
131 } else {
132 None
133 }
134 }
135
136 pub fn yank_pop(&mut self) -> Option<String> {
138 if let Some((start, end)) = self.last_yank_pos {
139 let chars: Vec<char> = self.buffer.chars().collect();
141 let mut new_buffer = String::new();
142 for (i, c) in chars.iter().enumerate() {
143 if i < start || i >= end {
144 new_buffer.push(*c);
145 }
146 }
147 self.buffer = new_buffer;
148 self.cursor = start;
149
150 if let Some(front) = self.kill_ring.pop_front() {
152 self.kill_ring.push_back(front);
153 }
154
155 self.yank()
157 } else {
158 None
159 }
160 }
161
162 pub fn kill_yank(&self) -> Option<&str> {
164 self.kill_ring.front().map(|s| s.as_str())
165 }
166
167 pub fn kill_rotate(&mut self) {
169 if let Some(front) = self.kill_ring.pop_front() {
170 self.kill_ring.push_back(front);
171 }
172 }
173}
174
175pub struct ZleManager {
177 pub keymaps: HashMap<KeymapName, Keymap>,
179 pub user_widgets: HashMap<String, String>,
181 pub active_keymap: KeymapName,
185}
186
187impl Default for ZleManager {
188 fn default() -> Self {
189 Self::new()
190 }
191}
192
193impl ZleManager {
194 pub fn new() -> Self {
195 let mut mgr = ZleManager {
196 keymaps: HashMap::new(),
197 user_widgets: HashMap::new(),
198 active_keymap: KeymapName::Emacs,
199 };
200
201 mgr.keymaps
202 .insert(KeymapName::Main, Keymap::emacs_default());
203 mgr.keymaps
204 .insert(KeymapName::Emacs, Keymap::emacs_default());
205 mgr.keymaps
206 .insert(KeymapName::ViInsert, Keymap::viins_default());
207 mgr.keymaps
208 .insert(KeymapName::ViCommand, Keymap::vicmd_default());
209 mgr.keymaps.insert(KeymapName::Isearch, Keymap::new());
210 mgr.keymaps.insert(KeymapName::Command, Keymap::new());
211 mgr.keymaps.insert(KeymapName::MenuSelect, Keymap::new());
212
213 mgr
214 }
215
216 pub fn define_widget(&mut self, name: &str, func: &str) {
218 self.user_widgets.insert(name.to_string(), func.to_string());
219 }
220
221 pub fn delete_widget(&mut self, name: &str) -> bool {
225 self.user_widgets.remove(name).is_some()
226 }
227
228 pub fn alias_widget(&mut self, new_name: &str, target: &str) -> bool {
233 if let Some(func) = self.user_widgets.get(target).cloned() {
235 self.user_widgets.insert(new_name.to_string(), func);
236 return true;
237 }
238 if BUILTIN_WIDGETS.contains(&target) {
241 self.user_widgets
242 .insert(new_name.to_string(), target.to_string());
243 return true;
244 }
245 false
246 }
247
248 pub fn select_keymap(&mut self, name: &str) -> bool {
254 let target = match name {
255 "main" => KeymapName::Main,
256 "emacs" => KeymapName::Emacs,
257 "viins" => KeymapName::ViInsert,
258 "vicmd" => KeymapName::ViCommand,
259 "isearch" => KeymapName::Isearch,
260 "command" => KeymapName::Command,
261 "menuselect" => KeymapName::MenuSelect,
262 _ => return false,
263 };
264 if !self.keymaps.contains_key(&target) {
265 return false;
266 }
267 self.active_keymap = target;
268 true
269 }
270
271 pub fn get_widget<'a>(&'a self, name: &'a str) -> Option<&'a str> {
273 if let Some(func) = self.user_widgets.get(name) {
275 return Some(func);
276 }
277 if BUILTIN_WIDGETS.contains(&name) {
279 return Some(name);
280 }
281 None
282 }
283
284 pub fn bind_key(&mut self, keymap: KeymapName, key: &str, widget: &str) {
286 if let Some(km) = self.keymaps.get_mut(&keymap) {
287 km.bind(key, widget);
288 }
289 }
290
291 pub fn unbind_key(&mut self, keymap: KeymapName, key: &str) {
293 if let Some(km) = self.keymaps.get_mut(&keymap) {
294 km.unbind(key);
295 }
296 }
297
298 pub fn execute_widget(
300 &mut self,
301 name: &str,
302 _key: Option<char>,
303 ) -> super::widgets::WidgetResult {
304 if self.get_widget(name).is_some() {
305 super::widgets::WidgetResult::Ok
306 } else {
307 super::widgets::WidgetResult::Error(format!("Unknown widget: {}", name))
308 }
309 }
310
311 pub fn list_widgets(&self) -> Vec<&str> {
313 let mut widgets: Vec<&str> = BUILTIN_WIDGETS.to_vec();
314
315 for name in self.user_widgets.keys() {
316 widgets.push(name.as_str());
317 }
318
319 widgets
320 }
321}
322
323const BUILTIN_WIDGETS: &[&str] = &[
325 "accept-line",
326 "accept-and-hold",
327 "backward-char",
328 "backward-delete-char",
329 "backward-kill-line",
330 "backward-kill-word",
331 "backward-word",
332 "beep",
333 "beginning-of-history",
334 "beginning-of-line",
335 "capitalize-word",
336 "clear-screen",
337 "complete-word",
338 "copy-region-as-kill",
339 "delete-char",
340 "delete-char-or-list",
341 "down-case-word",
342 "down-history",
343 "down-line-or-history",
344 "down-line-or-search",
345 "end-of-history",
346 "end-of-line",
347 "exchange-point-and-mark",
348 "execute-named-cmd",
349 "expand-or-complete",
350 "forward-char",
351 "forward-word",
352 "history-incremental-search-backward",
353 "history-incremental-search-forward",
354 "kill-buffer",
355 "kill-line",
356 "kill-region",
357 "kill-whole-line",
358 "kill-word",
359 "overwrite-mode",
360 "quoted-insert",
361 "redisplay",
362 "redo",
363 "self-insert",
364 "send-break",
365 "set-mark-command",
366 "transpose-chars",
367 "transpose-words",
368 "undo",
369 "up-case-word",
370 "up-history",
371 "up-line-or-history",
372 "up-line-or-search",
373 "vi-add-eol",
374 "vi-add-next",
375 "vi-backward-blank-word",
376 "vi-backward-char",
377 "vi-backward-delete-char",
378 "vi-backward-word",
379 "vi-change",
380 "vi-change-eol",
381 "vi-change-whole-line",
382 "vi-cmd-mode",
383 "vi-delete",
384 "vi-delete-char",
385 "vi-end-of-line",
386 "vi-find-next-char",
387 "vi-find-next-char-skip",
388 "vi-find-prev-char",
389 "vi-find-prev-char-skip",
390 "vi-first-non-blank",
391 "vi-forward-blank-word",
392 "vi-forward-char",
393 "vi-forward-word",
394 "vi-forward-word-end",
395 "vi-insert",
396 "vi-insert-bol",
397 "vi-join",
398 "vi-kill-eol",
399 "vi-open-line-above",
400 "vi-open-line-below",
401 "vi-put-after",
402 "vi-put-before",
403 "vi-repeat-change",
404 "vi-repeat-find",
405 "vi-repeat-search",
406 "vi-replace",
407 "vi-replace-chars",
408 "vi-rev-repeat-find",
409 "vi-rev-repeat-search",
410 "vi-substitute",
411 "vi-yank",
412 "vi-yank-whole-line",
413 "which-command",
414 "yank",
415 "yank-pop",
416];
417
418thread_local! {
419 static ZLE_MANAGER: RefCell<ZleManager> = RefCell::new(ZleManager::new());
420}
421
422pub struct ZleGuard<'a>(std::cell::RefMut<'a, ZleManager>);
424
425impl<'a> std::ops::Deref for ZleGuard<'a> {
426 type Target = ZleManager;
427 fn deref(&self) -> &Self::Target {
428 &self.0
429 }
430}
431
432impl<'a> std::ops::DerefMut for ZleGuard<'a> {
433 fn deref_mut(&mut self) -> &mut Self::Target {
434 &mut self.0
435 }
436}
437
438pub fn zle() -> ZleGuard<'static> {
440 ZLE_MANAGER.with(|m| {
441 ZleGuard(unsafe { std::mem::transmute(m.borrow_mut()) })
443 })
444}
445
446#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
448pub enum KeymapName {
449 Emacs,
450 ViInsert,
451 ViCommand,
452 Main, Isearch, Command, MenuSelect, }
457
458impl KeymapName {
459 pub fn from_str(s: &str) -> Option<Self> {
460 match s {
461 "emacs" => Some(Self::Emacs),
462 "viins" => Some(Self::ViInsert),
463 "vicmd" => Some(Self::ViCommand),
464 "main" => Some(Self::Main),
465 "isearch" => Some(Self::Isearch),
466 "command" => Some(Self::Command),
467 "menuselect" => Some(Self::MenuSelect),
468 _ => None,
469 }
470 }
471
472 pub fn as_str(&self) -> &'static str {
473 match self {
474 Self::Emacs => "emacs",
475 Self::ViInsert => "viins",
476 Self::ViCommand => "vicmd",
477 Self::Main => "main",
478 Self::Isearch => "isearch",
479 Self::Command => "command",
480 Self::MenuSelect => "menuselect",
481 }
482 }
483}
484
485#[derive(Debug, Clone)]
487pub struct Keymap {
488 bindings: HashMap<String, String>,
489}
490
491impl Default for Keymap {
492 fn default() -> Self {
493 Self::new()
494 }
495}
496
497impl Keymap {
498 pub fn new() -> Self {
499 Self {
500 bindings: HashMap::new(),
501 }
502 }
503
504 pub fn emacs_default() -> Self {
506 let mut km = Self::new();
507
508 km.bind("^F", "forward-char");
510 km.bind("^B", "backward-char");
511 km.bind("^A", "beginning-of-line");
512 km.bind("^E", "end-of-line");
513 km.bind("\\ef", "forward-word"); km.bind("\\eb", "backward-word"); km.bind("^D", "delete-char");
518 km.bind("^H", "backward-delete-char");
519 km.bind("^?", "backward-delete-char"); km.bind("^K", "kill-line");
521 km.bind("^U", "backward-kill-line");
522 km.bind("\\ed", "kill-word"); km.bind("\\e^?", "backward-kill-word"); km.bind("^W", "backward-kill-word");
525 km.bind("^Y", "yank");
526 km.bind("\\ey", "yank-pop"); km.bind("^_", "undo");
530 km.bind("^X^U", "undo");
531 km.bind("\\e_", "redo"); km.bind("^P", "up-line-or-history");
535 km.bind("^N", "down-line-or-history");
536 km.bind("\\e<", "beginning-of-history");
537 km.bind("\\e>", "end-of-history");
538 km.bind("^R", "history-incremental-search-backward");
539 km.bind("^S", "history-incremental-search-forward");
540
541 km.bind("^I", "expand-or-complete"); km.bind("\\e\\e", "complete-word");
544
545 km.bind("^J", "accept-line"); km.bind("^M", "accept-line"); km.bind("^G", "send-break");
549 km.bind("^C", "send-break");
550 km.bind("^L", "clear-screen");
551
552 km.bind("^T", "transpose-chars");
554 km.bind("\\et", "transpose-words");
555
556 km.bind("\\ec", "capitalize-word");
558 km.bind("\\el", "down-case-word");
559 km.bind("\\eu", "up-case-word");
560
561 km.bind("^@", "set-mark-command"); km.bind("^X^X", "exchange-point-and-mark");
564 km.bind("\\ew", "copy-region-as-kill");
565
566 km
567 }
568
569 pub fn viins_default() -> Self {
571 let mut km = Self::new();
572
573 km.bind("^[", "vi-cmd-mode"); km.bind("^H", "backward-delete-char");
578 km.bind("^?", "backward-delete-char");
579 km.bind("^W", "backward-kill-word");
580 km.bind("^U", "backward-kill-line");
581
582 km.bind("^J", "accept-line");
584 km.bind("^M", "accept-line");
585
586 km.bind("^I", "expand-or-complete");
588
589 km.bind("^P", "up-line-or-history");
591 km.bind("^N", "down-line-or-history");
592
593 km
594 }
595
596 pub fn vicmd_default() -> Self {
598 let mut km = Self::new();
599
600 km.bind("i", "vi-insert");
602 km.bind("a", "vi-add-next");
603 km.bind("I", "vi-insert-bol");
604 km.bind("A", "vi-add-eol");
605
606 km.bind("h", "backward-char");
608 km.bind("l", "forward-char");
609 km.bind("w", "forward-word");
610 km.bind("b", "backward-word");
611 km.bind("0", "beginning-of-line");
612 km.bind("^", "beginning-of-line");
613 km.bind("$", "end-of-line");
614
615 km.bind("x", "delete-char");
617 km.bind("X", "backward-delete-char");
618 km.bind("dd", "kill-whole-line");
619 km.bind("dw", "kill-word");
620 km.bind("db", "backward-kill-word");
621 km.bind("d$", "kill-line");
622 km.bind("d0", "backward-kill-line");
623
624 km.bind("y", "vi-yank");
626 km.bind("p", "vi-put-after");
627 km.bind("P", "vi-put-before");
628
629 km.bind("k", "up-line-or-history");
631 km.bind("j", "down-line-or-history");
632 km.bind("/", "history-incremental-search-backward");
633 km.bind("?", "history-incremental-search-forward");
634 km.bind("n", "vi-repeat-search");
635 km.bind("N", "vi-rev-repeat-search");
636
637 km.bind("u", "undo");
639 km.bind("^R", "redo");
640
641 km.bind("^J", "accept-line");
643 km.bind("^M", "accept-line");
644
645 km
646 }
647
648 pub fn bind(&mut self, keys: &str, widget: &str) {
650 let normalized = Self::normalize_keys(keys);
651 self.bindings.insert(normalized, widget.to_string());
652 }
653
654 pub fn unbind(&mut self, keys: &str) {
656 let normalized = Self::normalize_keys(keys);
657 self.bindings.remove(&normalized);
658 }
659
660 pub fn lookup(&self, keys: &str) -> Option<&str> {
662 let normalized = Self::normalize_keys(keys);
663 self.bindings.get(&normalized).map(|s| s.as_str())
664 }
665
666 pub fn has_prefix(&self, keys: &str) -> bool {
668 let normalized = Self::normalize_keys(keys);
669 self.bindings
670 .keys()
671 .any(|k| k.starts_with(&normalized) && k != &normalized)
672 }
673
674 pub fn list_bindings(&self) -> impl Iterator<Item = (&str, &str)> {
681 let mut sorted: Vec<(&String, &String)> = self.bindings.iter().collect();
682 sorted.sort_by(|a, b| a.0.cmp(b.0));
683 sorted.into_iter().map(|(k, v)| (k.as_str(), v.as_str()))
684 }
685
686 fn normalize_keys(keys: &str) -> String {
693 let mut result = String::new();
694 let mut chars = keys.chars().peekable();
695
696 while let Some(c) = chars.next() {
697 match c {
698 '^' => {
699 if let Some(&next) = chars.peek() {
701 chars.next();
702 let ctrl_char = if next == '?' {
703 '\x7f' } else if next == '@' {
705 '\x00' } else if next == '[' {
707 '\x1b' } else {
709 ((next.to_ascii_uppercase() as u8) & 0x1f) as char
711 };
712 result.push(ctrl_char);
713 } else {
714 result.push(c);
715 }
716 }
717 '\\' => {
718 if let Some(&next) = chars.peek() {
720 match next {
721 'e' | 'E' => {
722 chars.next();
723 result.push('\x1b'); }
725 'C' => {
726 chars.next();
727 if chars.peek() == Some(&'-') {
728 chars.next();
729 if let Some(&ctrl_char) = chars.peek() {
730 chars.next();
731 let ctrl =
732 ((ctrl_char.to_ascii_uppercase() as u8) & 0x1f) as char;
733 result.push(ctrl);
734 }
735 }
736 }
737 'M' => {
738 chars.next();
739 if chars.peek() == Some(&'-') {
740 chars.next();
741 result.push('\x1b'); if let Some(&meta_char) = chars.peek() {
743 chars.next();
744 result.push(meta_char);
745 }
746 }
747 }
748 'n' => {
749 chars.next();
750 result.push('\n');
751 }
752 't' => {
753 chars.next();
754 result.push('\t');
755 }
756 'r' => {
757 chars.next();
758 result.push('\r');
759 }
760 '\\' => {
761 chars.next();
762 result.push('\\');
763 }
764 _ => {
765 result.push(c);
766 }
767 }
768 } else {
769 result.push(c);
770 }
771 }
772 _ => result.push(c),
773 }
774 }
775
776 result
777 }
778}
779
780#[cfg(test)]
781mod tests {
782 use super::*;
783
784 #[test]
785 fn test_normalize_keys() {
786 assert_eq!(Keymap::normalize_keys("^A"), "\x01");
787 assert_eq!(Keymap::normalize_keys("^?"), "\x7f");
788 assert_eq!(Keymap::normalize_keys("\\ef"), "\x1bf");
789 assert_eq!(Keymap::normalize_keys("\\C-a"), "\x01");
790 assert_eq!(Keymap::normalize_keys("\\M-x"), "\x1bx");
791 }
792
793 #[test]
794 fn test_keymap_bind_lookup() {
795 let mut km = Keymap::new();
796 km.bind("^A", "beginning-of-line");
797
798 assert_eq!(km.lookup("^A"), Some("beginning-of-line"));
799 assert_eq!(km.lookup("\x01"), Some("beginning-of-line"));
800 }
801
802 #[test]
803 fn test_has_prefix() {
804 let mut km = Keymap::new();
805 km.bind("^X^U", "undo");
806
807 assert!(km.has_prefix("^X"));
808 assert!(!km.has_prefix("^X^U"));
809 assert!(!km.has_prefix("^A"));
810 }
811}