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