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