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