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