1use super::ZleState;
4
5#[derive(Debug, Clone)]
7pub enum WidgetResult {
8 Ok,
10 CallFunction(String),
12 Accept,
14 Abort,
16 Pending,
18 Ignored,
20 Error(String),
22 Refresh,
24 Clear,
26 TriggerCompletion,
28 MenuComplete,
30 ReverseMenuComplete,
32}
33
34#[derive(Debug, Clone)]
36pub enum Widget {
37 Builtin(BuiltinWidget),
39 User(String),
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub enum BuiltinWidget {
46 ForwardChar,
50 BackwardChar,
51 ForwardWord,
52 BackwardWord,
53 EmacsForwardWord,
54 EmacsBackwardWord,
55 BeginningOfLine,
56 EndOfLine,
57 ViBeginningOfLine,
58 ViEndOfLine,
59 ViFirstNonBlank,
60 ViForwardChar,
61 ViBackwardChar,
62 ViForwardWord,
63 ViBackwardWord,
64 ViForwardWordEnd,
65 ViBackwardWordEnd,
66 ViForwardBlankWord,
67 ViBackwardBlankWord,
68 ViForwardBlankWordEnd,
69 ViBackwardBlankWordEnd,
70 ViFindNextChar,
71 ViFindNextCharSkip,
72 ViFindPrevChar,
73 ViFindPrevCharSkip,
74 ViRepeatFind,
75 ViRevRepeatFind,
76 ViGotoColumn,
77 ViGotoMark,
78 ViGotoMarkLine,
79 UpLine,
80 DownLine,
81
82 UpLineOrHistory,
86 DownLineOrHistory,
87 ViUpLineOrHistory,
88 ViDownLineOrHistory,
89 UpLineOrSearch,
90 DownLineOrSearch,
91 UpHistory,
92 DownHistory,
93 BeginningOfHistory,
94 EndOfHistory,
95 BeginningOfBufferOrHistory,
96 EndOfBufferOrHistory,
97 BeginningOfLineHist,
98 EndOfLineHist,
99 ViFetchHistory,
100 HistoryIncrementalSearchBackward,
101 HistoryIncrementalSearchForward,
102 HistoryIncrementalPatternSearchBackward,
103 HistoryIncrementalPatternSearchForward,
104 HistorySearchBackward,
105 HistorySearchForward,
106 ViHistorySearchBackward,
107 ViHistorySearchForward,
108 HistoryBeginningSearchBackward,
109 HistoryBeginningSearchForward,
110 InferNextHistory,
111 InsertLastWord,
112 ViRepeatSearch,
113 ViRevRepeatSearch,
114 SetLocalHistory,
115
116 SelfInsert,
120 SelfInsertUnmeta,
121 QuotedInsert,
122 ViQuotedInsert,
123 DeleteChar,
124 BackwardDeleteChar,
125 ViDeleteChar,
126 ViBackwardDeleteChar,
127 DeleteWord,
128 BackwardDeleteWord,
129
130 KillLine,
134 BackwardKillLine,
135 ViKillLine,
136 ViKillEol,
137 KillWord,
138 BackwardKillWord,
139 ViBackwardKillWord,
140 KillWholeLine,
141 KillBuffer,
142 KillRegion,
143 CopyRegionAsKill,
144 CopyPrevWord,
145 CopyPrevShellWord,
146 Yank,
147 YankPop,
148 ViYank,
149 ViYankWholeLine,
150 ViYankEol,
151 ViPutBefore,
152 ViPutAfter,
153 PutReplaceSelection,
154
155 CapitalizeWord,
159 DownCaseWord,
160 UpCaseWord,
161 ViDownCase,
162 ViUpCase,
163 ViSwapCase,
164 ViOperSwapCase,
165
166 TransposeChars,
170 TransposeWords,
171 GosmacsTransposeChars,
172
173 ViChange,
177 ViChangeEol,
178 ViChangeWholeLine,
179 ViDelete,
180 ViIndent,
181 ViUnindent,
182 ViSubstitute,
183 ViAddNext,
184 ViAddEol,
185 ViInsert,
186 ViInsertBol,
187 ViOpenLineAbove,
188 ViOpenLineBelow,
189 ViReplace,
190 ViReplaceChars,
191 ViRepeatChange,
192 ViJoin,
193 ViMatchBracket,
194
195 Undo,
199 Redo,
200 ViUndoChange,
201 SplitUndo,
202
203 ExpandOrComplete,
207 ExpandOrCompletePrefix,
208 CompleteWord,
209 MenuComplete,
210 MenuExpandOrComplete,
211 ReverseMenuComplete,
212 AcceptAndMenuComplete,
213 DeleteCharOrList,
214 ExpandCmdPath,
215 ExpandHistory,
216 ExpandWord,
217 ListChoices,
218 ListExpand,
219 MagicSpace,
220 EndOfList,
221
222 AcceptLine,
226 AcceptAndHold,
227 AcceptAndInferNextHistory,
228 AcceptLineAndDownHistory,
229 SendBreak,
230
231 ViCmdMode,
235 ViCapsLockPanic,
236
237 DigitArgument,
241 NegArgument,
242 UniversalArgument,
243 ArgumentBase,
244 ViDigitOrBeginningOfLine,
245
246 SetMarkCommand,
250 ExchangePointAndMark,
251 ViSetMark,
252 ViSetBuffer,
253 DeactivateRegion,
254 VisualMode,
255 VisualLineMode,
256 SelectAWord,
257 SelectABlankWord,
258 SelectAShellWord,
259 SelectInWord,
260 SelectInBlankWord,
261 SelectInShellWord,
262
263 ClearScreen,
267 Redisplay,
268 ResetPrompt,
269 OverwriteMode,
270 UndefinedKey,
271 BracketedPaste,
272 PushLine,
273 PushLineOrEdit,
274 PushInput,
275 GetLine,
276 PoundInsert,
277 ViPoundInsert,
278 QuoteLine,
279 QuoteRegion,
280 ReadCommand,
281 RecursiveEdit,
282 RunHelp,
283 SpellWord,
284 WhatCursorPosition,
285 WhereIs,
286 WhichCommand,
287 ExecuteNamedCmd,
288 ExecuteLastNamedCmd,
289 DescribeKeyBriefly,
290 AutoSuffixRemove,
291 AutoSuffixRetain,
292
293 DeleteToChar,
297 ZapToChar,
298
299 ZleLineInit,
303 ZleLineFinish,
304 ZleLinePreRedraw,
305 ZleKeymapSelect,
306 ZleHistoryLineSet,
307 ZleIsearchUpdate,
308 ZleIsearchExit,
309}
310
311pub fn execute_builtin(
313 state: &mut ZleState,
314 widget: BuiltinWidget,
315 key: Option<char>,
316) -> WidgetResult {
317 state.save_undo();
318
319 match widget {
320 BuiltinWidget::ForwardChar => {
322 let n = state.numeric_arg.unwrap_or(1).unsigned_abs() as usize;
323 let chars: Vec<char> = state.buffer.chars().collect();
324 state.cursor = (state.cursor + n).min(chars.len());
325 state.numeric_arg = None;
326 WidgetResult::Ok
327 }
328 BuiltinWidget::BackwardChar => {
329 let n = state.numeric_arg.unwrap_or(1).unsigned_abs() as usize;
330 state.cursor = state.cursor.saturating_sub(n);
331 state.numeric_arg = None;
332 WidgetResult::Ok
333 }
334 BuiltinWidget::ForwardWord => {
335 let chars: Vec<char> = state.buffer.chars().collect();
336 let mut pos = state.cursor;
337 while pos < chars.len() && !chars[pos].is_alphanumeric() {
339 pos += 1;
340 }
341 while pos < chars.len() && chars[pos].is_alphanumeric() {
343 pos += 1;
344 }
345 state.cursor = pos;
346 WidgetResult::Ok
347 }
348 BuiltinWidget::BackwardWord => {
349 let chars: Vec<char> = state.buffer.chars().collect();
350 let mut pos = state.cursor;
351 while pos > 0 && !chars[pos.saturating_sub(1)].is_alphanumeric() {
353 pos -= 1;
354 }
355 while pos > 0 && chars[pos.saturating_sub(1)].is_alphanumeric() {
357 pos -= 1;
358 }
359 state.cursor = pos;
360 WidgetResult::Ok
361 }
362 BuiltinWidget::BeginningOfLine => {
363 state.cursor = 0;
364 WidgetResult::Ok
365 }
366 BuiltinWidget::EndOfLine => {
367 state.cursor = state.buffer.chars().count();
368 WidgetResult::Ok
369 }
370
371 BuiltinWidget::SelfInsert => {
373 if let Some(c) = key {
374 let chars: Vec<char> = state.buffer.chars().collect();
375 let mut new_buffer = String::new();
376 for (i, ch) in chars.iter().enumerate() {
377 if i == state.cursor {
378 new_buffer.push(c);
379 }
380 new_buffer.push(*ch);
381 }
382 if state.cursor >= chars.len() {
383 new_buffer.push(c);
384 }
385 state.buffer = new_buffer;
386 state.cursor += 1;
387 WidgetResult::Ok
388 } else {
389 WidgetResult::Ignored
390 }
391 }
392 BuiltinWidget::DeleteChar => {
393 let chars: Vec<char> = state.buffer.chars().collect();
394 if state.cursor < chars.len() {
395 let mut new_buffer = String::new();
396 for (i, ch) in chars.iter().enumerate() {
397 if i != state.cursor {
398 new_buffer.push(*ch);
399 }
400 }
401 state.buffer = new_buffer;
402 }
403 WidgetResult::Ok
404 }
405 BuiltinWidget::BackwardDeleteChar => {
406 if state.cursor > 0 {
407 let chars: Vec<char> = state.buffer.chars().collect();
408 let mut new_buffer = String::new();
409 for (i, ch) in chars.iter().enumerate() {
410 if i != state.cursor - 1 {
411 new_buffer.push(*ch);
412 }
413 }
414 state.buffer = new_buffer;
415 state.cursor -= 1;
416 }
417 WidgetResult::Ok
418 }
419 BuiltinWidget::KillLine => {
420 let chars: Vec<char> = state.buffer.chars().collect();
421 let killed: String = chars[state.cursor..].iter().collect();
422 state.kill_add(&killed);
423 state.buffer = chars[..state.cursor].iter().collect();
424 WidgetResult::Ok
425 }
426 BuiltinWidget::BackwardKillLine => {
427 let chars: Vec<char> = state.buffer.chars().collect();
428 let killed: String = chars[..state.cursor].iter().collect();
429 state.kill_add(&killed);
430 state.buffer = chars[state.cursor..].iter().collect();
431 state.cursor = 0;
432 WidgetResult::Ok
433 }
434 BuiltinWidget::KillWord => {
435 let chars: Vec<char> = state.buffer.chars().collect();
436 let mut end = state.cursor;
437 while end < chars.len() && !chars[end].is_alphanumeric() {
439 end += 1;
440 }
441 while end < chars.len() && chars[end].is_alphanumeric() {
443 end += 1;
444 }
445 let killed: String = chars[state.cursor..end].iter().collect();
446 state.kill_add(&killed);
447 let mut new_buffer: String = chars[..state.cursor].iter().collect();
448 new_buffer.push_str(&chars[end..].iter().collect::<String>());
449 state.buffer = new_buffer;
450 WidgetResult::Ok
451 }
452 BuiltinWidget::BackwardKillWord => {
453 let chars: Vec<char> = state.buffer.chars().collect();
454 let mut start = state.cursor;
455 while start > 0 && !chars[start.saturating_sub(1)].is_alphanumeric() {
457 start -= 1;
458 }
459 while start > 0 && chars[start.saturating_sub(1)].is_alphanumeric() {
461 start -= 1;
462 }
463 let killed: String = chars[start..state.cursor].iter().collect();
464 state.kill_add(&killed);
465 let mut new_buffer: String = chars[..start].iter().collect();
466 new_buffer.push_str(&chars[state.cursor..].iter().collect::<String>());
467 state.buffer = new_buffer;
468 state.cursor = start;
469 WidgetResult::Ok
470 }
471 BuiltinWidget::KillWholeLine => {
472 let buffer = state.buffer.clone();
473 state.kill_add(&buffer);
474 state.buffer.clear();
475 state.cursor = 0;
476 WidgetResult::Ok
477 }
478 BuiltinWidget::Yank => {
479 if let Some(text) = state.yank() {
480 let text = text.to_string();
481 let chars: Vec<char> = state.buffer.chars().collect();
482 let mut new_buffer: String = chars[..state.cursor].iter().collect();
483 new_buffer.push_str(&text);
484 new_buffer.push_str(&chars[state.cursor..].iter().collect::<String>());
485 state.cursor += text.chars().count();
486 state.buffer = new_buffer;
487 }
488 WidgetResult::Ok
489 }
490 BuiltinWidget::YankPop => {
491 if let Some(text) = state.yank_pop() {
492 let text = text.to_string();
493 let chars: Vec<char> = state.buffer.chars().collect();
495 let mut new_buffer: String = chars[..state.cursor].iter().collect();
496 new_buffer.push_str(&text);
497 new_buffer.push_str(&chars[state.cursor..].iter().collect::<String>());
498 state.cursor += text.chars().count();
499 state.buffer = new_buffer;
500 }
501 WidgetResult::Ok
502 }
503
504 BuiltinWidget::Undo => {
506 state.undo_stack.pop();
508 state.undo();
509 WidgetResult::Ok
510 }
511 BuiltinWidget::Redo => {
512 state.undo_stack.pop();
513 state.redo();
514 WidgetResult::Ok
515 }
516
517 BuiltinWidget::UpLineOrHistory => WidgetResult::Ok,
519 BuiltinWidget::DownLineOrHistory => WidgetResult::Ok,
520 BuiltinWidget::BeginningOfHistory => WidgetResult::Ok,
521 BuiltinWidget::EndOfHistory => WidgetResult::Ok,
522 BuiltinWidget::HistoryIncrementalSearchBackward => WidgetResult::Ok,
523 BuiltinWidget::HistoryIncrementalSearchForward => WidgetResult::Ok,
524
525 BuiltinWidget::ExpandOrComplete
527 | BuiltinWidget::ExpandOrCompletePrefix
528 | BuiltinWidget::CompleteWord
529 | BuiltinWidget::ExpandWord
530 | BuiltinWidget::ExpandCmdPath
531 | BuiltinWidget::ListChoices
532 | BuiltinWidget::ListExpand => WidgetResult::TriggerCompletion,
533 BuiltinWidget::MenuComplete
534 | BuiltinWidget::MenuExpandOrComplete
535 | BuiltinWidget::AcceptAndMenuComplete => WidgetResult::MenuComplete,
536 BuiltinWidget::ReverseMenuComplete => WidgetResult::ReverseMenuComplete,
537 BuiltinWidget::DeleteCharOrList => {
538 let chars: Vec<char> = state.buffer.chars().collect();
539 if state.cursor >= chars.len() {
540 WidgetResult::TriggerCompletion
541 } else {
542 let mut new_buffer = String::new();
543 for (i, ch) in chars.iter().enumerate() {
544 if i != state.cursor {
545 new_buffer.push(*ch);
546 }
547 }
548 state.buffer = new_buffer;
549 WidgetResult::Ok
550 }
551 }
552 BuiltinWidget::ExpandHistory | BuiltinWidget::MagicSpace | BuiltinWidget::EndOfList => {
553 WidgetResult::Ok
554 }
555
556 BuiltinWidget::AcceptLine => WidgetResult::Accept,
558 BuiltinWidget::AcceptAndHold
559 | BuiltinWidget::AcceptAndInferNextHistory
560 | BuiltinWidget::AcceptLineAndDownHistory => WidgetResult::Accept,
561 BuiltinWidget::SendBreak => WidgetResult::Abort,
562
563 BuiltinWidget::ClearScreen => WidgetResult::Clear,
565 BuiltinWidget::Redisplay => WidgetResult::Refresh,
566 BuiltinWidget::TransposeChars => {
567 let chars: Vec<char> = state.buffer.chars().collect();
568 if state.cursor > 0 && state.cursor < chars.len() {
569 let mut new_chars = chars.clone();
570 new_chars.swap(state.cursor - 1, state.cursor);
571 state.buffer = new_chars.iter().collect();
572 state.cursor += 1;
573 } else if state.cursor >= 2 && state.cursor == chars.len() {
574 let mut new_chars = chars.clone();
575 new_chars.swap(state.cursor - 2, state.cursor - 1);
576 state.buffer = new_chars.iter().collect();
577 }
578 WidgetResult::Ok
579 }
580 BuiltinWidget::TransposeWords => {
581 WidgetResult::Ok
583 }
584 BuiltinWidget::CapitalizeWord => {
585 let chars: Vec<char> = state.buffer.chars().collect();
586 let mut new_buffer = String::new();
587 let mut pos = state.cursor;
588 let mut first = true;
589
590 for ch in &chars[..pos] {
592 new_buffer.push(*ch);
593 }
594
595 while pos < chars.len() && !chars[pos].is_alphanumeric() {
597 new_buffer.push(chars[pos]);
598 pos += 1;
599 }
600
601 while pos < chars.len() && chars[pos].is_alphanumeric() {
603 if first {
604 new_buffer.extend(chars[pos].to_uppercase());
605 first = false;
606 } else {
607 new_buffer.extend(chars[pos].to_lowercase());
608 }
609 pos += 1;
610 }
611
612 for ch in &chars[pos..] {
614 new_buffer.push(*ch);
615 }
616
617 state.buffer = new_buffer;
618 state.cursor = pos;
619 WidgetResult::Ok
620 }
621 BuiltinWidget::DownCaseWord => {
622 let chars: Vec<char> = state.buffer.chars().collect();
623 let mut new_buffer = String::new();
624 let mut pos = state.cursor;
625
626 for ch in &chars[..pos] {
627 new_buffer.push(*ch);
628 }
629
630 while pos < chars.len() && !chars[pos].is_alphanumeric() {
631 new_buffer.push(chars[pos]);
632 pos += 1;
633 }
634
635 while pos < chars.len() && chars[pos].is_alphanumeric() {
636 new_buffer.extend(chars[pos].to_lowercase());
637 pos += 1;
638 }
639
640 for ch in &chars[pos..] {
641 new_buffer.push(*ch);
642 }
643
644 state.buffer = new_buffer;
645 state.cursor = pos;
646 WidgetResult::Ok
647 }
648 BuiltinWidget::UpCaseWord => {
649 let chars: Vec<char> = state.buffer.chars().collect();
650 let mut new_buffer = String::new();
651 let mut pos = state.cursor;
652
653 for ch in &chars[..pos] {
654 new_buffer.push(*ch);
655 }
656
657 while pos < chars.len() && !chars[pos].is_alphanumeric() {
658 new_buffer.push(chars[pos]);
659 pos += 1;
660 }
661
662 while pos < chars.len() && chars[pos].is_alphanumeric() {
663 new_buffer.extend(chars[pos].to_uppercase());
664 pos += 1;
665 }
666
667 for ch in &chars[pos..] {
668 new_buffer.push(*ch);
669 }
670
671 state.buffer = new_buffer;
672 state.cursor = pos;
673 WidgetResult::Ok
674 }
675 BuiltinWidget::QuotedInsert => {
676 WidgetResult::Pending
678 }
679 BuiltinWidget::ViCmdMode => {
680 state.vi_cmd_mode = true;
681 state.keymap = super::KeymapName::ViCommand;
682 WidgetResult::Ok
683 }
684 BuiltinWidget::ViInsert => {
685 state.vi_cmd_mode = false;
686 state.keymap = super::KeymapName::ViInsert;
687 WidgetResult::Ok
688 }
689 BuiltinWidget::SetMarkCommand => {
690 state.mark = state.cursor;
691 state.region_active = true;
692 WidgetResult::Ok
693 }
694 BuiltinWidget::ExchangePointAndMark => {
695 std::mem::swap(&mut state.cursor, &mut state.mark);
696 WidgetResult::Ok
697 }
698 BuiltinWidget::KillRegion => {
699 if state.region_active {
700 let (start, end) = if state.cursor < state.mark {
701 (state.cursor, state.mark)
702 } else {
703 (state.mark, state.cursor)
704 };
705 let chars: Vec<char> = state.buffer.chars().collect();
706 let killed: String = chars[start..end].iter().collect();
707 state.kill_add(&killed);
708 let mut new_buffer: String = chars[..start].iter().collect();
709 new_buffer.push_str(&chars[end..].iter().collect::<String>());
710 state.buffer = new_buffer;
711 state.cursor = start;
712 state.region_active = false;
713 }
714 WidgetResult::Ok
715 }
716 BuiltinWidget::CopyRegionAsKill => {
717 if state.region_active {
718 let (start, end) = if state.cursor < state.mark {
719 (state.cursor, state.mark)
720 } else {
721 (state.mark, state.cursor)
722 };
723 let chars: Vec<char> = state.buffer.chars().collect();
724 let copied: String = chars[start..end].iter().collect();
725 state.kill_add(&copied);
726 state.region_active = false;
727 }
728 WidgetResult::Ok
729 }
730
731 BuiltinWidget::DeleteToChar | BuiltinWidget::ZapToChar => {
733 WidgetResult::Pending
736 }
737
738 _ => WidgetResult::Ok,
740 }
741}
742
743pub fn delete_to_char(state: &mut ZleState, target: char, count: i32, zap: bool) -> WidgetResult {
746 state.save_undo();
747 let chars: Vec<char> = state.buffer.chars().collect();
748 let mut dest = state.cursor;
749
750 if count > 0 {
751 let mut remaining = count;
752 while remaining > 0 && dest < chars.len() {
753 while dest < chars.len() && chars[dest] != target {
754 dest += 1;
755 }
756 if dest < chars.len() {
757 if !zap || remaining > 1 {
758 dest += 1;
759 }
760 remaining -= 1;
761 if remaining == 0 {
762 let killed: String = chars[state.cursor..dest].iter().collect();
763 state.kill_add(&killed);
764 let mut new_buffer: String = chars[..state.cursor].iter().collect();
765 new_buffer.push_str(&chars[dest..].iter().collect::<String>());
766 state.buffer = new_buffer;
767 return WidgetResult::Ok;
768 }
769 }
770 }
771 } else {
772 if dest > 0 {
773 dest -= 1;
774 }
775 let mut remaining = -count;
776 while remaining > 0 && dest > 0 {
777 while dest > 0 && chars[dest] != target {
778 dest -= 1;
779 }
780 if chars[dest] == target {
781 remaining -= 1;
782 if remaining == 0 {
783 let adjust = if zap { 1 } else { 0 };
784 let killed: String = chars[dest + adjust..state.cursor].iter().collect();
785 state.kill_add(&killed);
786 let mut new_buffer: String = chars[..dest + adjust].iter().collect();
787 new_buffer.push_str(&chars[state.cursor..].iter().collect::<String>());
788 state.buffer = new_buffer;
789 state.cursor = dest + adjust;
790 return WidgetResult::Ok;
791 }
792 if dest > 0 {
793 dest -= 1;
794 }
795 }
796 }
797 }
798
799 WidgetResult::Ok
800}