rustyline/
line_buffer.rs

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