1use log::debug;
4use std::fmt;
5use unicode_segmentation::UnicodeSegmentation;
6use unicode_width::UnicodeWidthChar;
7
8use super::{Context, Helper, Result};
9use crate::error::ReadlineError;
10use crate::highlight::{CmdKind, Highlighter};
11use crate::hint::Hint;
12use crate::history::SearchDirection;
13use crate::keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word};
14use crate::keymap::{InputState, Invoke, Refresher};
15use crate::layout::{Layout, Position};
16use crate::line_buffer::{
17 ChangeListener, DeleteListener, Direction, LineBuffer, NoListener, WordAction, MAX_LINE,
18};
19use crate::tty::{Renderer, Term, Terminal};
20use crate::undo::Changeset;
21use crate::validate::{ValidationContext, ValidationResult};
22use crate::KillRing;
23
24pub struct State<'out, 'prompt, H: Helper> {
27 pub out: &'out mut <Terminal as Term>::Writer,
28 prompt: &'prompt str, prompt_size: Position, pub line: LineBuffer, pub layout: Layout,
32 saved_line_for_history: LineBuffer, byte_buffer: [u8; 4],
34 pub changes: Changeset, pub helper: Option<&'out H>,
36 pub ctx: Context<'out>, pub hint: Option<Box<dyn Hint>>, pub highlight_char: bool, }
40
41enum Info<'m> {
42 NoHint,
43 Hint,
44 Msg(Option<&'m str>),
45}
46
47impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
48 pub fn new(
49 out: &'out mut <Terminal as Term>::Writer,
50 prompt: &'prompt str,
51 helper: Option<&'out H>,
52 ctx: Context<'out>,
53 ) -> Self {
54 let prompt_size = out.calculate_position(prompt, Position::default());
55 Self {
56 out,
57 prompt,
58 prompt_size,
59 line: LineBuffer::with_capacity(MAX_LINE).can_growth(true),
60 layout: Layout::default(),
61 saved_line_for_history: LineBuffer::with_capacity(MAX_LINE).can_growth(true),
62 byte_buffer: [0; 4],
63 changes: Changeset::new(),
64 helper,
65 ctx,
66 hint: None,
67 highlight_char: false,
68 }
69 }
70
71 pub fn highlighter(&self) -> Option<&dyn Highlighter> {
72 if self.out.colors_enabled() {
73 self.helper.map(|h| h as &dyn Highlighter)
74 } else {
75 None
76 }
77 }
78
79 pub fn next_cmd(
80 &mut self,
81 input_state: &mut InputState,
82 rdr: &mut <Terminal as Term>::Reader,
83 single_esc_abort: bool,
84 ignore_external_print: bool,
85 ) -> Result<Cmd> {
86 loop {
87 let rc = input_state.next_cmd(rdr, self, single_esc_abort, ignore_external_print);
88 if let Err(ReadlineError::WindowResized) = rc {
89 debug!(target: "rustyline", "SIGWINCH");
90 let old_cols = self.out.get_columns();
91 self.out.update_size();
92 let new_cols = self.out.get_columns();
93 if new_cols != old_cols
94 && (self.layout.end.row > 0 || self.layout.end.col >= new_cols)
95 {
96 self.prompt_size = self
97 .out
98 .calculate_position(self.prompt, Position::default());
99 self.refresh_line()?;
100 }
101 continue;
102 }
103 if let Ok(Cmd::Replace(..)) = rc {
104 self.changes.begin();
105 }
106 return rc;
107 }
108 }
109
110 pub fn backup(&mut self) {
111 self.saved_line_for_history
112 .update(self.line.as_str(), self.line.pos(), &mut NoListener);
113 }
114
115 pub fn restore(&mut self) {
116 self.line.update(
117 self.saved_line_for_history.as_str(),
118 self.saved_line_for_history.pos(),
119 &mut self.changes,
120 );
121 }
122
123 pub fn move_cursor(&mut self, kind: CmdKind) -> Result<()> {
124 let cursor = self
126 .out
127 .calculate_position(&self.line[..self.line.pos()], self.prompt_size);
128 if self.layout.cursor == cursor {
129 return Ok(());
130 }
131 if self.highlight_char(kind) {
132 let prompt_size = self.prompt_size;
133 self.refresh(self.prompt, prompt_size, true, Info::NoHint)?;
134 } else {
135 self.out.move_cursor(self.layout.cursor, cursor)?;
136 self.layout.prompt_size = self.prompt_size;
137 self.layout.cursor = cursor;
138 debug_assert!(self.layout.prompt_size <= self.layout.cursor);
139 debug_assert!(self.layout.cursor <= self.layout.end);
140 }
141 Ok(())
142 }
143
144 pub fn move_cursor_to_end(&mut self) -> Result<()> {
145 if self.layout.cursor == self.layout.end {
146 return Ok(());
147 }
148 self.out.move_cursor(self.layout.cursor, self.layout.end)?;
149 self.layout.cursor = self.layout.end;
150 Ok(())
151 }
152
153 pub fn move_cursor_at_leftmost(&mut self, rdr: &mut <Terminal as Term>::Reader) -> Result<()> {
154 self.out.move_cursor_at_leftmost(rdr)
155 }
156
157 fn refresh(
158 &mut self,
159 prompt: &str,
160 prompt_size: Position,
161 default_prompt: bool,
162 info: Info<'_>,
163 ) -> Result<()> {
164 let info = match info {
165 Info::NoHint => None,
166 Info::Hint => self.hint.as_ref().map(|h| h.display()),
167 Info::Msg(msg) => msg,
168 };
169 let highlighter = if self.out.colors_enabled() {
170 self.helper.map(|h| h as &dyn Highlighter)
171 } else {
172 None
173 };
174
175 let new_layout = self
176 .out
177 .compute_layout(prompt_size, default_prompt, &self.line, info);
178
179 debug!(target: "rustyline", "old layout: {:?}", self.layout);
180 debug!(target: "rustyline", "new layout: {:?}", new_layout);
181 self.out.refresh_line(
182 prompt,
183 &self.line,
184 info,
185 &self.layout,
186 &new_layout,
187 highlighter,
188 )?;
189 self.layout = new_layout;
190
191 Ok(())
192 }
193
194 pub fn hint(&mut self) {
195 if let Some(hinter) = self.helper {
196 let hint = hinter.hint(self.line.as_str(), self.line.pos(), &self.ctx);
197 self.hint = match hint {
198 Some(val) if !val.display().is_empty() => Some(Box::new(val) as Box<dyn Hint>),
199 _ => None,
200 };
201 } else {
202 self.hint = None;
203 }
204 }
205
206 fn highlight_char(&mut self, kind: CmdKind) -> bool {
207 if let Some(highlighter) = self.highlighter() {
208 let highlight_char = highlighter.highlight_char(&self.line, self.line.pos(), kind);
209 if highlight_char {
210 self.highlight_char = true;
211 true
212 } else if self.highlight_char {
213 self.highlight_char = false;
215 true
216 } else {
217 false
218 }
219 } else {
220 false
221 }
222 }
223
224 pub fn is_default_prompt(&self) -> bool {
225 self.layout.default_prompt
226 }
227
228 pub fn validate(&mut self) -> Result<ValidationResult> {
229 if let Some(validator) = self.helper {
230 self.changes.begin();
231 let result = validator.validate(&mut ValidationContext::new(self))?;
232 let corrected = self.changes.end();
233 match result {
234 ValidationResult::Incomplete => {}
235 ValidationResult::Valid(ref msg) => {
236 if corrected || self.has_hint() || msg.is_some() {
238 self.refresh_line_with_msg(msg.as_deref(), CmdKind::ForcedRefresh)?;
241 }
242 }
243 ValidationResult::Invalid(ref msg) => {
244 if corrected || self.has_hint() || msg.is_some() {
245 self.refresh_line_with_msg(msg.as_deref(), CmdKind::Other)?;
246 }
247 }
248 }
249 Ok(result)
250 } else {
251 Ok(ValidationResult::Valid(None))
252 }
253 }
254}
255
256impl<H: Helper> Invoke for State<'_, '_, H> {
257 fn input(&self) -> &str {
258 self.line.as_str()
259 }
260}
261
262impl<H: Helper> Refresher for State<'_, '_, H> {
263 fn refresh_line(&mut self) -> Result<()> {
264 let prompt_size = self.prompt_size;
265 self.hint();
266 self.highlight_char(CmdKind::Other);
267 self.refresh(self.prompt, prompt_size, true, Info::Hint)
268 }
269
270 fn refresh_line_with_msg(&mut self, msg: Option<&str>, kind: CmdKind) -> Result<()> {
271 let prompt_size = self.prompt_size;
272 self.hint = None;
273 self.highlight_char(kind);
274 self.refresh(self.prompt, prompt_size, true, Info::Msg(msg))
275 }
276
277 fn refresh_prompt_and_line(&mut self, prompt: &str) -> Result<()> {
278 let prompt_size = self.out.calculate_position(prompt, Position::default());
279 self.hint();
280 self.highlight_char(CmdKind::Other);
281 self.refresh(prompt, prompt_size, false, Info::Hint)
282 }
283
284 fn doing_insert(&mut self) {
285 self.changes.begin();
286 }
287
288 fn done_inserting(&mut self) {
289 self.changes.end();
290 }
291
292 fn last_insert(&self) -> Option<String> {
293 self.changes.last_insert()
294 }
295
296 fn is_cursor_at_end(&self) -> bool {
297 self.line.pos() == self.line.len()
298 }
299
300 fn has_hint(&self) -> bool {
301 self.hint.is_some()
302 }
303
304 fn hint_text(&self) -> Option<&str> {
305 self.hint.as_ref().and_then(|hint| hint.completion())
306 }
307
308 fn line(&self) -> &str {
309 self.line.as_str()
310 }
311
312 fn pos(&self) -> usize {
313 self.line.pos()
314 }
315
316 fn external_print(&mut self, msg: String) -> Result<()> {
317 self.out.clear_rows(&self.layout)?;
318 self.layout.end.row = 0;
319 self.layout.cursor.row = 0;
320 self.out.write_and_flush(msg.as_str())?;
321 if !msg.ends_with('\n') {
322 self.out.write_and_flush("\n")?;
323 }
324 self.refresh_line()
325 }
326}
327
328impl<H: Helper> fmt::Debug for State<'_, '_, H> {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 f.debug_struct("State")
331 .field("prompt", &self.prompt)
332 .field("prompt_size", &self.prompt_size)
333 .field("buf", &self.line)
334 .field("cols", &self.out.get_columns())
335 .field("layout", &self.layout)
336 .field("saved_line_for_history", &self.saved_line_for_history)
337 .finish()
338 }
339}
340
341impl<H: Helper> State<'_, '_, H> {
342 pub fn clear_screen(&mut self) -> Result<()> {
343 self.out.clear_screen()?;
344 self.layout.cursor = Position::default();
345 self.layout.end = Position::default();
346 Ok(())
347 }
348
349 pub fn edit_insert(&mut self, ch: char, n: RepeatCount) -> Result<()> {
351 if let Some(push) = self.line.insert(ch, n, &mut self.changes) {
352 if push {
353 let prompt_size = self.prompt_size;
354 let no_previous_hint = self.hint.is_none();
355 self.hint();
356 let width = ch.width().unwrap_or(0);
357 if n == 1
358 && width != 0 && self.layout.cursor.col + width < self.out.get_columns()
360 && (self.hint.is_none() && no_previous_hint) && !self.highlight_char(CmdKind::Other)
362 {
363 self.layout.cursor.col += width;
365 self.layout.end.col += width;
366 debug_assert!(self.layout.prompt_size <= self.layout.cursor);
367 debug_assert!(self.layout.cursor <= self.layout.end);
368 let bits = ch.encode_utf8(&mut self.byte_buffer);
369 self.out.write_and_flush(bits)
370 } else {
371 self.refresh(self.prompt, prompt_size, true, Info::Hint)
372 }
373 } else {
374 self.refresh_line()
375 }
376 } else {
377 Ok(())
378 }
379 }
380
381 pub fn edit_replace_char(&mut self, ch: char, n: RepeatCount) -> Result<()> {
383 self.changes.begin();
384 let succeed = if let Some(chars) = self.line.delete(n, &mut self.changes) {
385 let count = chars.graphemes(true).count();
386 self.line.insert(ch, count, &mut self.changes);
387 self.line.move_backward(1);
388 true
389 } else {
390 false
391 };
392 self.changes.end();
393 if succeed {
394 self.refresh_line()
395 } else {
396 Ok(())
397 }
398 }
399
400 pub fn edit_overwrite_char(&mut self, ch: char) -> Result<()> {
402 if let Some(end) = self.line.next_pos(1) {
403 {
404 let text = ch.encode_utf8(&mut self.byte_buffer);
405 let start = self.line.pos();
406 self.line.replace(start..end, text, &mut self.changes);
407 }
408 self.refresh_line()
409 } else {
410 Ok(())
411 }
412 }
413
414 pub fn edit_yank(
416 &mut self,
417 input_state: &InputState,
418 text: &str,
419 anchor: Anchor,
420 n: RepeatCount,
421 ) -> Result<()> {
422 if let Anchor::After = anchor {
423 self.line.move_forward(1);
424 }
425 if self.line.yank(text, n, &mut self.changes).is_some() {
426 if !input_state.is_emacs_mode() {
427 self.line.move_backward(1);
428 }
429 self.refresh_line()
430 } else {
431 Ok(())
432 }
433 }
434
435 pub fn edit_yank_pop(&mut self, yank_size: usize, text: &str) -> Result<()> {
437 self.changes.begin();
438 let result = if self
439 .line
440 .yank_pop(yank_size, text, &mut self.changes)
441 .is_some()
442 {
443 self.refresh_line()
444 } else {
445 Ok(())
446 };
447 self.changes.end();
448 result
449 }
450
451 pub fn edit_move_backward(&mut self, n: RepeatCount) -> Result<()> {
453 if self.line.move_backward(n) {
454 self.move_cursor(CmdKind::MoveCursor)
455 } else {
456 Ok(())
457 }
458 }
459
460 pub fn edit_move_forward(&mut self, n: RepeatCount) -> Result<()> {
462 if self.line.move_forward(n) {
463 self.move_cursor(CmdKind::MoveCursor)
464 } else {
465 Ok(())
466 }
467 }
468
469 pub fn edit_move_home(&mut self) -> Result<()> {
471 if self.line.move_home() {
472 self.move_cursor(CmdKind::MoveCursor)
473 } else {
474 Ok(())
475 }
476 }
477
478 pub fn edit_move_end(&mut self) -> Result<()> {
480 if self.line.move_end() {
481 self.move_cursor(CmdKind::MoveCursor)
482 } else {
483 Ok(())
484 }
485 }
486
487 pub fn edit_move_buffer_start(&mut self) -> Result<()> {
489 if self.line.move_buffer_start() {
490 self.move_cursor(CmdKind::MoveCursor)
491 } else {
492 Ok(())
493 }
494 }
495
496 pub fn edit_move_buffer_end(&mut self, kind: CmdKind) -> Result<()> {
498 if self.line.move_buffer_end() {
499 self.move_cursor(kind)
500 } else {
501 Ok(())
502 }
503 }
504
505 pub fn edit_kill(&mut self, mvt: &Movement, kill_ring: &mut KillRing) -> Result<()> {
506 struct Proxy<'p>(&'p mut Changeset, &'p mut KillRing);
507 let mut proxy = Proxy(&mut self.changes, kill_ring);
508 impl DeleteListener for Proxy<'_> {
509 fn start_killing(&mut self) {
510 self.1.start_killing();
511 }
512
513 fn delete(&mut self, idx: usize, string: &str, dir: Direction) {
514 self.0.delete(idx, string);
515 self.1.delete(idx, string, dir);
516 }
517
518 fn stop_killing(&mut self) {
519 self.1.stop_killing()
520 }
521 }
522 impl ChangeListener for Proxy<'_> {
523 fn insert_char(&mut self, idx: usize, c: char) {
524 self.0.insert_char(idx, c)
525 }
526
527 fn insert_str(&mut self, idx: usize, string: &str) {
528 self.0.insert_str(idx, string)
529 }
530
531 fn replace(&mut self, idx: usize, old: &str, new: &str) {
532 self.0.replace(idx, old, new)
533 }
534 }
535 if self.line.kill(mvt, &mut proxy) {
536 self.refresh_line()
537 } else {
538 Ok(())
539 }
540 }
541
542 pub fn edit_insert_text(&mut self, text: &str) -> Result<()> {
543 if text.is_empty() {
544 return Ok(());
545 }
546 let cursor = self.line.pos();
547 self.line.insert_str(cursor, text, &mut self.changes);
548 self.refresh_line()
549 }
550
551 pub fn edit_transpose_chars(&mut self) -> Result<()> {
553 self.changes.begin();
554 let succeed = self.line.transpose_chars(&mut self.changes);
555 self.changes.end();
556 if succeed {
557 self.refresh_line()
558 } else {
559 Ok(())
560 }
561 }
562
563 pub fn edit_move_to_prev_word(&mut self, word_def: Word, n: RepeatCount) -> Result<()> {
564 if self.line.move_to_prev_word(word_def, n) {
565 self.move_cursor(CmdKind::MoveCursor)
566 } else {
567 Ok(())
568 }
569 }
570
571 pub fn edit_move_to_next_word(&mut self, at: At, word_def: Word, n: RepeatCount) -> Result<()> {
572 if self.line.move_to_next_word(at, word_def, n) {
573 self.move_cursor(CmdKind::MoveCursor)
574 } else {
575 Ok(())
576 }
577 }
578
579 pub fn edit_move_line_up(&mut self, n: RepeatCount) -> Result<bool> {
581 if self.line.move_to_line_up(n) {
582 self.move_cursor(CmdKind::MoveCursor)?;
583 Ok(true)
584 } else {
585 Ok(false)
586 }
587 }
588
589 pub fn edit_move_line_down(&mut self, n: RepeatCount) -> Result<bool> {
591 if self.line.move_to_line_down(n) {
592 self.move_cursor(CmdKind::MoveCursor)?;
593 Ok(true)
594 } else {
595 Ok(false)
596 }
597 }
598
599 pub fn edit_move_to(&mut self, cs: CharSearch, n: RepeatCount) -> Result<()> {
600 if self.line.move_to(cs, n) {
601 self.move_cursor(CmdKind::MoveCursor)
602 } else {
603 Ok(())
604 }
605 }
606
607 pub fn edit_word(&mut self, a: WordAction) -> Result<()> {
608 self.changes.begin();
609 let succeed = self.line.edit_word(a, &mut self.changes);
610 self.changes.end();
611 if succeed {
612 self.refresh_line()
613 } else {
614 Ok(())
615 }
616 }
617
618 pub fn edit_transpose_words(&mut self, n: RepeatCount) -> Result<()> {
619 self.changes.begin();
620 let succeed = self.line.transpose_words(n, &mut self.changes);
621 self.changes.end();
622 if succeed {
623 self.refresh_line()
624 } else {
625 Ok(())
626 }
627 }
628
629 pub fn edit_history_next(&mut self, prev: bool) -> Result<()> {
632 let history = self.ctx.history;
633 if history.is_empty() {
634 return Ok(());
635 }
636 if self.ctx.history_index == history.len() {
637 if prev {
638 self.backup();
640 } else {
641 return Ok(());
642 }
643 } else if self.ctx.history_index == 0 && prev {
644 return Ok(());
645 }
646 let (idx, dir) = if prev {
647 (self.ctx.history_index - 1, SearchDirection::Reverse)
648 } else {
649 self.ctx.history_index += 1;
650 (self.ctx.history_index, SearchDirection::Forward)
651 };
652 if idx < history.len() {
653 if let Some(r) = history.get(idx, dir)? {
654 let buf = r.entry;
655 self.ctx.history_index = r.idx;
656 self.changes.begin();
657 self.line.update(&buf, buf.len(), &mut self.changes);
658 self.changes.end();
659 } else {
660 return Ok(());
661 }
662 } else {
663 self.restore();
665 }
666 self.refresh_line()
667 }
668
669 pub fn edit_history_search(&mut self, dir: SearchDirection) -> Result<()> {
671 let history = self.ctx.history;
672 if history.is_empty() {
673 return self.out.beep();
674 }
675 if self.ctx.history_index == history.len() && dir == SearchDirection::Forward
676 || self.ctx.history_index == 0 && dir == SearchDirection::Reverse
677 {
678 return self.out.beep();
679 }
680 if dir == SearchDirection::Reverse {
681 self.ctx.history_index -= 1;
682 } else {
683 self.ctx.history_index += 1;
684 }
685 if let Some(sr) = history.starts_with(
686 &self.line.as_str()[..self.line.pos()],
687 self.ctx.history_index,
688 dir,
689 )? {
690 self.ctx.history_index = sr.idx;
691 self.changes.begin();
692 self.line.update(&sr.entry, sr.pos, &mut self.changes);
693 self.changes.end();
694 self.refresh_line()
695 } else {
696 self.out.beep()
697 }
698 }
699
700 pub fn edit_history(&mut self, first: bool) -> Result<()> {
702 let history = self.ctx.history;
703 if history.is_empty() {
704 return Ok(());
705 }
706 if self.ctx.history_index == history.len() {
707 if first {
708 self.backup();
710 } else {
711 return Ok(());
712 }
713 } else if self.ctx.history_index == 0 && first {
714 return Ok(());
715 }
716 if first {
717 if let Some(r) = history.get(0, SearchDirection::Forward)? {
718 let buf = r.entry;
719 self.ctx.history_index = r.idx;
720 self.changes.begin();
721 self.line.update(&buf, buf.len(), &mut self.changes);
722 self.changes.end();
723 } else {
724 return Ok(());
725 }
726 } else {
727 self.ctx.history_index = history.len();
728 self.restore();
730 }
731 self.refresh_line()
732 }
733
734 pub fn edit_indent(&mut self, mvt: &Movement, amount: usize, dedent: bool) -> Result<()> {
736 if self.line.indent(mvt, amount, dedent, &mut self.changes) {
737 self.refresh_line()
738 } else {
739 Ok(())
740 }
741 }
742}
743
744#[cfg(test)]
745pub fn init_state<'out, H: Helper>(
746 out: &'out mut <Terminal as Term>::Writer,
747 line: &str,
748 pos: usize,
749 helper: Option<&'out H>,
750 history: &'out crate::history::DefaultHistory,
751) -> State<'out, 'static, H> {
752 State {
753 out,
754 prompt: "",
755 prompt_size: Position::default(),
756 line: LineBuffer::init(line, pos),
757 layout: Layout::default(),
758 saved_line_for_history: LineBuffer::with_capacity(100),
759 byte_buffer: [0; 4],
760 changes: Changeset::new(),
761 helper,
762 ctx: Context::new(history),
763 hint: Some(Box::new("hint".to_owned())),
764 highlight_char: false,
765 }
766}
767
768#[cfg(test)]
769mod test {
770 use super::init_state;
771 use crate::history::{DefaultHistory, History};
772 use crate::tty::Sink;
773
774 #[test]
775 fn edit_history_next() {
776 let mut out = Sink::default();
777 let mut history = DefaultHistory::new();
778 history.add("line0").unwrap();
779 history.add("line1").unwrap();
780 let line = "current edited line";
781 let helper: Option<()> = None;
782 let mut s = init_state(&mut out, line, 6, helper.as_ref(), &history);
783 s.ctx.history_index = history.len();
784
785 for _ in 0..2 {
786 s.edit_history_next(false).unwrap();
787 assert_eq!(line, s.line.as_str());
788 }
789
790 s.edit_history_next(true).unwrap();
791 assert_eq!(line, s.saved_line_for_history.as_str());
792 assert_eq!(1, s.ctx.history_index);
793 assert_eq!("line1", s.line.as_str());
794
795 for _ in 0..2 {
796 s.edit_history_next(true).unwrap();
797 assert_eq!(line, s.saved_line_for_history.as_str());
798 assert_eq!(0, s.ctx.history_index);
799 assert_eq!("line0", s.line.as_str());
800 }
801
802 s.edit_history_next(false).unwrap();
803 assert_eq!(line, s.saved_line_for_history.as_str());
804 assert_eq!(1, s.ctx.history_index);
805 assert_eq!("line1", s.line.as_str());
806
807 s.edit_history_next(false).unwrap();
808 assert_eq!(2, s.ctx.history_index);
810 assert_eq!(line, s.line.as_str());
811 }
812}