rustyline/
line_buffer.rs

1//! Line buffer with current cursor position
2use crate::keymap::{At, CharSearch, Movement, RepeatCount, Word};
3use crate::layout::Layout;
4use std::cmp::min;
5use std::fmt;
6use std::iter;
7use std::ops::{Deref, Index, Range};
8use std::string::Drain;
9use unicode_segmentation::UnicodeSegmentation;
10
11/// Default maximum buffer size for the line read
12pub(crate) const MAX_LINE: usize = 4096;
13pub(crate) const INDENT: &str = "                                ";
14
15/// Word's case change
16#[derive(Clone, Copy)]
17pub enum WordAction {
18    /// Capitalize word
19    Capitalize,
20    /// lowercase word
21    Lowercase,
22    /// uppercase word
23    Uppercase,
24}
25
26/// Delete (kill) direction
27#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
28pub enum Direction {
29    /// After cursor
30    #[default]
31    Forward,
32    /// Before cursor
33    Backward,
34}
35
36/// Listener to be notified when some text is deleted.
37pub trait DeleteListener {
38    /// used to make the distinction between simple character(s) deletion and
39    /// word(s)/line(s) deletion
40    fn start_killing(&mut self) {}
41    /// `string` deleted at `idx` index
42    fn delete(&mut self, idx: usize, string: &str, dir: Direction);
43    /// used to make the distinction between simple character(s) deletion and
44    /// word(s)/line(s) deletion
45    fn stop_killing(&mut self) {}
46}
47
48/// Listener to be notified when the line is modified.
49pub trait ChangeListener: DeleteListener {
50    /// `c`har inserted at `idx` index
51    fn insert_char(&mut self, idx: usize, c: char);
52    /// `string` inserted at `idx` index
53    fn insert_str(&mut self, idx: usize, string: &str);
54    /// `old` text replaced by `new` text at `idx` index
55    fn replace(&mut self, idx: usize, old: &str, new: &str);
56}
57
58pub(crate) struct NoListener;
59
60impl DeleteListener for NoListener {
61    fn delete(&mut self, _idx: usize, _string: &str, _dir: Direction) {}
62}
63impl ChangeListener for NoListener {
64    fn insert_char(&mut self, _idx: usize, _c: char) {}
65
66    fn insert_str(&mut self, _idx: usize, _string: &str) {}
67
68    fn replace(&mut self, _idx: usize, _old: &str, _new: &str) {}
69}
70
71// TODO split / cache lines ?
72
73/// Represent the current input (text and cursor position).
74///
75/// The methods do text manipulations or/and cursor movements.
76pub struct LineBuffer {
77    buf: String,      // Edited line buffer (rl_line_buffer)
78    pos: usize,       // Current cursor position (byte position) (rl_point)
79    can_growth: bool, // Whether to allow dynamic growth
80}
81
82impl fmt::Debug for LineBuffer {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        f.debug_struct("LineBuffer")
85            .field("buf", &self.buf)
86            .field("pos", &self.pos)
87            .finish()
88    }
89}
90
91impl LineBuffer {
92    /// Create a new line buffer with the given maximum `capacity`.
93    #[must_use]
94    pub fn with_capacity(capacity: usize) -> Self {
95        Self {
96            buf: String::with_capacity(capacity),
97            pos: 0,
98            can_growth: false,
99        }
100    }
101
102    /// Set whether to allow dynamic allocation
103    pub(crate) fn can_growth(mut self, can_growth: bool) -> Self {
104        self.can_growth = can_growth;
105        self
106    }
107
108    fn must_truncate(&self, new_len: usize) -> bool {
109        !self.can_growth && new_len > self.buf.capacity()
110    }
111
112    #[cfg(test)]
113    pub(crate) fn init(line: &str, pos: usize) -> Self {
114        let mut lb = Self::with_capacity(MAX_LINE);
115        assert!(lb.insert_str(0, line, &mut NoListener));
116        lb.set_pos(pos);
117        lb
118    }
119
120    /// Extracts a string slice containing the entire buffer.
121    #[must_use]
122    pub fn as_str(&self) -> &str {
123        &self.buf
124    }
125
126    /// Converts a buffer into a `String` without copying or allocating.
127    #[must_use]
128    pub fn into_string(self) -> String {
129        self.buf
130    }
131
132    /// Current cursor position (byte position)
133    #[must_use]
134    pub fn pos(&self) -> usize {
135        self.pos
136    }
137
138    /// Check if cursor is at the end of input
139    #[must_use]
140    pub fn is_cursor_at_end(&self) -> bool {
141        self.pos == self.buf.len()
142    }
143
144    /// Set cursor position (byte position)
145    pub fn set_pos(&mut self, pos: usize) {
146        assert!(pos <= self.buf.len());
147        self.pos = pos;
148    }
149
150    /// Returns the length of this buffer, in bytes.
151    #[must_use]
152    pub fn len(&self) -> usize {
153        self.buf.len()
154    }
155
156    /// Returns `true` if this buffer has a length of zero.
157    #[must_use]
158    pub fn is_empty(&self) -> bool {
159        self.buf.is_empty()
160    }
161
162    /// Set line content (`buf`) and cursor position (`pos`).
163    pub fn update<C: ChangeListener>(&mut self, buf: &str, pos: usize, cl: &mut C) {
164        assert!(pos <= buf.len());
165        let end = self.len();
166        self.drain(0..end, Direction::default(), cl);
167        let max = self.buf.capacity();
168        if self.must_truncate(buf.len()) {
169            self.insert_str(0, &buf[..max], cl);
170            self.pos = max.min(pos);
171        } else {
172            self.insert_str(0, buf, cl);
173            self.pos = pos;
174        }
175    }
176
177    fn end_of_line(&self) -> usize {
178        if let Some(n) = self.buf[self.pos..].find('\n') {
179            n + self.pos
180        } else {
181            self.buf.len()
182        }
183    }
184
185    fn start_of_line(&self) -> usize {
186        if let Some(i) = self.buf[..self.pos].rfind('\n') {
187            // `i` is before the new line, e.g. at the end of the previous one.
188            i + 1
189        } else {
190            0
191        }
192    }
193
194    /// Returns the character at current cursor position.
195    pub(crate) fn grapheme_at_cursor(&self) -> Option<&str> {
196        if self.is_cursor_at_end() {
197            None
198        } else {
199            self.buf[self.pos..].graphemes(true).next()
200        }
201    }
202
203    /// Returns the position of the character just after the current cursor
204    /// position.
205    #[must_use]
206    pub fn next_pos(&self, n: RepeatCount) -> Option<usize> {
207        if self.is_cursor_at_end() {
208            return None;
209        }
210        self.buf[self.pos..]
211            .grapheme_indices(true)
212            .take(usize::from(n))
213            .last()
214            .map(|(i, s)| i + self.pos + s.len())
215    }
216
217    /// Returns the position of the character just before the current cursor
218    /// position.
219    fn prev_pos(&self, n: RepeatCount) -> Option<usize> {
220        if self.pos == 0 {
221            return None;
222        }
223        self.buf[..self.pos]
224            .grapheme_indices(true)
225            .rev()
226            .take(usize::from(n))
227            .last()
228            .map(|(i, _)| i)
229    }
230
231    /// Insert the character `ch` at current cursor position
232    /// and advance cursor position accordingly.
233    /// Return `None` when maximum buffer size has been reached,
234    /// `true` when the character has been appended to the end of the line.
235    pub fn insert<C: ChangeListener>(
236        &mut self,
237        ch: char,
238        n: RepeatCount,
239        cl: &mut C,
240    ) -> Option<bool> {
241        let n = usize::from(n);
242        let shift = ch.len_utf8() * n;
243        if self.must_truncate(self.buf.len() + shift) {
244            return None;
245        }
246        let push = self.is_cursor_at_end();
247        if n == 1 {
248            self.buf.insert(self.pos, ch);
249            cl.insert_char(self.pos, ch);
250        } else {
251            let text = iter::repeat_n(ch, n).collect::<String>();
252            let pos = self.pos;
253            self.insert_str(pos, &text, cl);
254        }
255        self.pos += shift;
256        Some(push)
257    }
258
259    /// Yank/paste `text` at current position.
260    /// Return `None` when maximum buffer size has been reached or is empty,
261    /// `true` when the character has been appended to the end of the line.
262    pub fn yank<C: ChangeListener>(
263        &mut self,
264        text: &str,
265        n: RepeatCount,
266        cl: &mut C,
267    ) -> Option<bool> {
268        let n = usize::from(n);
269        let shift = text.len() * n;
270        if text.is_empty() || self.must_truncate(self.buf.len() + shift) {
271            return None;
272        }
273        let push = self.is_cursor_at_end();
274        let pos = self.pos;
275        if n == 1 {
276            self.insert_str(pos, text, cl);
277        } else {
278            let text = text.repeat(n);
279            self.insert_str(pos, &text, cl);
280        }
281        self.pos += shift;
282        Some(push)
283    }
284
285    /// Delete previously yanked text and yank/paste `text` at current position.
286    pub fn yank_pop<C: ChangeListener>(
287        &mut self,
288        yank_size: usize,
289        text: &str,
290        cl: &mut C,
291    ) -> Option<bool> {
292        let end = self.pos;
293        let start = end - yank_size;
294        self.drain(start..end, Direction::default(), cl);
295        self.pos -= yank_size;
296        self.yank(text, 1, cl)
297    }
298
299    /// Move cursor on the left.
300    pub fn move_backward(&mut self, n: RepeatCount) -> bool {
301        match self.prev_pos(n) {
302            Some(pos) => {
303                self.pos = pos;
304                true
305            }
306            None => false,
307        }
308    }
309
310    /// Move cursor on the right.
311    pub fn move_forward(&mut self, n: RepeatCount) -> bool {
312        match self.next_pos(n) {
313            Some(pos) => {
314                self.pos = pos;
315                true
316            }
317            None => false,
318        }
319    }
320
321    /// Move cursor to the start of the buffer.
322    pub fn move_buffer_start(&mut self) -> bool {
323        if self.pos > 0 {
324            self.pos = 0;
325            true
326        } else {
327            false
328        }
329    }
330
331    /// Move cursor to the end of the buffer.
332    pub fn move_buffer_end(&mut self) -> bool {
333        if self.is_cursor_at_end() {
334            false
335        } else {
336            self.pos = self.buf.len();
337            true
338        }
339    }
340
341    /// Move cursor to the start of the line.
342    pub fn move_home(&mut self) -> bool {
343        let start = self.start_of_line();
344        if self.pos > start {
345            self.pos = start;
346            true
347        } else {
348            false
349        }
350    }
351
352    /// Move cursor to the end of the line.
353    pub fn move_end(&mut self) -> bool {
354        let end = self.end_of_line();
355        if self.pos == end {
356            false
357        } else {
358            self.pos = end;
359            true
360        }
361    }
362
363    /// Is cursor at the end of input (whitespaces after cursor is discarded)
364    #[must_use]
365    pub fn is_end_of_input(&self) -> bool {
366        self.pos >= self.buf.trim_end().len()
367    }
368
369    /// Delete the character at the right of the cursor without altering the
370    /// cursor position. Basically this is what happens with the "Delete"
371    /// keyboard key.
372    /// Return the number of characters deleted.
373    pub fn delete<D: DeleteListener>(&mut self, n: RepeatCount, dl: &mut D) -> Option<String> {
374        match self.next_pos(n) {
375            Some(pos) => {
376                let start = self.pos;
377                let chars = self
378                    .drain(start..pos, Direction::Forward, dl)
379                    .collect::<String>();
380                Some(chars)
381            }
382            None => None,
383        }
384    }
385
386    /// Delete the character at the left of the cursor.
387    /// Basically that is what happens with the "Backspace" keyboard key.
388    pub fn backspace<D: DeleteListener>(&mut self, n: RepeatCount, dl: &mut D) -> bool {
389        match self.prev_pos(n) {
390            Some(pos) => {
391                let end = self.pos;
392                self.drain(pos..end, Direction::Backward, dl);
393                self.pos = pos;
394                true
395            }
396            None => false,
397        }
398    }
399
400    /// Kill the text from point to the end of the line.
401    pub fn kill_line<D: DeleteListener>(&mut self, dl: &mut D) -> bool {
402        if !self.buf.is_empty() && self.pos < self.buf.len() {
403            let start = self.pos;
404            let end = self.end_of_line();
405            if start == end {
406                self.delete(1, dl);
407            } else {
408                self.drain(start..end, Direction::Forward, dl);
409            }
410            true
411        } else {
412            false
413        }
414    }
415
416    /// Kill the text from point to the end of the buffer.
417    pub fn kill_buffer<D: DeleteListener>(&mut self, dl: &mut D) -> bool {
418        if !self.buf.is_empty() && self.pos < self.buf.len() {
419            let start = self.pos;
420            let end = self.buf.len();
421            self.drain(start..end, Direction::Forward, dl);
422            true
423        } else {
424            false
425        }
426    }
427
428    /// Kill backward from point to the beginning of the line.
429    pub fn discard_line<D: DeleteListener>(&mut self, dl: &mut D) -> bool {
430        if self.pos > 0 && !self.buf.is_empty() {
431            let start = self.start_of_line();
432            let end = self.pos;
433            if end == start {
434                self.backspace(1, dl)
435            } else {
436                self.drain(start..end, Direction::Backward, dl);
437                self.pos = start;
438                true
439            }
440        } else {
441            false
442        }
443    }
444
445    /// Kill backward from point to the beginning of the buffer.
446    pub fn discard_buffer<D: DeleteListener>(&mut self, dl: &mut D) -> bool {
447        if self.pos > 0 && !self.buf.is_empty() {
448            let end = self.pos;
449            self.drain(0..end, Direction::Backward, dl);
450            self.pos = 0;
451            true
452        } else {
453            false
454        }
455    }
456
457    /// Exchange the char before cursor with the character at cursor.
458    pub fn transpose_chars<C: ChangeListener>(&mut self, cl: &mut C) -> bool {
459        if self.pos == 0 || self.buf.graphemes(true).count() < 2 {
460            return false;
461        }
462        if self.is_cursor_at_end() {
463            self.move_backward(1);
464        }
465        let chars = self.delete(1, cl).unwrap();
466        self.move_backward(1);
467        self.yank(&chars, 1, cl);
468        self.move_forward(1);
469        true
470    }
471
472    /// Go left until start of word
473    fn prev_word_pos(&self, pos: usize, word_def: Word, n: RepeatCount) -> Option<usize> {
474        if pos == 0 {
475            return None;
476        }
477        let mut sow = 0;
478        let mut gis = self.buf[..pos].grapheme_indices(true).rev();
479        'outer: for _ in 0..n {
480            sow = 0;
481            let mut gj = gis.next();
482            'inner: loop {
483                if let Some((j, y)) = gj {
484                    let gi = gis.next();
485                    if let Some((_, x)) = gi {
486                        if is_start_of_word(word_def, x, y) {
487                            sow = j;
488                            break 'inner;
489                        }
490                        gj = gi;
491                    } else {
492                        break 'outer;
493                    }
494                } else {
495                    break 'outer;
496                }
497            }
498        }
499        Some(sow)
500    }
501
502    /// Moves the cursor to the beginning of previous word.
503    pub fn move_to_prev_word(&mut self, word_def: Word, n: RepeatCount) -> bool {
504        if let Some(pos) = self.prev_word_pos(self.pos, word_def, n) {
505            self.pos = pos;
506            true
507        } else {
508            false
509        }
510    }
511
512    /// Delete the previous word, maintaining the cursor at the start of the
513    /// current word.
514    pub fn delete_prev_word<D: DeleteListener>(
515        &mut self,
516        word_def: Word,
517        n: RepeatCount,
518        dl: &mut D,
519    ) -> bool {
520        if let Some(pos) = self.prev_word_pos(self.pos, word_def, n) {
521            let end = self.pos;
522            self.drain(pos..end, Direction::Backward, dl);
523            self.pos = pos;
524            true
525        } else {
526            false
527        }
528    }
529
530    fn next_word_pos(&self, pos: usize, at: At, word_def: Word, n: RepeatCount) -> Option<usize> {
531        if pos == self.buf.len() {
532            return None;
533        }
534        let mut wp = 0;
535        let mut gis = self.buf[pos..].grapheme_indices(true);
536        let mut gi = if at == At::BeforeEnd {
537            // TODO Validate
538            gis.next()
539        } else {
540            None
541        };
542        'outer: for _ in 0..n {
543            wp = 0;
544            gi = gis.next();
545            'inner: loop {
546                if let Some((i, x)) = gi {
547                    let gj = gis.next();
548                    if let Some((j, y)) = gj {
549                        if at == At::Start && is_start_of_word(word_def, x, y) {
550                            wp = j;
551                            break 'inner;
552                        } else if at != At::Start && is_end_of_word(word_def, x, y) {
553                            if word_def == Word::Emacs || at == At::AfterEnd {
554                                wp = j;
555                            } else {
556                                wp = i;
557                            }
558                            break 'inner;
559                        }
560                        gi = gj;
561                    } else {
562                        break 'outer;
563                    }
564                } else {
565                    break 'outer;
566                }
567            }
568        }
569        if wp == 0 {
570            if word_def == Word::Emacs || at == At::AfterEnd {
571                Some(self.buf.len())
572            } else {
573                match gi {
574                    Some((i, _)) if i != 0 => Some(i + pos),
575                    _ => None,
576                }
577            }
578        } else {
579            Some(wp + pos)
580        }
581    }
582
583    /// Moves the cursor to the end of next word.
584    pub fn move_to_next_word(&mut self, at: At, word_def: Word, n: RepeatCount) -> bool {
585        if let Some(pos) = self.next_word_pos(self.pos, at, word_def, n) {
586            self.pos = pos;
587            true
588        } else {
589            false
590        }
591    }
592
593    /// Moves the cursor to the same column in the line above
594    pub fn move_to_line_up(&mut self, n: RepeatCount, layout: &Layout) -> bool {
595        match self.buf[..self.pos].rfind('\n') {
596            Some(off) => {
597                let column = layout.width(&self.buf[off + 1..self.pos]);
598
599                let mut dest_start = self.buf[..off].rfind('\n').map_or(0, |n| n + 1);
600                let mut dest_end = off;
601                for _ in 1..n {
602                    if dest_start == 0 {
603                        break;
604                    }
605                    dest_end = dest_start - 1;
606                    dest_start = self.buf[..dest_end].rfind('\n').map_or(0, |n| n + 1);
607                }
608                let offset = if dest_start == 0 {
609                    layout.prompt_size.col
610                } else {
611                    0
612                };
613                let gidx = self.buf[dest_start..dest_end]
614                    .grapheme_indices(true)
615                    .nth(column.saturating_sub(offset) as usize);
616
617                self.pos = gidx.map_or(off, |(idx, _)| dest_start + idx); // if there's no enough columns
618                true
619            }
620            None => false,
621        }
622    }
623
624    /// N lines up starting from the current one
625    ///
626    /// Fails if the cursor is on the first line
627    fn n_lines_up(&self, n: RepeatCount) -> Option<(usize, usize)> {
628        let mut start = if let Some(off) = self.buf[..self.pos].rfind('\n') {
629            off + 1
630        } else {
631            return None;
632        };
633        let end = self.buf[self.pos..]
634            .find('\n')
635            .map_or_else(|| self.buf.len(), |x| self.pos + x + 1);
636        for _ in 0..n {
637            if let Some(off) = self.buf[..start - 1].rfind('\n') {
638                start = off + 1;
639            } else {
640                start = 0;
641                break;
642            }
643        }
644        Some((start, end))
645    }
646
647    /// N lines down starting from the current one
648    ///
649    /// Fails if the cursor is on the last line
650    fn n_lines_down(&self, n: RepeatCount) -> Option<(usize, usize)> {
651        let mut end = if let Some(off) = self.buf[self.pos..].find('\n') {
652            self.pos + off + 1
653        } else {
654            return None;
655        };
656        let start = self.buf[..self.pos].rfind('\n').unwrap_or(0);
657        for _ in 0..n {
658            if let Some(off) = self.buf[end..].find('\n') {
659                end = end + off + 1;
660            } else {
661                end = self.buf.len();
662                break;
663            };
664        }
665        Some((start, end))
666    }
667
668    /// Moves the cursor to the same column in the line above
669    pub fn move_to_line_down(&mut self, n: RepeatCount, layout: &Layout) -> bool {
670        match self.buf[self.pos..].find('\n') {
671            Some(off) => {
672                let line_start = self.buf[..self.pos].rfind('\n').map_or(0, |n| n + 1);
673                let offset = if line_start == 0 {
674                    layout.prompt_size.col
675                } else {
676                    0
677                };
678                let column = layout.width(&self.buf[line_start..self.pos]) + offset;
679                let mut dest_start = self.pos + off + 1;
680                let mut dest_end = self.buf[dest_start..]
681                    .find('\n')
682                    .map_or_else(|| self.buf.len(), |v| dest_start + v);
683                for _ in 1..n {
684                    if dest_end == self.buf.len() {
685                        break;
686                    }
687                    dest_start = dest_end + 1;
688                    dest_end = self.buf[dest_start..]
689                        .find('\n')
690                        .map_or_else(|| self.buf.len(), |v| dest_start + v);
691                }
692                self.pos = self.buf[dest_start..dest_end]
693                    .grapheme_indices(true)
694                    .nth(column as usize)
695                    .map_or(dest_end, |(idx, _)| dest_start + idx); // if there's no enough columns
696                debug_assert!(self.pos <= self.buf.len());
697                true
698            }
699            None => false,
700        }
701    }
702
703    fn search_char_pos(&self, cs: CharSearch, n: RepeatCount) -> Option<usize> {
704        let n = usize::from(n);
705        let mut shift = 0;
706        let search_result = match cs {
707            CharSearch::Backward(c) | CharSearch::BackwardAfter(c) => self.buf[..self.pos]
708                .char_indices()
709                .rev()
710                .filter(|&(_, ch)| ch == c)
711                .take(n)
712                .last()
713                .map(|(i, _)| i),
714            CharSearch::Forward(c) | CharSearch::ForwardBefore(c) => {
715                if let Some(cc) = self.grapheme_at_cursor() {
716                    shift = self.pos + cc.len();
717                    if shift < self.buf.len() {
718                        self.buf[shift..]
719                            .char_indices()
720                            .filter(|&(_, ch)| ch == c)
721                            .take(n)
722                            .last()
723                            .map(|(i, _)| i)
724                    } else {
725                        None
726                    }
727                } else {
728                    None
729                }
730            }
731        };
732        search_result.map(|pos| match cs {
733            CharSearch::Backward(_) => pos,
734            CharSearch::BackwardAfter(c) => pos + c.len_utf8(),
735            CharSearch::Forward(_) => shift + pos,
736            CharSearch::ForwardBefore(_) => {
737                shift + pos
738                    - self.buf[..shift + pos]
739                        .chars()
740                        .next_back()
741                        .unwrap()
742                        .len_utf8()
743            }
744        })
745    }
746
747    /// Move cursor to the matching character position.
748    /// Return `true` when the search succeeds.
749    pub fn move_to(&mut self, cs: CharSearch, n: RepeatCount) -> bool {
750        if let Some(pos) = self.search_char_pos(cs, n) {
751            self.pos = pos;
752            true
753        } else {
754            false
755        }
756    }
757
758    /// Kill from the cursor to the end of the current word,
759    /// or, if between words, to the end of the next word.
760    pub fn delete_word<D: DeleteListener>(
761        &mut self,
762        at: At,
763        word_def: Word,
764        n: RepeatCount,
765        dl: &mut D,
766    ) -> bool {
767        if let Some(pos) = self.next_word_pos(self.pos, at, word_def, n) {
768            let start = self.pos;
769            self.drain(start..pos, Direction::Forward, dl);
770            true
771        } else {
772            false
773        }
774    }
775
776    /// Delete range specified by `cs` search.
777    pub fn delete_to<D: DeleteListener>(
778        &mut self,
779        cs: CharSearch,
780        n: RepeatCount,
781        dl: &mut D,
782    ) -> bool {
783        let search_result = match cs {
784            CharSearch::ForwardBefore(c) => self.search_char_pos(CharSearch::Forward(c), n),
785            _ => self.search_char_pos(cs, n),
786        };
787        if let Some(pos) = search_result {
788            match cs {
789                CharSearch::Backward(_) | CharSearch::BackwardAfter(_) => {
790                    let end = self.pos;
791                    self.pos = pos;
792                    self.drain(pos..end, Direction::Backward, dl);
793                }
794                CharSearch::ForwardBefore(_) => {
795                    let start = self.pos;
796                    self.drain(start..pos, Direction::Forward, dl);
797                }
798                CharSearch::Forward(c) => {
799                    let start = self.pos;
800                    self.drain(start..pos + c.len_utf8(), Direction::Forward, dl);
801                }
802            };
803            true
804        } else {
805            false
806        }
807    }
808
809    fn skip_whitespace(&self) -> Option<usize> {
810        if self.is_cursor_at_end() {
811            return None;
812        }
813        self.buf[self.pos..]
814            .grapheme_indices(true)
815            .find_map(|(i, ch)| {
816                if ch.chars().all(char::is_alphanumeric) {
817                    Some(i)
818                } else {
819                    None
820                }
821            })
822            .map(|i| i + self.pos)
823    }
824
825    /// Alter the next word.
826    pub fn edit_word<C: ChangeListener>(&mut self, a: WordAction, cl: &mut C) -> bool {
827        if let Some(start) = self.skip_whitespace() {
828            if let Some(end) = self.next_word_pos(start, At::AfterEnd, Word::Emacs, 1) {
829                if start == end {
830                    return false;
831                }
832                let word = self
833                    .drain(start..end, Direction::default(), cl)
834                    .collect::<String>();
835                let result = match a {
836                    WordAction::Capitalize => {
837                        let ch = word.graphemes(true).next().unwrap();
838                        let cap = ch.to_uppercase();
839                        cap + &word[ch.len()..].to_lowercase()
840                    }
841                    WordAction::Lowercase => word.to_lowercase(),
842                    WordAction::Uppercase => word.to_uppercase(),
843                };
844                self.insert_str(start, &result, cl);
845                self.pos = start + result.len();
846                return true;
847            }
848        }
849        false
850    }
851
852    /// Transpose two words
853    pub fn transpose_words<C: ChangeListener>(&mut self, n: RepeatCount, cl: &mut C) -> bool {
854        let word_def = Word::Emacs;
855        self.move_to_next_word(At::AfterEnd, word_def, n);
856        let w2_end = self.pos;
857        self.move_to_prev_word(word_def, 1);
858        let w2_beg = self.pos;
859        self.move_to_prev_word(word_def, n);
860        let w1_beg = self.pos;
861        self.move_to_next_word(At::AfterEnd, word_def, 1);
862        let w1_end = self.pos;
863        if w1_beg == w2_beg || w2_beg < w1_end {
864            return false;
865        }
866
867        let w1 = self.buf[w1_beg..w1_end].to_owned();
868
869        let w2 = self
870            .drain(w2_beg..w2_end, Direction::default(), cl)
871            .collect::<String>();
872        self.insert_str(w2_beg, &w1, cl);
873
874        self.drain(w1_beg..w1_end, Direction::default(), cl);
875        self.insert_str(w1_beg, &w2, cl);
876
877        self.pos = w2_end;
878        true
879    }
880
881    /// Replaces the content between [`start`..`end`] with `text`
882    /// and positions the cursor to the end of text.
883    pub fn replace<C: ChangeListener>(&mut self, range: Range<usize>, text: &str, cl: &mut C) {
884        let start = range.start;
885        cl.replace(start, self.buf.index(range.clone()), text);
886        self.buf.drain(range);
887        if start == self.buf.len() {
888            self.buf.push_str(text);
889        } else {
890            self.buf.insert_str(start, text);
891        }
892        self.pos = start + text.len();
893    }
894
895    /// Insert the `s`tring at the specified position.
896    /// Return `true` if the text has been inserted at the end of the line.
897    pub fn insert_str<C: ChangeListener>(&mut self, idx: usize, s: &str, cl: &mut C) -> bool {
898        cl.insert_str(idx, s);
899        if idx == self.buf.len() {
900            self.buf.push_str(s);
901            true
902        } else {
903            self.buf.insert_str(idx, s);
904            false
905        }
906    }
907
908    /// Remove the specified `range` in the line.
909    pub fn delete_range<D: DeleteListener>(&mut self, range: Range<usize>, dl: &mut D) {
910        self.set_pos(range.start);
911        self.drain(range, Direction::default(), dl);
912    }
913
914    fn drain<D: DeleteListener>(
915        &mut self,
916        range: Range<usize>,
917        dir: Direction,
918        dl: &mut D,
919    ) -> Drain<'_> {
920        dl.delete(range.start, &self.buf[range.start..range.end], dir);
921        self.buf.drain(range)
922    }
923
924    /// Return the content between current cursor position and `mvt` position.
925    /// Return `None` when the buffer is empty or when the movement fails.
926    #[must_use]
927    pub fn copy(&self, mvt: &Movement) -> Option<String> {
928        if self.is_empty() {
929            return None;
930        }
931        match *mvt {
932            Movement::WholeLine => {
933                let start = self.start_of_line();
934                let end = self.end_of_line();
935                if start == end {
936                    None
937                } else {
938                    Some(self.buf[start..self.pos].to_owned())
939                }
940            }
941            Movement::BeginningOfLine => {
942                let start = self.start_of_line();
943                if self.pos == start {
944                    None
945                } else {
946                    Some(self.buf[start..self.pos].to_owned())
947                }
948            }
949            Movement::ViFirstPrint => {
950                if self.pos == 0 {
951                    None
952                } else {
953                    self.next_word_pos(0, At::Start, Word::Big, 1)
954                        .map(|pos| self.buf[pos..self.pos].to_owned())
955                }
956            }
957            Movement::EndOfLine => {
958                let end = self.end_of_line();
959                if self.pos == end {
960                    None
961                } else {
962                    Some(self.buf[self.pos..end].to_owned())
963                }
964            }
965            Movement::EndOfBuffer => {
966                if self.is_cursor_at_end() {
967                    None
968                } else {
969                    Some(self.buf[self.pos..].to_owned())
970                }
971            }
972            Movement::WholeBuffer => {
973                if self.buf.is_empty() {
974                    None
975                } else {
976                    Some(self.buf.clone())
977                }
978            }
979            Movement::BeginningOfBuffer => {
980                if self.pos == 0 {
981                    None
982                } else {
983                    Some(self.buf[..self.pos].to_owned())
984                }
985            }
986            Movement::BackwardWord(n, word_def) => self
987                .prev_word_pos(self.pos, word_def, n)
988                .map(|pos| self.buf[pos..self.pos].to_owned()),
989            Movement::ForwardWord(n, at, word_def) => self
990                .next_word_pos(self.pos, at, word_def, n)
991                .map(|pos| self.buf[self.pos..pos].to_owned()),
992            Movement::ViCharSearch(n, cs) => {
993                let search_result = match cs {
994                    CharSearch::ForwardBefore(c) => self.search_char_pos(CharSearch::Forward(c), n),
995                    _ => self.search_char_pos(cs, n),
996                };
997                search_result.map(|pos| match cs {
998                    CharSearch::Backward(_) | CharSearch::BackwardAfter(_) => {
999                        self.buf[pos..self.pos].to_owned()
1000                    }
1001                    CharSearch::ForwardBefore(_) => self.buf[self.pos..pos].to_owned(),
1002                    CharSearch::Forward(c) => self.buf[self.pos..pos + c.len_utf8()].to_owned(),
1003                })
1004            }
1005            Movement::BackwardChar(n) => self
1006                .prev_pos(n)
1007                .map(|pos| self.buf[pos..self.pos].to_owned()),
1008            Movement::ForwardChar(n) => self
1009                .next_pos(n)
1010                .map(|pos| self.buf[self.pos..pos].to_owned()),
1011            Movement::LineUp(n) => {
1012                if let Some((start, end)) = self.n_lines_up(n) {
1013                    Some(self.buf[start..end].to_owned())
1014                } else {
1015                    None
1016                }
1017            }
1018            Movement::LineDown(n) => {
1019                if let Some((start, end)) = self.n_lines_down(n) {
1020                    Some(self.buf[start..end].to_owned())
1021                } else {
1022                    None
1023                }
1024            }
1025        }
1026    }
1027
1028    /// Kill range specified by `mvt`.
1029    pub fn kill<D: DeleteListener>(&mut self, mvt: &Movement, dl: &mut D) -> bool {
1030        let notify = !matches!(*mvt, Movement::ForwardChar(_) | Movement::BackwardChar(_));
1031        if notify {
1032            dl.start_killing();
1033        }
1034        let killed = match *mvt {
1035            Movement::ForwardChar(n) => {
1036                // Delete (forward) `n` characters at point.
1037                self.delete(n, dl).is_some()
1038            }
1039            Movement::BackwardChar(n) => {
1040                // Delete `n` characters backward.
1041                self.backspace(n, dl)
1042            }
1043            Movement::EndOfLine => {
1044                // Kill the text from point to the end of the line.
1045                self.kill_line(dl)
1046            }
1047            Movement::WholeLine => {
1048                self.move_home();
1049                self.kill_line(dl)
1050            }
1051            Movement::BeginningOfLine => {
1052                // Kill backward from point to the beginning of the line.
1053                self.discard_line(dl)
1054            }
1055            Movement::BackwardWord(n, word_def) => {
1056                // kill `n` words backward (until start of word)
1057                self.delete_prev_word(word_def, n, dl)
1058            }
1059            Movement::ForwardWord(n, at, word_def) => {
1060                // kill `n` words forward (until start/end of word)
1061                self.delete_word(at, word_def, n, dl)
1062            }
1063            Movement::ViCharSearch(n, cs) => self.delete_to(cs, n, dl),
1064            Movement::LineUp(n) => {
1065                if let Some((start, end)) = self.n_lines_up(n) {
1066                    self.delete_range(start..end, dl);
1067                    true
1068                } else {
1069                    false
1070                }
1071            }
1072            Movement::LineDown(n) => {
1073                if let Some((start, end)) = self.n_lines_down(n) {
1074                    self.delete_range(start..end, dl);
1075                    true
1076                } else {
1077                    false
1078                }
1079            }
1080            Movement::ViFirstPrint => {
1081                false // TODO
1082            }
1083            Movement::EndOfBuffer => {
1084                // Kill the text from point to the end of the buffer.
1085                self.kill_buffer(dl)
1086            }
1087            Movement::BeginningOfBuffer => {
1088                // Kill backward from point to the beginning of the buffer.
1089                self.discard_buffer(dl)
1090            }
1091            Movement::WholeBuffer => {
1092                self.move_buffer_start();
1093                self.kill_buffer(dl)
1094            }
1095        };
1096        if notify {
1097            dl.stop_killing();
1098        }
1099        killed
1100    }
1101
1102    /// Indent range specified by `mvt`.
1103    pub fn indent<C: ChangeListener>(
1104        &mut self,
1105        mvt: &Movement,
1106        amount: u8,
1107        dedent: bool,
1108        cl: &mut C,
1109    ) -> bool {
1110        let pair = match *mvt {
1111            // All inline operators are the same: indent current line
1112            Movement::WholeLine
1113            | Movement::BeginningOfLine
1114            | Movement::ViFirstPrint
1115            | Movement::EndOfLine
1116            | Movement::BackwardChar(..)
1117            | Movement::ForwardChar(..)
1118            | Movement::ViCharSearch(..) => Some((self.pos, self.pos)),
1119            Movement::EndOfBuffer => Some((self.pos, self.buf.len())),
1120            Movement::WholeBuffer => Some((0, self.buf.len())),
1121            Movement::BeginningOfBuffer => Some((0, self.pos)),
1122            Movement::BackwardWord(n, word_def) => self
1123                .prev_word_pos(self.pos, word_def, n)
1124                .map(|pos| (pos, self.pos)),
1125            Movement::ForwardWord(n, at, word_def) => self
1126                .next_word_pos(self.pos, at, word_def, n)
1127                .map(|pos| (self.pos, pos)),
1128            Movement::LineUp(n) => self.n_lines_up(n),
1129            Movement::LineDown(n) => self.n_lines_down(n),
1130        };
1131        let amount = usize::from(amount);
1132        let (start, end) = pair.unwrap_or((self.pos, self.pos));
1133        let start = self.buf[..start].rfind('\n').map_or(0, |pos| pos + 1);
1134        let end = self.buf[end..]
1135            .rfind('\n')
1136            .map_or_else(|| self.buf.len(), |pos| end + pos);
1137        let mut index = start;
1138        if dedent {
1139            #[expect(clippy::unnecessary_to_owned)]
1140            for line in self.buf[start..end].to_string().split('\n') {
1141                let max = line.len() - line.trim_start().len();
1142                let deleting = min(max, amount);
1143                self.drain(index..index + deleting, Direction::default(), cl);
1144                if self.pos >= index {
1145                    if self.pos.saturating_sub(index) < deleting {
1146                        // don't wrap into the previous line
1147                        self.pos = index;
1148                    } else {
1149                        self.pos -= deleting;
1150                    }
1151                }
1152                index += line.len() + 1 - deleting;
1153            }
1154        } else {
1155            #[expect(clippy::unnecessary_to_owned)]
1156            for line in self.buf[start..end].to_string().split('\n') {
1157                for off in (0..amount).step_by(INDENT.len()) {
1158                    self.insert_str(index, &INDENT[..min(amount - off, INDENT.len())], cl);
1159                }
1160                if self.pos >= index {
1161                    self.pos += amount;
1162                }
1163                index += amount + line.len() + 1;
1164            }
1165        }
1166        true
1167    }
1168}
1169
1170impl Deref for LineBuffer {
1171    type Target = str;
1172
1173    fn deref(&self) -> &str {
1174        self.as_str()
1175    }
1176}
1177
1178fn is_start_of_word(word_def: Word, previous: &str, grapheme: &str) -> bool {
1179    (!is_word_char(word_def, previous) && is_word_char(word_def, grapheme))
1180        || (word_def == Word::Vi && !is_other_char(previous) && is_other_char(grapheme))
1181}
1182fn is_end_of_word(word_def: Word, grapheme: &str, next: &str) -> bool {
1183    (!is_word_char(word_def, next) && is_word_char(word_def, grapheme))
1184        || (word_def == Word::Vi && !is_other_char(next) && is_other_char(grapheme))
1185}
1186
1187fn is_word_char(word_def: Word, grapheme: &str) -> bool {
1188    match word_def {
1189        Word::Emacs => grapheme.chars().all(char::is_alphanumeric),
1190        Word::Vi => is_vi_word_char(grapheme),
1191        Word::Big => !grapheme.chars().any(char::is_whitespace),
1192    }
1193}
1194fn is_vi_word_char(grapheme: &str) -> bool {
1195    grapheme.chars().all(char::is_alphanumeric) || grapheme == "_"
1196}
1197fn is_other_char(grapheme: &str) -> bool {
1198    !(grapheme.chars().any(char::is_whitespace) || is_vi_word_char(grapheme))
1199}
1200
1201#[cfg(test)]
1202mod test {
1203    use super::{
1204        ChangeListener, DeleteListener, Direction, LineBuffer, NoListener, WordAction, MAX_LINE,
1205    };
1206    use crate::{
1207        keymap::{At, CharSearch, Word},
1208        layout::Layout,
1209    };
1210
1211    struct Listener {
1212        deleted_str: Option<String>,
1213    }
1214
1215    impl Listener {
1216        fn new() -> Self {
1217            Self { deleted_str: None }
1218        }
1219
1220        fn assert_deleted_str_eq(&self, expected: &str) {
1221            let actual = self.deleted_str.as_ref().expect("no deleted string");
1222            assert_eq!(expected, actual)
1223        }
1224    }
1225
1226    impl DeleteListener for Listener {
1227        fn delete(&mut self, _: usize, string: &str, _: Direction) {
1228            self.deleted_str = Some(string.to_owned());
1229        }
1230    }
1231    impl ChangeListener for Listener {
1232        fn insert_char(&mut self, _: usize, _: char) {}
1233
1234        fn insert_str(&mut self, _: usize, _: &str) {}
1235
1236        fn replace(&mut self, _: usize, _: &str, _: &str) {}
1237    }
1238
1239    #[test]
1240    fn next_pos() {
1241        let s = LineBuffer::init("ö̲g̈", 0);
1242        assert_eq!(7, s.len());
1243        let pos = s.next_pos(1);
1244        assert_eq!(Some(4), pos);
1245
1246        let s = LineBuffer::init("ö̲g̈", 4);
1247        let pos = s.next_pos(1);
1248        assert_eq!(Some(7), pos);
1249    }
1250
1251    #[test]
1252    fn prev_pos() {
1253        let s = LineBuffer::init("ö̲g̈", 4);
1254        assert_eq!(7, s.len());
1255        let pos = s.prev_pos(1);
1256        assert_eq!(Some(0), pos);
1257
1258        let s = LineBuffer::init("ö̲g̈", 7);
1259        let pos = s.prev_pos(1);
1260        assert_eq!(Some(4), pos);
1261    }
1262
1263    #[test]
1264    fn insert() {
1265        let mut s = LineBuffer::with_capacity(MAX_LINE);
1266        let push = s.insert('α', 1, &mut NoListener).unwrap();
1267        assert_eq!("α", s.buf);
1268        assert_eq!(2, s.pos);
1269        assert!(push);
1270
1271        let push = s.insert('ß', 1, &mut NoListener).unwrap();
1272        assert_eq!("αß", s.buf);
1273        assert_eq!(4, s.pos);
1274        assert!(push);
1275
1276        s.pos = 0;
1277        let push = s.insert('γ', 1, &mut NoListener).unwrap();
1278        assert_eq!("γαß", s.buf);
1279        assert_eq!(2, s.pos);
1280        assert!(!push);
1281    }
1282
1283    #[test]
1284    fn yank_after() {
1285        let mut s = LineBuffer::init("αß", 2);
1286        s.move_forward(1);
1287        let ok = s.yank("γδε", 1, &mut NoListener);
1288        assert_eq!(Some(true), ok);
1289        assert_eq!("αßγδε", s.buf);
1290        assert_eq!(10, s.pos);
1291    }
1292
1293    #[test]
1294    fn yank_before() {
1295        let mut s = LineBuffer::init("αε", 2);
1296        let ok = s.yank("ßγδ", 1, &mut NoListener);
1297        assert_eq!(Some(false), ok);
1298        assert_eq!("αßγδε", s.buf);
1299        assert_eq!(8, s.pos);
1300    }
1301
1302    #[test]
1303    fn moves() {
1304        let mut s = LineBuffer::init("αß", 4);
1305        let ok = s.move_backward(1);
1306        assert_eq!("αß", s.buf);
1307        assert_eq!(2, s.pos);
1308        assert!(ok);
1309
1310        let ok = s.move_forward(1);
1311        assert_eq!("αß", s.buf);
1312        assert_eq!(4, s.pos);
1313        assert!(ok);
1314
1315        let ok = s.move_home();
1316        assert_eq!("αß", s.buf);
1317        assert_eq!(0, s.pos);
1318        assert!(ok);
1319
1320        let ok = s.move_end();
1321        assert_eq!("αß", s.buf);
1322        assert_eq!(4, s.pos);
1323        assert!(ok);
1324    }
1325
1326    #[test]
1327    fn move_home_end_multiline() {
1328        let text = "αa\nsdf ßc\nasdf";
1329        let mut s = LineBuffer::init(text, 7);
1330        let ok = s.move_home();
1331        assert_eq!(text, s.buf);
1332        assert_eq!(4, s.pos);
1333        assert!(ok);
1334
1335        let ok = s.move_home();
1336        assert_eq!(text, s.buf);
1337        assert_eq!(4, s.pos);
1338        assert!(!ok);
1339
1340        let ok = s.move_end();
1341        assert_eq!(text, s.buf);
1342        assert_eq!(11, s.pos);
1343        assert!(ok);
1344
1345        let ok = s.move_end();
1346        assert_eq!(text, s.buf);
1347        assert_eq!(11, s.pos);
1348        assert!(!ok);
1349    }
1350
1351    #[test]
1352    fn move_buffer_multiline() {
1353        let text = "αa\nsdf ßc\nasdf";
1354        let mut s = LineBuffer::init(text, 7);
1355        let ok = s.move_buffer_start();
1356        assert_eq!(text, s.buf);
1357        assert_eq!(0, s.pos);
1358        assert!(ok);
1359
1360        let ok = s.move_buffer_start();
1361        assert_eq!(text, s.buf);
1362        assert_eq!(0, s.pos);
1363        assert!(!ok);
1364
1365        let ok = s.move_buffer_end();
1366        assert_eq!(text, s.buf);
1367        assert_eq!(text.len(), s.pos);
1368        assert!(ok);
1369
1370        let ok = s.move_buffer_end();
1371        assert_eq!(text, s.buf);
1372        assert_eq!(text.len(), s.pos);
1373        assert!(!ok);
1374    }
1375
1376    #[test]
1377    fn move_grapheme() {
1378        let mut s = LineBuffer::init("ag̈", 4);
1379        assert_eq!(4, s.len());
1380        let ok = s.move_backward(1);
1381        assert!(ok);
1382        assert_eq!(1, s.pos);
1383
1384        let ok = s.move_forward(1);
1385        assert!(ok);
1386        assert_eq!(4, s.pos);
1387    }
1388
1389    #[test]
1390    fn delete() {
1391        let mut cl = Listener::new();
1392        let mut s = LineBuffer::init("αß", 2);
1393        let chars = s.delete(1, &mut cl);
1394        assert_eq!("α", s.buf);
1395        assert_eq!(2, s.pos);
1396        assert_eq!(Some("ß".to_owned()), chars);
1397
1398        let ok = s.backspace(1, &mut cl);
1399        assert_eq!("", s.buf);
1400        assert_eq!(0, s.pos);
1401        assert!(ok);
1402        cl.assert_deleted_str_eq("α");
1403    }
1404
1405    #[test]
1406    fn kill() {
1407        let mut cl = Listener::new();
1408        let mut s = LineBuffer::init("αßγδε", 6);
1409        let ok = s.kill_line(&mut cl);
1410        assert_eq!("αßγ", s.buf);
1411        assert_eq!(6, s.pos);
1412        assert!(ok);
1413        cl.assert_deleted_str_eq("δε");
1414
1415        s.pos = 4;
1416        let ok = s.discard_line(&mut cl);
1417        assert_eq!("γ", s.buf);
1418        assert_eq!(0, s.pos);
1419        assert!(ok);
1420        cl.assert_deleted_str_eq("αß");
1421    }
1422
1423    #[test]
1424    fn kill_multiline() {
1425        let mut cl = Listener::new();
1426        let mut s = LineBuffer::init("αß\nγδ 12\nε f4", 7);
1427
1428        let ok = s.kill_line(&mut cl);
1429        assert_eq!("αß\nγ\nε f4", s.buf);
1430        assert_eq!(7, s.pos);
1431        assert!(ok);
1432        cl.assert_deleted_str_eq("δ 12");
1433
1434        let ok = s.kill_line(&mut cl);
1435        assert_eq!("αß\nγε f4", s.buf);
1436        assert_eq!(7, s.pos);
1437        assert!(ok);
1438        cl.assert_deleted_str_eq("\n");
1439
1440        let ok = s.kill_line(&mut cl);
1441        assert_eq!("αß\nγ", s.buf);
1442        assert_eq!(7, s.pos);
1443        assert!(ok);
1444        cl.assert_deleted_str_eq("ε f4");
1445
1446        let ok = s.kill_line(&mut cl);
1447        assert_eq!(7, s.pos);
1448        assert!(!ok);
1449    }
1450
1451    #[test]
1452    fn discard_multiline() {
1453        let mut cl = Listener::new();
1454        let mut s = LineBuffer::init("αß\nc γδε", 9);
1455
1456        let ok = s.discard_line(&mut cl);
1457        assert_eq!("αß\nδε", s.buf);
1458        assert_eq!(5, s.pos);
1459        assert!(ok);
1460        cl.assert_deleted_str_eq("c γ");
1461
1462        let ok = s.discard_line(&mut cl);
1463        assert_eq!("αßδε", s.buf);
1464        assert_eq!(4, s.pos);
1465        assert!(ok);
1466        cl.assert_deleted_str_eq("\n");
1467
1468        let ok = s.discard_line(&mut cl);
1469        assert_eq!("δε", s.buf);
1470        assert_eq!(0, s.pos);
1471        assert!(ok);
1472        cl.assert_deleted_str_eq("αß");
1473
1474        let ok = s.discard_line(&mut cl);
1475        assert_eq!(0, s.pos);
1476        assert!(!ok);
1477    }
1478
1479    #[test]
1480    fn transpose() {
1481        let mut s = LineBuffer::init("aßc", 1);
1482        let ok = s.transpose_chars(&mut NoListener);
1483        assert_eq!("ßac", s.buf);
1484        assert_eq!(3, s.pos);
1485        assert!(ok);
1486
1487        s.buf = String::from("aßc");
1488        s.pos = 3;
1489        let ok = s.transpose_chars(&mut NoListener);
1490        assert_eq!("acß", s.buf);
1491        assert_eq!(4, s.pos);
1492        assert!(ok);
1493
1494        s.buf = String::from("aßc");
1495        s.pos = 4;
1496        let ok = s.transpose_chars(&mut NoListener);
1497        assert_eq!("acß", s.buf);
1498        assert_eq!(4, s.pos);
1499        assert!(ok);
1500    }
1501
1502    #[test]
1503    fn move_to_prev_word() {
1504        let mut s = LineBuffer::init("a ß  c", 6); // before 'c'
1505        let ok = s.move_to_prev_word(Word::Emacs, 1);
1506        assert_eq!("a ß  c", s.buf);
1507        assert_eq!(2, s.pos); // before 'ß'
1508        assert!(ok);
1509
1510        assert!(s.move_end()); // after 'c'
1511        assert_eq!(7, s.pos);
1512        let ok = s.move_to_prev_word(Word::Emacs, 1);
1513        assert!(ok);
1514        assert_eq!(6, s.pos); // before 'c'
1515
1516        let ok = s.move_to_prev_word(Word::Emacs, 2);
1517        assert!(ok);
1518        assert_eq!(0, s.pos);
1519    }
1520
1521    #[test]
1522    fn move_to_prev_vi_word() {
1523        let mut s = LineBuffer::init("alpha ,beta/rho; mu", 19);
1524        let ok = s.move_to_prev_word(Word::Vi, 1);
1525        assert!(ok);
1526        assert_eq!(17, s.pos);
1527        let ok = s.move_to_prev_word(Word::Vi, 1);
1528        assert!(ok);
1529        assert_eq!(15, s.pos);
1530        let ok = s.move_to_prev_word(Word::Vi, 1);
1531        assert!(ok);
1532        assert_eq!(12, s.pos);
1533        let ok = s.move_to_prev_word(Word::Vi, 1);
1534        assert!(ok);
1535        assert_eq!(11, s.pos);
1536        let ok = s.move_to_prev_word(Word::Vi, 1);
1537        assert!(ok);
1538        assert_eq!(7, s.pos);
1539        let ok = s.move_to_prev_word(Word::Vi, 1);
1540        assert!(ok);
1541        assert_eq!(6, s.pos);
1542        let ok = s.move_to_prev_word(Word::Vi, 1);
1543        assert!(ok);
1544        assert_eq!(0, s.pos);
1545        let ok = s.move_to_prev_word(Word::Vi, 1);
1546        assert!(!ok);
1547    }
1548
1549    #[test]
1550    fn move_to_prev_big_word() {
1551        let mut s = LineBuffer::init("alpha ,beta/rho; mu", 19);
1552        let ok = s.move_to_prev_word(Word::Big, 1);
1553        assert!(ok);
1554        assert_eq!(17, s.pos);
1555        let ok = s.move_to_prev_word(Word::Big, 1);
1556        assert!(ok);
1557        assert_eq!(6, s.pos);
1558        let ok = s.move_to_prev_word(Word::Big, 1);
1559        assert!(ok);
1560        assert_eq!(0, s.pos);
1561        let ok = s.move_to_prev_word(Word::Big, 1);
1562        assert!(!ok);
1563    }
1564
1565    #[test]
1566    fn move_to_forward() {
1567        let mut s = LineBuffer::init("αßγδε", 2);
1568        let ok = s.move_to(CharSearch::ForwardBefore('ε'), 1);
1569        assert!(ok);
1570        assert_eq!(6, s.pos);
1571
1572        let mut s = LineBuffer::init("αßγδε", 2);
1573        let ok = s.move_to(CharSearch::Forward('ε'), 1);
1574        assert!(ok);
1575        assert_eq!(8, s.pos);
1576
1577        let mut s = LineBuffer::init("αßγδε", 2);
1578        let ok = s.move_to(CharSearch::Forward('ε'), 10);
1579        assert!(ok);
1580        assert_eq!(8, s.pos);
1581    }
1582
1583    #[test]
1584    fn move_to_backward() {
1585        let mut s = LineBuffer::init("αßγδε", 8);
1586        let ok = s.move_to(CharSearch::BackwardAfter('ß'), 1);
1587        assert!(ok);
1588        assert_eq!(4, s.pos);
1589
1590        let mut s = LineBuffer::init("αßγδε", 8);
1591        let ok = s.move_to(CharSearch::Backward('ß'), 1);
1592        assert!(ok);
1593        assert_eq!(2, s.pos);
1594    }
1595
1596    #[test]
1597    fn delete_prev_word() {
1598        let mut cl = Listener::new();
1599        let mut s = LineBuffer::init("a ß  c", 6);
1600        let ok = s.delete_prev_word(Word::Big, 1, &mut cl);
1601        assert_eq!("a c", s.buf);
1602        assert_eq!(2, s.pos);
1603        assert!(ok);
1604        cl.assert_deleted_str_eq("ß  ");
1605    }
1606
1607    #[test]
1608    fn move_to_next_word() {
1609        let mut s = LineBuffer::init("a ß  c", 1); // after 'a'
1610        let ok = s.move_to_next_word(At::AfterEnd, Word::Emacs, 1);
1611        assert_eq!("a ß  c", s.buf);
1612        assert!(ok);
1613        assert_eq!(4, s.pos); // after 'ß'
1614
1615        let ok = s.move_to_next_word(At::AfterEnd, Word::Emacs, 1);
1616        assert!(ok);
1617        assert_eq!(7, s.pos); // after 'c'
1618
1619        s.move_home();
1620        let ok = s.move_to_next_word(At::AfterEnd, Word::Emacs, 1);
1621        assert!(ok);
1622        assert_eq!(1, s.pos); // after 'a'
1623
1624        let ok = s.move_to_next_word(At::AfterEnd, Word::Emacs, 2);
1625        assert!(ok);
1626        assert_eq!(7, s.pos); // after 'c'
1627    }
1628
1629    #[test]
1630    fn move_to_end_of_word() {
1631        let mut s = LineBuffer::init("a ßeta  c", 1);
1632        let ok = s.move_to_next_word(At::BeforeEnd, Word::Vi, 1);
1633        assert_eq!("a ßeta  c", s.buf);
1634        assert_eq!(6, s.pos);
1635        assert!(ok);
1636    }
1637
1638    #[test]
1639    fn move_to_end_of_vi_word() {
1640        let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0);
1641        let ok = s.move_to_next_word(At::BeforeEnd, Word::Vi, 1);
1642        assert!(ok);
1643        assert_eq!(4, s.pos);
1644        let ok = s.move_to_next_word(At::BeforeEnd, Word::Vi, 1);
1645        assert!(ok);
1646        assert_eq!(6, s.pos);
1647        let ok = s.move_to_next_word(At::BeforeEnd, Word::Vi, 1);
1648        assert!(ok);
1649        assert_eq!(10, s.pos);
1650        let ok = s.move_to_next_word(At::BeforeEnd, Word::Vi, 1);
1651        assert!(ok);
1652        assert_eq!(11, s.pos);
1653        let ok = s.move_to_next_word(At::BeforeEnd, Word::Vi, 1);
1654        assert!(ok);
1655        assert_eq!(14, s.pos);
1656        let ok = s.move_to_next_word(At::BeforeEnd, Word::Vi, 1);
1657        assert!(ok);
1658        assert_eq!(15, s.pos);
1659        let ok = s.move_to_next_word(At::BeforeEnd, Word::Vi, 1);
1660        assert!(ok);
1661        assert_eq!(18, s.pos);
1662        let ok = s.move_to_next_word(At::BeforeEnd, Word::Vi, 1);
1663        assert!(!ok);
1664    }
1665
1666    #[test]
1667    fn move_to_end_of_big_word() {
1668        let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0);
1669        let ok = s.move_to_next_word(At::BeforeEnd, Word::Big, 1);
1670        assert!(ok);
1671        assert_eq!(4, s.pos);
1672        let ok = s.move_to_next_word(At::BeforeEnd, Word::Big, 1);
1673        assert!(ok);
1674        assert_eq!(15, s.pos);
1675        let ok = s.move_to_next_word(At::BeforeEnd, Word::Big, 1);
1676        assert!(ok);
1677        assert_eq!(18, s.pos);
1678        let ok = s.move_to_next_word(At::BeforeEnd, Word::Big, 1);
1679        assert!(!ok);
1680    }
1681
1682    #[test]
1683    fn move_to_start_of_word() {
1684        let mut s = LineBuffer::init("a ß  c", 2);
1685        let ok = s.move_to_next_word(At::Start, Word::Emacs, 1);
1686        assert_eq!("a ß  c", s.buf);
1687        assert_eq!(6, s.pos);
1688        assert!(ok);
1689    }
1690
1691    #[test]
1692    fn move_to_start_of_vi_word() {
1693        let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0);
1694        let ok = s.move_to_next_word(At::Start, Word::Vi, 1);
1695        assert!(ok);
1696        assert_eq!(6, s.pos);
1697        let ok = s.move_to_next_word(At::Start, Word::Vi, 1);
1698        assert!(ok);
1699        assert_eq!(7, s.pos);
1700        let ok = s.move_to_next_word(At::Start, Word::Vi, 1);
1701        assert!(ok);
1702        assert_eq!(11, s.pos);
1703        let ok = s.move_to_next_word(At::Start, Word::Vi, 1);
1704        assert!(ok);
1705        assert_eq!(12, s.pos);
1706        let ok = s.move_to_next_word(At::Start, Word::Vi, 1);
1707        assert!(ok);
1708        assert_eq!(15, s.pos);
1709        let ok = s.move_to_next_word(At::Start, Word::Vi, 1);
1710        assert!(ok);
1711        assert_eq!(17, s.pos);
1712        let ok = s.move_to_next_word(At::Start, Word::Vi, 1);
1713        assert!(ok);
1714        assert_eq!(18, s.pos);
1715        let ok = s.move_to_next_word(At::Start, Word::Vi, 1);
1716        assert!(!ok);
1717    }
1718
1719    #[test]
1720    fn move_to_start_of_big_word() {
1721        let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0);
1722        let ok = s.move_to_next_word(At::Start, Word::Big, 1);
1723        assert!(ok);
1724        assert_eq!(6, s.pos);
1725        let ok = s.move_to_next_word(At::Start, Word::Big, 1);
1726        assert!(ok);
1727        assert_eq!(17, s.pos);
1728        let ok = s.move_to_next_word(At::Start, Word::Big, 1);
1729        assert!(ok);
1730        assert_eq!(18, s.pos);
1731        let ok = s.move_to_next_word(At::Start, Word::Big, 1);
1732        assert!(!ok);
1733    }
1734
1735    #[test]
1736    fn delete_word() {
1737        let mut cl = Listener::new();
1738        let mut s = LineBuffer::init("a ß  c", 1);
1739        let ok = s.delete_word(At::AfterEnd, Word::Emacs, 1, &mut cl);
1740        assert_eq!("a  c", s.buf);
1741        assert_eq!(1, s.pos);
1742        assert!(ok);
1743        cl.assert_deleted_str_eq(" ß");
1744
1745        let mut s = LineBuffer::init("test", 0);
1746        let ok = s.delete_word(At::AfterEnd, Word::Vi, 1, &mut cl);
1747        assert_eq!("", s.buf);
1748        assert_eq!(0, s.pos);
1749        assert!(ok);
1750        cl.assert_deleted_str_eq("test");
1751    }
1752
1753    #[test]
1754    fn delete_til_start_of_word() {
1755        let mut cl = Listener::new();
1756        let mut s = LineBuffer::init("a ß  c", 2);
1757        let ok = s.delete_word(At::Start, Word::Emacs, 1, &mut cl);
1758        assert_eq!("a c", s.buf);
1759        assert_eq!(2, s.pos);
1760        assert!(ok);
1761        cl.assert_deleted_str_eq("ß  ");
1762    }
1763
1764    #[test]
1765    fn delete_to_forward() {
1766        let mut cl = Listener::new();
1767        let mut s = LineBuffer::init("αßγδε", 2);
1768        let ok = s.delete_to(CharSearch::ForwardBefore('ε'), 1, &mut cl);
1769        assert!(ok);
1770        cl.assert_deleted_str_eq("ßγδ");
1771        assert_eq!("αε", s.buf);
1772        assert_eq!(2, s.pos);
1773
1774        let mut s = LineBuffer::init("αßγδε", 2);
1775        let ok = s.delete_to(CharSearch::Forward('ε'), 1, &mut cl);
1776        assert!(ok);
1777        cl.assert_deleted_str_eq("ßγδε");
1778        assert_eq!("α", s.buf);
1779        assert_eq!(2, s.pos);
1780    }
1781
1782    #[test]
1783    fn delete_to_backward() {
1784        let mut cl = Listener::new();
1785        let mut s = LineBuffer::init("αßγδε", 8);
1786        let ok = s.delete_to(CharSearch::BackwardAfter('α'), 1, &mut cl);
1787        assert!(ok);
1788        cl.assert_deleted_str_eq("ßγδ");
1789        assert_eq!("αε", s.buf);
1790        assert_eq!(2, s.pos);
1791
1792        let mut s = LineBuffer::init("αßγδε", 8);
1793        let ok = s.delete_to(CharSearch::Backward('ß'), 1, &mut cl);
1794        assert!(ok);
1795        cl.assert_deleted_str_eq("ßγδ");
1796        assert_eq!("αε", s.buf);
1797        assert_eq!(2, s.pos);
1798    }
1799
1800    #[test]
1801    fn edit_word() {
1802        let mut s = LineBuffer::init("a ßeta  c", 1);
1803        assert!(s.edit_word(WordAction::Uppercase, &mut NoListener));
1804        assert_eq!("a SSETA  c", s.buf);
1805        assert_eq!(7, s.pos);
1806
1807        let mut s = LineBuffer::init("a ßetA  c", 1);
1808        assert!(s.edit_word(WordAction::Lowercase, &mut NoListener));
1809        assert_eq!("a ßeta  c", s.buf);
1810        assert_eq!(7, s.pos);
1811
1812        let mut s = LineBuffer::init("a ßETA  c", 1);
1813        assert!(s.edit_word(WordAction::Capitalize, &mut NoListener));
1814        assert_eq!("a SSeta  c", s.buf);
1815        assert_eq!(7, s.pos);
1816
1817        let mut s = LineBuffer::init("test", 1);
1818        assert!(s.edit_word(WordAction::Capitalize, &mut NoListener));
1819        assert_eq!("tEst", s.buf);
1820        assert_eq!(4, s.pos);
1821    }
1822
1823    #[test]
1824    fn transpose_words() {
1825        let mut s = LineBuffer::init("ßeta / δelta__", 15);
1826        assert!(s.transpose_words(1, &mut NoListener));
1827        assert_eq!("δelta__ / ßeta", s.buf);
1828        assert_eq!(16, s.pos);
1829
1830        let mut s = LineBuffer::init("ßeta / δelta", 14);
1831        assert!(s.transpose_words(1, &mut NoListener));
1832        assert_eq!("δelta / ßeta", s.buf);
1833        assert_eq!(14, s.pos);
1834
1835        let mut s = LineBuffer::init(" / δelta", 8);
1836        assert!(!s.transpose_words(1, &mut NoListener));
1837
1838        let mut s = LineBuffer::init("ßeta / __", 9);
1839        assert!(!s.transpose_words(1, &mut NoListener));
1840    }
1841
1842    #[test]
1843    fn move_by_line() {
1844        let text = "aa123\nsdf bc\nasdf";
1845        let mut s = LineBuffer::init(text, 14);
1846        let mut layout = Layout::default();
1847        // move up
1848        let ok = s.move_to_line_up(1, &layout);
1849        assert_eq!(7, s.pos);
1850        assert!(ok);
1851
1852        let ok = s.move_to_line_up(1, &layout);
1853        assert_eq!(1, s.pos);
1854        assert!(ok);
1855
1856        let ok = s.move_to_line_up(1, &layout);
1857        assert_eq!(1, s.pos);
1858        assert!(!ok);
1859
1860        // move down
1861        let ok = s.move_to_line_down(1, &layout);
1862        assert_eq!(7, s.pos);
1863        assert!(ok);
1864
1865        let ok = s.move_to_line_down(1, &layout);
1866        assert_eq!(14, s.pos);
1867        assert!(ok);
1868
1869        let ok = s.move_to_line_down(1, &layout);
1870        assert_eq!(14, s.pos);
1871        assert!(!ok);
1872
1873        // move by multiple steps
1874        let ok = s.move_to_line_up(2, &layout);
1875        assert_eq!(1, s.pos);
1876        assert!(ok);
1877
1878        let ok = s.move_to_line_down(2, &layout);
1879        assert_eq!(14, s.pos);
1880        assert!(ok);
1881
1882        // non-empty prompt
1883        layout.prompt_size.col = 2;
1884        s.move_to_line_up(1, &layout);
1885        assert_eq!(7, s.pos);
1886        s.move_to_line_up(1, &layout);
1887        assert_eq!(0, s.pos);
1888        s.move_to_line_down(1, &layout);
1889        assert_eq!(8, s.pos);
1890    }
1891
1892    #[test]
1893    fn test_send() {
1894        fn assert_send<T: Send>() {}
1895        assert_send::<LineBuffer>();
1896    }
1897
1898    #[test]
1899    fn test_sync() {
1900        fn assert_sync<T: Sync>() {}
1901        assert_sync::<LineBuffer>();
1902    }
1903}