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 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}