pepper/
buffer_view.rs

1use crate::{
2    buffer::{
3        Buffer, BufferCollection, BufferHandle, BufferIndentationConfig, CharDisplayDistances,
4    },
5    buffer_history::EditKind,
6    buffer_position::{BufferPosition, BufferPositionIndex, BufferRange},
7    client::ClientHandle,
8    cursor::{Cursor, CursorCollection},
9    events::{BufferEditMutGuard, EditorEventTextInsert, EditorEventWriter},
10    word_database::{WordDatabase, WordIter, WordKind},
11};
12
13pub enum CursorMovement {
14    ColumnsForward(usize),
15    ColumnsBackward(usize),
16    LinesForward { count: usize, tab_size: u8 },
17    LinesBackward { count: usize, tab_size: u8 },
18    WordsForward(usize),
19    WordsBackward(usize),
20    WordEndForward(usize),
21    Home,
22    HomeNonWhitespace,
23    End,
24    FirstLine,
25    LastLine,
26}
27
28#[derive(Clone, Copy)]
29pub enum CursorMovementKind {
30    PositionAndAnchor,
31    PositionOnly,
32}
33
34pub struct BufferView {
35    alive: bool,
36    handle: BufferViewHandle,
37    pub client_handle: ClientHandle,
38    pub buffer_handle: BufferHandle,
39    pub cursors: CursorCollection,
40    pub(crate) scroll: BufferPositionIndex,
41}
42
43impl BufferView {
44    pub fn handle(&self) -> BufferViewHandle {
45        self.handle
46    }
47
48    fn reset(&mut self, client_handle: ClientHandle, buffer_handle: BufferHandle) {
49        self.alive = true;
50        self.client_handle = client_handle;
51        self.buffer_handle = buffer_handle;
52        self.cursors.mut_guard().clear();
53    }
54
55    pub fn move_cursors(
56        &mut self,
57        buffers: &BufferCollection,
58        movement: CursorMovement,
59        movement_kind: CursorMovementKind,
60    ) {
61        fn try_nth<I, E>(iter: I, mut n: usize) -> Result<E, usize>
62        where
63            I: Iterator<Item = E>,
64        {
65            for e in iter {
66                if n == 0 {
67                    return Ok(e);
68                }
69                n -= 1;
70            }
71            Err(n)
72        }
73
74        let buffer = buffers.get(self.buffer_handle).content();
75
76        let mut cursors = self.cursors.mut_guard();
77        match movement {
78            CursorMovement::ColumnsForward(n) => {
79                let last_line_index = buffer.lines().len() - 1;
80                for c in &mut cursors[..] {
81                    let line = buffer.lines()[c.position.line_index as usize].as_str();
82                    match try_nth(
83                        line[c.position.column_byte_index as usize..].char_indices(),
84                        n,
85                    ) {
86                        Ok((i, _)) => c.position.column_byte_index += i as BufferPositionIndex,
87                        Err(0) => c.position.column_byte_index = line.len() as _,
88                        Err(mut n) => {
89                            n -= 1;
90                            loop {
91                                if c.position.line_index == last_line_index as _ {
92                                    c.position.column_byte_index =
93                                        buffer.lines()[last_line_index].as_str().len() as _;
94                                    break;
95                                }
96
97                                c.position.line_index += 1;
98                                let line = buffer.lines()[c.position.line_index as usize].as_str();
99                                match try_nth(line.char_indices(), n) {
100                                    Ok((i, _)) => {
101                                        c.position.column_byte_index = i as _;
102                                        break;
103                                    }
104                                    Err(0) => {
105                                        c.position.column_byte_index = line.len() as _;
106                                        break;
107                                    }
108                                    Err(rest) => n = rest - 1,
109                                }
110                            }
111                        }
112                    }
113                }
114            }
115            CursorMovement::ColumnsBackward(n) => {
116                if n == 0 {
117                    return;
118                }
119                let n = n - 1;
120
121                for c in &mut cursors[..] {
122                    let line = buffer.lines()[c.position.line_index as usize].as_str();
123                    match try_nth(
124                        line[..c.position.column_byte_index as usize]
125                            .char_indices()
126                            .rev(),
127                        n,
128                    ) {
129                        Ok((i, _)) => c.position.column_byte_index = i as _,
130                        Err(0) => {
131                            if c.position.line_index == 0 {
132                                c.position.column_byte_index = 0;
133                            } else {
134                                c.position.line_index -= 1;
135                                c.position.column_byte_index =
136                                    buffer.lines()[c.position.line_index as usize]
137                                        .as_str()
138                                        .len() as _;
139                            }
140                        }
141                        Err(mut n) => {
142                            n -= 1;
143                            loop {
144                                if c.position.line_index == 0 {
145                                    c.position.column_byte_index = 0;
146                                    break;
147                                }
148
149                                c.position.line_index -= 1;
150                                let line = buffer.lines()[c.position.line_index as usize].as_str();
151                                match try_nth(line.char_indices().rev(), n) {
152                                    Ok((i, _)) => {
153                                        c.position.column_byte_index = i as _;
154                                        break;
155                                    }
156                                    Err(0) => {
157                                        if c.position.line_index == 0 {
158                                            c.position.column_byte_index = 0;
159                                        } else {
160                                            c.position.line_index -= 1;
161                                            c.position.column_byte_index = buffer.lines()
162                                                [c.position.line_index as usize]
163                                                .as_str()
164                                                .len()
165                                                as _;
166                                        }
167                                        break;
168                                    }
169                                    Err(rest) => n = rest - 1,
170                                }
171                            }
172                        }
173                    }
174                }
175            }
176            CursorMovement::LinesForward { count: n, tab_size } => {
177                cursors.save_display_distances(buffer, tab_size);
178                for i in 0..cursors[..].len() {
179                    let saved_display_distance = cursors.get_saved_display_distance(i);
180                    let c = &mut cursors[i];
181                    c.position.line_index = buffer
182                        .lines()
183                        .len()
184                        .saturating_sub(1)
185                        .min(c.position.line_index as usize + n)
186                        as _;
187                    if let Some(distance) = saved_display_distance {
188                        let line = buffer.lines()[c.position.line_index as usize].as_str();
189                        c.position.column_byte_index = CharDisplayDistances::new(line, tab_size)
190                            .find(|d| d.distance > distance as _)
191                            .map(|d| d.char_index as usize)
192                            .unwrap_or(line.len())
193                            as _;
194                    }
195                    c.position = buffer.saturate_position(c.position);
196                }
197            }
198            CursorMovement::LinesBackward { count: n, tab_size } => {
199                cursors.save_display_distances(buffer, tab_size);
200                for i in 0..cursors[..].len() {
201                    let saved_display_distance = cursors.get_saved_display_distance(i);
202                    let c = &mut cursors[i];
203                    c.position.line_index = c.position.line_index.saturating_sub(n as _);
204                    if let Some(distance) = saved_display_distance {
205                        let line = buffer.lines()[c.position.line_index as usize].as_str();
206                        c.position.column_byte_index = CharDisplayDistances::new(line, tab_size)
207                            .find(|d| d.distance > distance as _)
208                            .map(|d| d.char_index as usize)
209                            .unwrap_or(line.len())
210                            as _;
211                    }
212                    c.position = buffer.saturate_position(c.position);
213                }
214            }
215            CursorMovement::WordsForward(n) => {
216                let last_line_index = buffer.lines().len() - 1;
217                for c in &mut cursors[..] {
218                    let mut n = n;
219                    let mut line = buffer.lines()[c.position.line_index as usize].as_str();
220
221                    while n > 0 {
222                        if c.position.column_byte_index == line.len() as _ {
223                            if c.position.line_index == last_line_index as _ {
224                                break;
225                            }
226
227                            c.position.line_index += 1;
228                            c.position.column_byte_index = 0;
229                            line = buffer.lines()[c.position.line_index as usize].as_str();
230                            n -= 1;
231                            continue;
232                        }
233
234                        let words = WordIter(&line[c.position.column_byte_index as usize..])
235                            .inspect(|w| {
236                                c.position.column_byte_index += w.text.len() as BufferPositionIndex
237                            })
238                            .skip(1)
239                            .filter(|w| w.kind != WordKind::Whitespace);
240
241                        match try_nth(words, n - 1) {
242                            Ok(word) => {
243                                c.position.column_byte_index -=
244                                    word.text.len() as BufferPositionIndex;
245                                break;
246                            }
247                            Err(rest) => {
248                                n = rest;
249                                c.position.column_byte_index = line.len() as _;
250                            }
251                        }
252                    }
253                }
254            }
255            CursorMovement::WordsBackward(n) => {
256                for c in &mut cursors[..] {
257                    let mut n = n;
258                    let mut line = &buffer.lines()[c.position.line_index as usize].as_str()
259                        [..c.position.column_byte_index as usize];
260
261                    while n > 0 {
262                        let mut last_kind = WordKind::Identifier;
263                        let words = WordIter(line)
264                            .rev()
265                            .inspect(|w| {
266                                c.position.column_byte_index -= w.text.len() as BufferPositionIndex;
267                                last_kind = w.kind;
268                            })
269                            .filter(|w| w.kind != WordKind::Whitespace);
270
271                        match try_nth(words, n - 1) {
272                            Ok(_) => break,
273                            Err(rest) => n = rest + 1,
274                        }
275
276                        if last_kind == WordKind::Whitespace {
277                            n -= 1;
278                            if n == 0 {
279                                break;
280                            }
281                        }
282
283                        if c.position.line_index == 0 {
284                            break;
285                        }
286
287                        c.position.line_index -= 1;
288                        line = buffer.lines()[c.position.line_index as usize].as_str();
289                        c.position.column_byte_index = line.len() as _;
290                        n -= 1;
291                    }
292                }
293            }
294            CursorMovement::WordEndForward(n) => {
295                let last_line_index = buffer.lines().len() - 1;
296                for c in &mut cursors[..] {
297                    let mut n = n;
298                    let mut line = buffer.lines()[c.position.line_index as usize].as_str();
299
300                    while n > 0 {
301                        if c.position.column_byte_index == line.len() as _ {
302                            if c.position.line_index == last_line_index as _ {
303                                break;
304                            }
305
306                            c.position.line_index += 1;
307                            c.position.column_byte_index = 0;
308                            line = buffer.lines()[c.position.line_index as usize].as_str();
309                            n -= 1;
310                            continue;
311                        }
312
313                        let words = WordIter(&line[c.position.column_byte_index as usize..])
314                            .inspect(|w| {
315                                c.position.column_byte_index += w.text.len() as BufferPositionIndex
316                            })
317                            .filter(|w| w.kind != WordKind::Whitespace);
318
319                        match try_nth(words, n - 1) {
320                            Ok(word) => {
321                                //c.position.column_byte_index -=
322                                word.text.len() as BufferPositionIndex;
323                                break;
324                            }
325                            Err(rest) => {
326                                n = rest;
327                                c.position.column_byte_index = line.len() as _;
328                            }
329                        }
330                    }
331                }
332            }
333            CursorMovement::Home => {
334                for c in &mut cursors[..] {
335                    c.position.column_byte_index = 0;
336                }
337            }
338            CursorMovement::HomeNonWhitespace => {
339                for c in &mut cursors[..] {
340                    let first_word = buffer.lines()[c.position.line_index as usize].word_at(0);
341                    match first_word.kind {
342                        WordKind::Whitespace => {
343                            c.position.column_byte_index = first_word.text.len() as _
344                        }
345                        _ => c.position.column_byte_index = 0,
346                    }
347                }
348            }
349            CursorMovement::End => {
350                for c in &mut cursors[..] {
351                    c.position.column_byte_index = buffer.lines()[c.position.line_index as usize]
352                        .as_str()
353                        .len() as _;
354                }
355            }
356            CursorMovement::FirstLine => {
357                for c in &mut cursors[..] {
358                    c.position.line_index = 0;
359                    c.position = buffer.saturate_position(c.position);
360                }
361            }
362            CursorMovement::LastLine => {
363                for c in &mut cursors[..] {
364                    c.position.line_index = (buffer.lines().len() - 1) as _;
365                    c.position = buffer.saturate_position(c.position);
366                }
367            }
368        }
369
370        if let CursorMovementKind::PositionAndAnchor = movement_kind {
371            for c in &mut cursors[..] {
372                c.anchor = c.position;
373            }
374        }
375    }
376
377    pub fn append_selection_text_and_ranges(
378        &self,
379        buffers: &BufferCollection,
380        text: &mut String,
381        ranges: &mut Vec<(BufferPositionIndex, BufferPositionIndex)>,
382    ) {
383        let buffer = buffers.get(self.buffer_handle).content();
384        let mut iter = self.cursors[..].iter();
385        if let Some(cursor) = iter.next() {
386            let mut last_range = cursor.to_range();
387            let from = text.len() as _;
388            for t in buffer.text_range(last_range) {
389                text.push_str(t);
390            }
391            ranges.push((from, text.len() as _));
392
393            for cursor in iter {
394                let range = cursor.to_range();
395                if range.from.line_index > last_range.to.line_index {
396                    text.push('\n');
397                }
398                let from = text.len() as _;
399                for t in buffer.text_range(range) {
400                    text.push_str(t);
401                }
402                ranges.push((from, text.len() as _));
403
404                last_range = range;
405            }
406        }
407    }
408
409    pub fn insert_text_at_cursor_positions(
410        &self,
411        buffers: &mut BufferCollection,
412        word_database: &mut WordDatabase,
413        text: &str,
414        events: &mut EditorEventWriter,
415    ) {
416        let buffer = buffers.get_mut(self.buffer_handle);
417        let mut events = events.buffer_text_inserts_mut_guard(self.buffer_handle);
418        for cursor in self.cursors[..].iter().rev() {
419            buffer.insert_text(word_database, cursor.position, text, &mut events);
420        }
421    }
422
423    pub fn delete_text_in_cursor_ranges(
424        &self,
425        buffers: &mut BufferCollection,
426        word_database: &mut WordDatabase,
427        events: &mut EditorEventWriter,
428    ) {
429        let buffer = buffers.get_mut(self.buffer_handle);
430        let mut events = events.buffer_range_deletes_mut_guard(self.buffer_handle);
431        for cursor in self.cursors[..].iter().rev() {
432            buffer.delete_range(word_database, cursor.to_range(), &mut events);
433        }
434    }
435
436    pub fn fix_indentation_in_cursor_ranges(
437        &self,
438        indentation_config: BufferIndentationConfig,
439        buffers: &mut BufferCollection,
440        events: &mut EditorEventWriter,
441    ) {
442        let buffer = buffers.get_mut(self.buffer_handle);
443        let mut events = BufferEditMutGuard::new(events, self.buffer_handle);
444
445        let mut previous_fix_line_index = BufferPositionIndex::MAX;
446        for cursor in &self.cursors[..] {
447            let range = cursor.to_range();
448            let from_line_index = previous_fix_line_index
449                .wrapping_add(1)
450                .max(range.from.line_index);
451            let to_line_index = range.to.line_index;
452            previous_fix_line_index = to_line_index;
453
454            for line_index in from_line_index..=to_line_index {
455                buffer.fix_line_indentation(indentation_config, line_index as _, &mut events);
456            }
457        }
458    }
459
460    pub fn find_completion_positions(
461        &self,
462        buffers: &mut BufferCollection,
463        positions: &mut Vec<BufferPosition>,
464    ) {
465        positions.clear();
466
467        let buffer = buffers.get_mut(self.buffer_handle).content();
468        for cursor in self.cursors[..].iter() {
469            let position = buffer.position_before(cursor.position);
470            let word = buffer.word_at(position);
471            match word.kind {
472                WordKind::Identifier => positions.push(word.position),
473                _ => positions.push(cursor.position),
474            }
475        }
476    }
477
478    pub fn apply_completion(
479        &self,
480        buffers: &mut BufferCollection,
481        word_database: &mut WordDatabase,
482        completion: &str,
483        positions: &[BufferPosition],
484        events: &mut EditorEventWriter,
485    ) {
486        let buffer = buffers.get_mut(self.buffer_handle);
487        for (cursor, &position) in self.cursors[..].iter().zip(positions.iter()).rev() {
488            let range = BufferRange::between(position, cursor.position);
489            buffer.delete_range(
490                word_database,
491                range,
492                &mut events.buffer_range_deletes_mut_guard(self.buffer_handle),
493            );
494            buffer.insert_text(
495                word_database,
496                position,
497                completion,
498                &mut events.buffer_text_inserts_mut_guard(self.buffer_handle),
499            );
500        }
501    }
502
503    pub fn undo(
504        &mut self,
505        buffers: &mut BufferCollection,
506        word_database: &mut WordDatabase,
507        events: &mut EditorEventWriter,
508    ) {
509        let edits = buffers
510            .get_mut(self.buffer_handle)
511            .undo(word_database, events);
512
513        let mut fix_cursors = events.fix_cursors_mut_guard(self.handle);
514
515        let mut last_edit_kind = None;
516        for edit in edits {
517            if last_edit_kind != Some(edit.kind) {
518                fix_cursors.clear();
519            }
520            let position = match edit.kind {
521                EditKind::Insert => edit.range.to,
522                EditKind::Delete => edit.range.from,
523            };
524            fix_cursors.add(Cursor {
525                anchor: edit.range.from,
526                position,
527            });
528
529            last_edit_kind = Some(edit.kind);
530        }
531    }
532
533    pub fn redo(
534        &mut self,
535        buffers: &mut BufferCollection,
536        word_database: &mut WordDatabase,
537        events: &mut EditorEventWriter,
538    ) {
539        let edits = buffers
540            .get_mut(self.buffer_handle)
541            .redo(word_database, events);
542
543        let mut fix_cursors = events.fix_cursors_mut_guard(self.handle);
544
545        let mut last_edit_kind = None;
546        for edit in edits {
547            if last_edit_kind != Some(edit.kind) {
548                fix_cursors.clear();
549            }
550            let position = match edit.kind {
551                EditKind::Insert => {
552                    for cursor in fix_cursors.cursors() {
553                        cursor.insert(edit.range);
554                    }
555                    edit.range.to
556                }
557                EditKind::Delete => {
558                    for cursor in fix_cursors.cursors() {
559                        cursor.delete(edit.range);
560                    }
561                    edit.range.from
562                }
563            };
564
565            fix_cursors.add(Cursor {
566                anchor: edit.range.from,
567                position,
568            });
569
570            last_edit_kind = Some(edit.kind);
571        }
572    }
573}
574
575#[derive(Clone, Copy, Eq, PartialEq)]
576pub struct BufferViewHandle(u32);
577
578#[derive(Default)]
579pub struct BufferViewCollection {
580    buffer_views: Vec<BufferView>,
581}
582
583impl BufferViewCollection {
584    pub fn add_new(
585        &mut self,
586        client_handle: ClientHandle,
587        buffer_handle: BufferHandle,
588    ) -> BufferViewHandle {
589        for (i, view) in self.buffer_views.iter_mut().enumerate() {
590            if !view.alive {
591                view.reset(client_handle, buffer_handle);
592                return BufferViewHandle(i as _);
593            }
594        }
595        let handle = BufferViewHandle(self.buffer_views.len() as _);
596        self.buffer_views.push(BufferView {
597            alive: true,
598            handle,
599            client_handle,
600            buffer_handle,
601            cursors: CursorCollection::new(),
602            scroll: 0,
603        });
604        handle
605    }
606
607    pub fn remove_buffer_views_with_client(&mut self, client_handle: ClientHandle) {
608        for view in &mut self.buffer_views {
609            if view.alive && view.client_handle == client_handle {
610                view.alive = false;
611            }
612        }
613    }
614
615    pub fn remove_buffer_views_with_buffer(&mut self, buffer_handle: BufferHandle) {
616        for view in &mut self.buffer_views {
617            if view.alive && view.buffer_handle == buffer_handle {
618                view.alive = false;
619            }
620        }
621    }
622
623    pub fn get(&self, handle: BufferViewHandle) -> &BufferView {
624        &self.buffer_views[handle.0 as usize]
625    }
626
627    pub fn get_mut(&mut self, handle: BufferViewHandle) -> &mut BufferView {
628        &mut self.buffer_views[handle.0 as usize]
629    }
630
631    pub fn iter(&self) -> impl Iterator<Item = &BufferView> {
632        self.buffer_views.iter().filter(|v| v.alive)
633    }
634
635    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut BufferView> {
636        self.buffer_views.iter_mut().filter(|v| v.alive)
637    }
638
639    pub fn buffer_view_handle_from_buffer_handle(
640        &mut self,
641        client_handle: ClientHandle,
642        buffer_handle: BufferHandle,
643    ) -> BufferViewHandle {
644        let current_buffer_view_handle = self
645            .buffer_views
646            .iter()
647            .position(|v| {
648                v.alive && v.buffer_handle == buffer_handle && v.client_handle == client_handle
649            })
650            .map(|i| BufferViewHandle(i as _));
651
652        match current_buffer_view_handle {
653            Some(handle) => handle,
654            None => self.add_new(client_handle, buffer_handle),
655        }
656    }
657
658    pub(crate) fn on_buffer_text_inserts(
659        &mut self,
660        buffer_handle: BufferHandle,
661        inserts: &[EditorEventTextInsert],
662    ) {
663        for view in self.iter_mut() {
664            if view.buffer_handle == buffer_handle {
665                let mut cursors = view.cursors.mut_guard();
666                for insert in inserts {
667                    let range = insert.range;
668                    for c in &mut cursors[..] {
669                        c.insert(range);
670                    }
671                }
672            }
673        }
674    }
675
676    pub(crate) fn on_buffer_range_deletes(
677        &mut self,
678        buffer_handle: BufferHandle,
679        deletes: &[BufferRange],
680    ) {
681        for view in self.iter_mut() {
682            if view.buffer_handle == buffer_handle {
683                let mut cursors = view.cursors.mut_guard();
684                for &range in deletes {
685                    for c in &mut cursors[..] {
686                        c.delete(range);
687                    }
688                }
689            }
690        }
691    }
692
693    pub(crate) fn on_buffer_read(&mut self, buffer: &Buffer) {
694        let buffer_handle = buffer.handle();
695        let buffer = buffer.content();
696
697        for view in self.iter_mut() {
698            if view.buffer_handle == buffer_handle {
699                for c in &mut view.cursors.mut_guard()[..] {
700                    c.anchor = buffer.saturate_position(c.anchor);
701                    c.position = buffer.saturate_position(c.position);
702                }
703            }
704        }
705    }
706}
707
708#[cfg(test)]
709mod tests {
710    use super::*;
711
712    use std::ops::Range;
713
714    use crate::{
715        buffer::BufferProperties, buffer_position::BufferPosition, events::EditorEventQueue,
716    };
717
718    struct TestContext {
719        pub buffers: BufferCollection,
720        pub buffer_views: BufferViewCollection,
721        pub buffer_view_handle: BufferViewHandle,
722    }
723
724    impl TestContext {
725        pub fn with_buffer(text: &str) -> Self {
726            let mut events = EditorEventQueue::default();
727            let mut word_database = WordDatabase::new();
728
729            let mut buffers = BufferCollection::default();
730            let buffer = buffers.add_new();
731            buffer.properties = BufferProperties::text();
732            buffer.insert_text(
733                &mut word_database,
734                BufferPosition::zero(),
735                text,
736                &mut events
737                    .writer()
738                    .buffer_text_inserts_mut_guard(buffer.handle()),
739            );
740
741            let mut buffer_views = BufferViewCollection::default();
742            let buffer_view_handle = buffer_views.add_new(ClientHandle(0), buffer.handle());
743
744            Self {
745                buffers,
746                buffer_views,
747                buffer_view_handle,
748            }
749        }
750    }
751
752    #[test]
753    fn buffer_view_cursor_movement() {
754        fn set_cursor(ctx: &mut TestContext, position: BufferPosition) {
755            let buffer_view = ctx.buffer_views.get_mut(ctx.buffer_view_handle);
756            let mut cursors = buffer_view.cursors.mut_guard();
757            cursors.clear();
758            cursors.add(Cursor {
759                anchor: position,
760                position,
761            });
762        }
763
764        fn main_cursor_position(ctx: &TestContext) -> BufferPosition {
765            ctx.buffer_views
766                .get(ctx.buffer_view_handle)
767                .cursors
768                .main_cursor()
769                .position
770        }
771
772        fn assert_movement(
773            ctx: &mut TestContext,
774            from: Range<usize>,
775            to: Range<usize>,
776            movement: CursorMovement,
777        ) {
778            set_cursor(
779                ctx,
780                BufferPosition::line_col(from.start as _, from.end as _),
781            );
782            ctx.buffer_views
783                .get_mut(ctx.buffer_view_handle)
784                .move_cursors(
785                    &ctx.buffers,
786                    movement,
787                    CursorMovementKind::PositionAndAnchor,
788                );
789            assert_eq!(
790                BufferPosition::line_col(to.start as _, to.end as _),
791                main_cursor_position(ctx)
792            );
793        }
794
795        let mut ctx = TestContext::with_buffer("ab\nc e\nefgh\ni k\nlm");
796        assert_movement(&mut ctx, 2..2, 2..2, CursorMovement::ColumnsForward(0));
797        assert_movement(&mut ctx, 2..2, 2..3, CursorMovement::ColumnsForward(1));
798        assert_movement(&mut ctx, 2..2, 2..4, CursorMovement::ColumnsForward(2));
799        assert_movement(&mut ctx, 2..2, 3..0, CursorMovement::ColumnsForward(3));
800        assert_movement(&mut ctx, 2..2, 3..3, CursorMovement::ColumnsForward(6));
801        assert_movement(&mut ctx, 2..2, 4..0, CursorMovement::ColumnsForward(7));
802        assert_movement(&mut ctx, 2..2, 4..2, CursorMovement::ColumnsForward(999));
803
804        assert_movement(&mut ctx, 2..2, 2..2, CursorMovement::ColumnsBackward(0));
805        assert_movement(&mut ctx, 2..2, 2..1, CursorMovement::ColumnsBackward(1));
806        assert_movement(&mut ctx, 2..0, 1..3, CursorMovement::ColumnsBackward(1));
807        assert_movement(&mut ctx, 2..2, 1..3, CursorMovement::ColumnsBackward(3));
808        assert_movement(&mut ctx, 2..2, 0..2, CursorMovement::ColumnsBackward(7));
809        assert_movement(&mut ctx, 2..2, 0..0, CursorMovement::ColumnsBackward(999));
810
811        assert_movement(&mut ctx, 2..2, 2..2, CursorMovement::WordsForward(0));
812        assert_movement(&mut ctx, 2..0, 2..4, CursorMovement::WordsForward(1));
813        assert_movement(&mut ctx, 2..0, 3..0, CursorMovement::WordsForward(2));
814        assert_movement(&mut ctx, 2..2, 3..2, CursorMovement::WordsForward(3));
815        assert_movement(&mut ctx, 2..2, 3..3, CursorMovement::WordsForward(4));
816        assert_movement(&mut ctx, 2..2, 4..0, CursorMovement::WordsForward(5));
817        assert_movement(&mut ctx, 2..2, 4..2, CursorMovement::WordsForward(6));
818        assert_movement(&mut ctx, 2..2, 4..2, CursorMovement::WordsForward(999));
819
820        assert_movement(&mut ctx, 2..2, 2..2, CursorMovement::WordsBackward(0));
821        assert_movement(&mut ctx, 2..0, 1..3, CursorMovement::WordsBackward(1));
822        assert_movement(&mut ctx, 2..0, 1..2, CursorMovement::WordsBackward(2));
823        assert_movement(&mut ctx, 2..2, 2..0, CursorMovement::WordsBackward(1));
824        assert_movement(&mut ctx, 2..2, 1..3, CursorMovement::WordsBackward(2));
825        assert_movement(&mut ctx, 2..2, 1..2, CursorMovement::WordsBackward(3));
826        assert_movement(&mut ctx, 2..2, 1..0, CursorMovement::WordsBackward(4));
827        assert_movement(&mut ctx, 2..2, 0..2, CursorMovement::WordsBackward(5));
828        assert_movement(&mut ctx, 2..2, 0..0, CursorMovement::WordsBackward(6));
829        assert_movement(&mut ctx, 2..2, 0..0, CursorMovement::WordsBackward(999));
830
831        let mut ctx = TestContext::with_buffer("123\n  abc def\nghi");
832        assert_movement(&mut ctx, 1..0, 1..2, CursorMovement::WordsForward(1));
833        assert_movement(&mut ctx, 1..9, 2..0, CursorMovement::WordsForward(1));
834        assert_movement(&mut ctx, 1..2, 1..0, CursorMovement::WordsBackward(1));
835        assert_movement(&mut ctx, 2..0, 1..9, CursorMovement::WordsBackward(1));
836    }
837}