rustyline/
edit.rs

1//! Command processor
2
3use std::cell::RefCell;
4use std::fmt;
5use std::rc::Rc;
6use unicode_segmentation::UnicodeSegmentation;
7use unicode_width::UnicodeWidthChar;
8
9use super::Result;
10use highlight::Highlighter;
11use hint::Hinter;
12use history::{Direction, History};
13use keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word};
14use keymap::{InputState, Refresher};
15use line_buffer::{LineBuffer, WordAction, MAX_LINE};
16use tty::{Position, RawReader, Renderer};
17use undo::Changeset;
18
19/// Represent the state during line editing.
20/// Implement rendering.
21pub struct State<'out, 'prompt> {
22    pub out: &'out mut dyn Renderer,
23    prompt: &'prompt str,  // Prompt to display (rl_prompt)
24    prompt_size: Position, // Prompt Unicode/visible width and height
25    pub line: LineBuffer,  // Edited line buffer
26    pub cursor: Position,  /* Cursor position (relative to the start of the prompt
27                            * for `row`) */
28    pub old_rows: usize, // Number of rows used so far (from start of prompt to end of input)
29    history_index: usize, // The history index we are currently editing
30    saved_line_for_history: LineBuffer, // Current edited line before history browsing
31    byte_buffer: [u8; 4],
32    pub changes: Rc<RefCell<Changeset>>, // changes to line, for undo/redo
33    pub hinter: Option<&'out dyn Hinter>,
34    pub highlighter: Option<&'out dyn Highlighter>,
35    no_hint: bool, // `false` if an hint has been displayed
36}
37
38impl<'out, 'prompt> State<'out, 'prompt> {
39    pub fn new(
40        out: &'out mut dyn Renderer,
41        prompt: &'prompt str,
42        history_index: usize,
43        hinter: Option<&'out dyn Hinter>,
44        highlighter: Option<&'out dyn Highlighter>,
45    ) -> State<'out, 'prompt> {
46        let capacity = MAX_LINE;
47        let prompt_size = out.calculate_position(prompt, Position::default());
48        State {
49            out,
50            prompt,
51            prompt_size,
52            line: LineBuffer::with_capacity(capacity),
53            cursor: prompt_size,
54            old_rows: 0,
55            history_index,
56            saved_line_for_history: LineBuffer::with_capacity(capacity),
57            byte_buffer: [0; 4],
58            changes: Rc::new(RefCell::new(Changeset::new())),
59            hinter,
60            highlighter,
61            no_hint: true,
62        }
63    }
64
65    pub fn next_cmd<R: RawReader>(
66        &mut self,
67        input_state: &mut InputState,
68        rdr: &mut R,
69        single_esc_abort: bool,
70    ) -> Result<Cmd> {
71        loop {
72            let rc = input_state.next_cmd(rdr, self, single_esc_abort);
73            if rc.is_err() && self.out.sigwinch() {
74                self.out.update_size();
75                try!(self.refresh_line());
76                continue;
77            }
78            if let Ok(Cmd::Replace(_, _)) = rc {
79                self.changes.borrow_mut().begin();
80            }
81            return rc;
82        }
83    }
84
85    pub fn backup(&mut self) {
86        self.saved_line_for_history
87            .update(self.line.as_str(), self.line.pos());
88    }
89
90    pub fn restore(&mut self) {
91        self.line.update(
92            self.saved_line_for_history.as_str(),
93            self.saved_line_for_history.pos(),
94        );
95    }
96
97    pub fn move_cursor(&mut self) -> Result<()> {
98        // calculate the desired position of the cursor
99        let cursor = self
100            .out
101            .calculate_position(&self.line[..self.line.pos()], self.prompt_size);
102        if self.cursor == cursor {
103            return Ok(());
104        }
105        if self.highlighter.map_or(false, |h| {
106            self.line
107                .grapheme_at_cursor()
108                .map_or(false, |s| h.highlight_char(s))
109        }) {
110            let prompt_size = self.prompt_size;
111            try!(self.refresh(self.prompt, prompt_size, None));
112        } else {
113            try!(self.out.move_cursor(self.cursor, cursor));
114        }
115        self.cursor = cursor;
116        Ok(())
117    }
118
119    fn refresh(&mut self, prompt: &str, prompt_size: Position, hint: Option<String>) -> Result<()> {
120        let (cursor, end_pos) = try!(self.out.refresh_line(
121            prompt,
122            prompt_size,
123            &self.line,
124            hint,
125            self.cursor.row,
126            self.old_rows,
127            self.highlighter,
128        ));
129
130        self.cursor = cursor;
131        self.old_rows = end_pos.row;
132        Ok(())
133    }
134
135    fn hint(&mut self) -> Option<String> {
136        if let Some(hinter) = self.hinter {
137            self.no_hint = false;
138            hinter.hint(self.line.as_str(), self.line.pos())
139        } else {
140            self.no_hint = true;
141            None
142        }
143    }
144}
145
146impl<'out, 'prompt> Refresher for State<'out, 'prompt> {
147    fn refresh_line(&mut self) -> Result<()> {
148        let prompt_size = self.prompt_size;
149        let hint = self.hint();
150        self.refresh(self.prompt, prompt_size, hint)
151    }
152
153    fn refresh_prompt_and_line(&mut self, prompt: &str) -> Result<()> {
154        let prompt_size = self.out.calculate_position(prompt, Position::default());
155        let hint = self.hint();
156        self.refresh(prompt, prompt_size, hint)
157    }
158
159    fn doing_insert(&mut self) {
160        self.changes.borrow_mut().begin();
161    }
162
163    fn done_inserting(&mut self) {
164        self.changes.borrow_mut().end();
165    }
166
167    fn last_insert(&self) -> Option<String> {
168        self.changes.borrow().last_insert()
169    }
170}
171
172impl<'out, 'prompt> fmt::Debug for State<'out, 'prompt> {
173    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174        f.debug_struct("State")
175            .field("prompt", &self.prompt)
176            .field("prompt_size", &self.prompt_size)
177            .field("buf", &self.line)
178            .field("cursor", &self.cursor)
179            .field("cols", &self.out.get_columns())
180            .field("old_rows", &self.old_rows)
181            .field("history_index", &self.history_index)
182            .field("saved_line_for_history", &self.saved_line_for_history)
183            .finish()
184    }
185}
186
187impl<'out, 'prompt> State<'out, 'prompt> {
188    /// Insert the character `ch` at cursor current position.
189    pub fn edit_insert(&mut self, ch: char, n: RepeatCount) -> Result<()> {
190        if let Some(push) = self.line.insert(ch, n) {
191            if push {
192                let prompt_size = self.prompt_size;
193                let no_previous_hint = self.no_hint;
194                let hint = self.hint();
195                if n == 1
196                    && self.cursor.col + ch.width().unwrap_or(0) < self.out.get_columns()
197                    && (hint.is_none() && no_previous_hint) // TODO refresh only current line
198                    && !self.highlighter.map_or(true, |h| h.highlight_char(ch.encode_utf8(&mut self.byte_buffer)))
199                {
200                    // Avoid a full update of the line in the trivial case.
201                    let cursor = self
202                        .out
203                        .calculate_position(&self.line[..self.line.pos()], self.prompt_size);
204                    self.cursor = cursor;
205                    let bits = ch.encode_utf8(&mut self.byte_buffer);
206                    let bits = bits.as_bytes();
207                    self.out.write_and_flush(bits)
208                } else {
209                    self.refresh(self.prompt, prompt_size, hint)
210                }
211            } else {
212                self.refresh_line()
213            }
214        } else {
215            Ok(())
216        }
217    }
218
219    /// Replace a single (or n) character(s) under the cursor (Vi mode)
220    pub fn edit_replace_char(&mut self, ch: char, n: RepeatCount) -> Result<()> {
221        self.changes.borrow_mut().begin();
222        let succeed = if let Some(chars) = self.line.delete(n) {
223            let count = chars.graphemes(true).count();
224            self.line.insert(ch, count);
225            self.line.move_backward(1);
226            true
227        } else {
228            false
229        };
230        self.changes.borrow_mut().end();
231        if succeed {
232            self.refresh_line()
233        } else {
234            Ok(())
235        }
236    }
237
238    /// Overwrite the character under the cursor (Vi mode)
239    pub fn edit_overwrite_char(&mut self, ch: char) -> Result<()> {
240        if let Some(end) = self.line.next_pos(1) {
241            {
242                let text = ch.encode_utf8(&mut self.byte_buffer);
243                let start = self.line.pos();
244                self.line.replace(start..end, text);
245            }
246            self.refresh_line()
247        } else {
248            Ok(())
249        }
250    }
251
252    // Yank/paste `text` at current position.
253    pub fn edit_yank(
254        &mut self,
255        input_state: &InputState,
256        text: &str,
257        anchor: Anchor,
258        n: RepeatCount,
259    ) -> Result<()> {
260        if let Anchor::After = anchor {
261            self.line.move_forward(1);
262        }
263        if self.line.yank(text, n).is_some() {
264            if !input_state.is_emacs_mode() {
265                self.line.move_backward(1);
266            }
267            self.refresh_line()
268        } else {
269            Ok(())
270        }
271    }
272
273    // Delete previously yanked text and yank/paste `text` at current position.
274    pub fn edit_yank_pop(&mut self, yank_size: usize, text: &str) -> Result<()> {
275        self.changes.borrow_mut().begin();
276        let result = if self.line.yank_pop(yank_size, text).is_some() {
277            self.refresh_line()
278        } else {
279            Ok(())
280        };
281        self.changes.borrow_mut().end();
282        result
283    }
284
285    /// Move cursor on the left.
286    pub fn edit_move_backward(&mut self, n: RepeatCount) -> Result<()> {
287        if self.line.move_backward(n) {
288            self.move_cursor()
289        } else {
290            Ok(())
291        }
292    }
293
294    /// Move cursor on the right.
295    pub fn edit_move_forward(&mut self, n: RepeatCount) -> Result<()> {
296        if self.line.move_forward(n) {
297            self.move_cursor()
298        } else {
299            Ok(())
300        }
301    }
302
303    /// Move cursor to the start of the line.
304    pub fn edit_move_home(&mut self) -> Result<()> {
305        if self.line.move_home() {
306            self.move_cursor()
307        } else {
308            Ok(())
309        }
310    }
311
312    /// Move cursor to the end of the line.
313    pub fn edit_move_end(&mut self) -> Result<()> {
314        if self.line.move_end() {
315            self.move_cursor()
316        } else {
317            Ok(())
318        }
319    }
320
321    pub fn edit_kill(&mut self, mvt: &Movement) -> Result<()> {
322        if self.line.kill(mvt) {
323            self.refresh_line()
324        } else {
325            Ok(())
326        }
327    }
328
329    pub fn edit_insert_text(&mut self, text: &str) -> Result<()> {
330        if text.is_empty() {
331            return Ok(());
332        }
333        let cursor = self.line.pos();
334        self.line.insert_str(cursor, text);
335        self.refresh_line()
336    }
337
338    pub fn edit_delete(&mut self, n: RepeatCount) -> Result<()> {
339        if self.line.delete(n).is_some() {
340            self.refresh_line()
341        } else {
342            Ok(())
343        }
344    }
345
346    /// Exchange the char before cursor with the character at cursor.
347    pub fn edit_transpose_chars(&mut self) -> Result<()> {
348        self.changes.borrow_mut().begin();
349        let succeed = self.line.transpose_chars();
350        self.changes.borrow_mut().end();
351        if succeed {
352            self.refresh_line()
353        } else {
354            Ok(())
355        }
356    }
357
358    pub fn edit_move_to_prev_word(&mut self, word_def: Word, n: RepeatCount) -> Result<()> {
359        if self.line.move_to_prev_word(word_def, n) {
360            self.move_cursor()
361        } else {
362            Ok(())
363        }
364    }
365
366    pub fn edit_move_to_next_word(&mut self, at: At, word_def: Word, n: RepeatCount) -> Result<()> {
367        if self.line.move_to_next_word(at, word_def, n) {
368            self.move_cursor()
369        } else {
370            Ok(())
371        }
372    }
373
374    pub fn edit_move_to(&mut self, cs: CharSearch, n: RepeatCount) -> Result<()> {
375        if self.line.move_to(cs, n) {
376            self.move_cursor()
377        } else {
378            Ok(())
379        }
380    }
381
382    pub fn edit_word(&mut self, a: WordAction) -> Result<()> {
383        self.changes.borrow_mut().begin();
384        let succeed = self.line.edit_word(a);
385        self.changes.borrow_mut().end();
386        if succeed {
387            self.refresh_line()
388        } else {
389            Ok(())
390        }
391    }
392
393    pub fn edit_transpose_words(&mut self, n: RepeatCount) -> Result<()> {
394        self.changes.borrow_mut().begin();
395        let succeed = self.line.transpose_words(n);
396        self.changes.borrow_mut().end();
397        if succeed {
398            self.refresh_line()
399        } else {
400            Ok(())
401        }
402    }
403
404    /// Substitute the currently edited line with the next or previous history
405    /// entry.
406    pub fn edit_history_next(&mut self, history: &History, prev: bool) -> Result<()> {
407        if history.is_empty() {
408            return Ok(());
409        }
410        if self.history_index == history.len() {
411            if prev {
412                // Save the current edited line before overwriting it
413                self.backup();
414            } else {
415                return Ok(());
416            }
417        } else if self.history_index == 0 && prev {
418            return Ok(());
419        }
420        if prev {
421            self.history_index -= 1;
422        } else {
423            self.history_index += 1;
424        }
425        if self.history_index < history.len() {
426            let buf = history.get(self.history_index).unwrap();
427            self.changes.borrow_mut().begin();
428            self.line.update(buf, buf.len());
429            self.changes.borrow_mut().end();
430        } else {
431            // Restore current edited line
432            self.restore();
433        }
434        self.refresh_line()
435    }
436
437    // Non-incremental, anchored search
438    pub fn edit_history_search(&mut self, history: &History, dir: Direction) -> Result<()> {
439        if history.is_empty() {
440            return self.out.beep();
441        }
442        if self.history_index == history.len() && dir == Direction::Forward
443            || self.history_index == 0 && dir == Direction::Reverse
444        {
445            return self.out.beep();
446        }
447        if dir == Direction::Reverse {
448            self.history_index -= 1;
449        } else {
450            self.history_index += 1;
451        }
452        if let Some(history_index) = history.starts_with(
453            &self.line.as_str()[..self.line.pos()],
454            self.history_index,
455            dir,
456        ) {
457            self.history_index = history_index;
458            let buf = history.get(history_index).unwrap();
459            self.changes.borrow_mut().begin();
460            self.line.update(buf, buf.len());
461            self.changes.borrow_mut().end();
462            self.refresh_line()
463        } else {
464            self.out.beep()
465        }
466    }
467
468    /// Substitute the currently edited line with the first/last history entry.
469    pub fn edit_history(&mut self, history: &History, first: bool) -> Result<()> {
470        if history.is_empty() {
471            return Ok(());
472        }
473        if self.history_index == history.len() {
474            if first {
475                // Save the current edited line before overwriting it
476                self.backup();
477            } else {
478                return Ok(());
479            }
480        } else if self.history_index == 0 && first {
481            return Ok(());
482        }
483        if first {
484            self.history_index = 0;
485            let buf = history.get(self.history_index).unwrap();
486            self.changes.borrow_mut().begin();
487            self.line.update(buf, buf.len());
488            self.changes.borrow_mut().end();
489        } else {
490            self.history_index = history.len();
491            // Restore current edited line
492            self.restore();
493        }
494        self.refresh_line()
495    }
496}
497
498#[cfg(test)]
499pub fn init_state<'out>(out: &'out mut Renderer, line: &str, pos: usize) -> State<'out, 'static> {
500    State {
501        out,
502        prompt: "",
503        prompt_size: Position::default(),
504        line: LineBuffer::init(line, pos, None),
505        cursor: Position::default(),
506        old_rows: 0,
507        history_index: 0,
508        saved_line_for_history: LineBuffer::with_capacity(100),
509        byte_buffer: [0; 4],
510        changes: Rc::new(RefCell::new(Changeset::new())),
511        hinter: None,
512        highlighter: None,
513        no_hint: true,
514    }
515}
516
517#[cfg(test)]
518mod test {
519    use super::init_state;
520    use history::History;
521    use tty::Sink;
522
523    #[test]
524    fn edit_history_next() {
525        let mut out = Sink::new();
526        let line = "current edited line";
527        let mut s = init_state(&mut out, line, 6);
528        let mut history = History::new();
529        history.add("line0");
530        history.add("line1");
531        s.history_index = history.len();
532
533        for _ in 0..2 {
534            s.edit_history_next(&history, false).unwrap();
535            assert_eq!(line, s.line.as_str());
536        }
537
538        s.edit_history_next(&history, true).unwrap();
539        assert_eq!(line, s.saved_line_for_history.as_str());
540        assert_eq!(1, s.history_index);
541        assert_eq!("line1", s.line.as_str());
542
543        for _ in 0..2 {
544            s.edit_history_next(&history, true).unwrap();
545            assert_eq!(line, s.saved_line_for_history.as_str());
546            assert_eq!(0, s.history_index);
547            assert_eq!("line0", s.line.as_str());
548        }
549
550        s.edit_history_next(&history, false).unwrap();
551        assert_eq!(line, s.saved_line_for_history.as_str());
552        assert_eq!(1, s.history_index);
553        assert_eq!("line1", s.line.as_str());
554
555        s.edit_history_next(&history, false).unwrap();
556        // assert_eq!(line, s.saved_line_for_history);
557        assert_eq!(2, s.history_index);
558        assert_eq!(line, s.line.as_str());
559    }
560}