@@ -5,16 +5,15 @@ use tree_sitter::{Node, QueryCursor};
6use crate::{
6use crate::{
7 chars::{categorize_char, char_is_line_ending, CharCategory},
7 chars::{categorize_char, char_is_line_ending, CharCategory},
9 graphemes::{
8 graphemes::{
10 next_grapheme_boundary, nth_next_grapheme_boundary, nth_prev_grapheme_boundary,
9 next_grapheme_boundary, nth_next_grapheme_boundary, nth_prev_grapheme_boundary,
11 prev_grapheme_boundary,
10 prev_grapheme_boundary,
13 line_ending::rope_is_line_ending,
12 line_ending::rope_is_line_ending,
14− pos_at_coords,
13+ pos_at_visual_coords,
15 syntax::LanguageConfiguration,
14 syntax::LanguageConfiguration,
16 textobject::TextObject,
15 textobject::TextObject,
17− Position, Range, RopeSlice,
16+ visual_coords_at_pos, Position, Range, RopeSlice,
20#[derive(Debug, Copy, Clone, PartialEq, Eq)]
19#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -35,6 +34,7 @@ pub fn move_horizontally(
35 dir: Direction,
34 dir: Direction,
36 count: usize,
35 count: usize,
37 behaviour: Movement,
36 behaviour: Movement,
38) -> Range {
38) -> Range {
39 let pos = range.cursor(slice);
39 let pos = range.cursor(slice);
@@ -54,15 +54,12 @@ pub fn move_vertically(
54 dir: Direction,
54 dir: Direction,
55 count: usize,
55 count: usize,
56 behaviour: Movement,
56 behaviour: Movement,
57) -> Range {
58) -> Range {
58 let pos = range.cursor(slice);
59 let pos = range.cursor(slice);
60 // Compute the current position's 2d coordinates.
61 // Compute the current position's 2d coordinates.
61− // TODO: switch this to use `visual_coords_at_pos` rather than
62− // `coords_at_pos` as this will cause a jerky movement when the visual
63− // position does not match, like moving from a line with tabs/CJK to
65− let Position { row, col } = coords_at_pos(slice, pos);
62+ let Position { row, col } = visual_coords_at_pos(slice, pos, tab_width);
66 let horiz = range.horiz.unwrap_or(col as u32);
63 let horiz = range.horiz.unwrap_or(col as u32);
68 // Compute the new position.
65 // Compute the new position.
@@ -71,7 +68,7 @@ pub fn move_vertically(
71 Direction::Backward => row.saturating_sub(count),
68 Direction::Backward => row.saturating_sub(count),
73 let new_col = col.max(horiz as usize);
70 let new_col = col.max(horiz as usize);
74− let new_pos = pos_at_coords(slice, Position::new(new_row, new_col), true);
71+ let new_pos = pos_at_visual_coords(slice, Position::new(new_row, new_col), tab_width);
76 // Special-case to avoid moving to the end of the last non-empty line.
73 // Special-case to avoid moving to the end of the last non-empty line.
77 if behaviour == Movement::Extend && slice.line(new_row).len_chars() == 0 {
74 if behaviour == Movement::Extend && slice.line(new_row).len_chars() == 0 {
@@ -446,6 +443,8 @@ pub fn goto_treesitter_object(
446mod test {
443mod test {
447 use ropey::Rope;
444 use ropey::Rope;
446+ use crate::{coords_at_pos, pos_at_coords};
449 use super::*;
448 use super::*;
451 const SINGLE_LINE_SAMPLE: &str = "This is a simple alphabetic line";
450 const SINGLE_LINE_SAMPLE: &str = "This is a simple alphabetic line";
@@ -472,7 +471,7 @@ mod test {
472 assert_eq!(
471 assert_eq!(
473 coords_at_pos(
472 coords_at_pos(
475− move_vertically(slice, range, Direction::Forward, 1, Movement::Move).head
474+ move_vertically(slice, range, Direction::Forward, 1, Movement::Move, 4).head
477 (1, 3).into()
476 (1, 3).into()
@@ -496,7 +495,7 @@ mod test {
498 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
497 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
499− range = move_horizontally(slice, range, direction, amount, Movement::Move);
498+ range = move_horizontally(slice, range, direction, amount, Movement::Move, 0);
500 assert_eq!(coords_at_pos(slice, range.head), coordinates.into())
499 assert_eq!(coords_at_pos(slice, range.head), coordinates.into())
@@ -522,7 +521,7 @@ mod test {
524 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
523 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
525− range = move_horizontally(slice, range, direction, amount, Movement::Move);
524+ range = move_horizontally(slice, range, direction, amount, Movement::Move, 0);
526 assert_eq!(coords_at_pos(slice, range.head), coordinates.into());
525 assert_eq!(coords_at_pos(slice, range.head), coordinates.into());
527 assert_eq!(range.head, range.anchor);
526 assert_eq!(range.head, range.anchor);
@@ -544,7 +543,7 @@ mod test {
546 for (direction, amount) in moves {
545 for (direction, amount) in moves {
547− range = move_horizontally(slice, range, direction, amount, Movement::Extend);
546+ range = move_horizontally(slice, range, direction, amount, Movement::Extend, 0);
548 assert_eq!(range.anchor, original_anchor);
547 assert_eq!(range.anchor, original_anchor);
@@ -568,7 +567,7 @@ mod test {
570 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
569 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
571− range = move_vertically(slice, range, direction, amount, Movement::Move);
570+ range = move_vertically(slice, range, direction, amount, Movement::Move, 4);
572 assert_eq!(coords_at_pos(slice, range.head), coordinates.into());
571 assert_eq!(coords_at_pos(slice, range.head), coordinates.into());
573 assert_eq!(range.head, range.anchor);
572 assert_eq!(range.head, range.anchor);
@@ -602,8 +601,8 @@ mod test {
603 for ((axis, direction, amount), coordinates) in moves_and_expected_coordinates {
602 for ((axis, direction, amount), coordinates) in moves_and_expected_coordinates {
604 range = match axis {
603 range = match axis {
605− Axis::H => move_horizontally(slice, range, direction, amount, Movement::Move),
604+ Axis::H => move_horizontally(slice, range, direction, amount, Movement::Move, 0),
606− Axis::V => move_vertically(slice, range, direction, amount, Movement::Move),
605+ Axis::V => move_vertically(slice, range, direction, amount, Movement::Move, 4),
608 assert_eq!(coords_at_pos(slice, range.head), coordinates.into());
607 assert_eq!(coords_at_pos(slice, range.head), coordinates.into());
609 assert_eq!(range.head, range.anchor);
608 assert_eq!(range.head, range.anchor);
@@ -627,18 +626,18 @@ mod test {
627 let moves_and_expected_coordinates = [
626 let moves_and_expected_coordinates = [
628 // Places cursor at the fourth kana.
627 // Places cursor at the fourth kana.
629 ((Axis::H, Direction::Forward, 4), (0, 4)),
628 ((Axis::H, Direction::Forward, 4), (0, 4)),
630− // Descent places cursor at the 4th character.
629+ // Descent places cursor at the 8th character.
631− ((Axis::V, Direction::Forward, 1usize), (1, 4)),
630+ ((Axis::V, Direction::Forward, 1usize), (1, 8)),
632− // Moving back 1 character.
631+ // Moving back 2 characters.
633− ((Axis::H, Direction::Backward, 1usize), (1, 3)),
632+ ((Axis::H, Direction::Backward, 2usize), (1, 6)),
634 // Jumping back up 1 line.
633 // Jumping back up 1 line.
635 ((Axis::V, Direction::Backward, 1usize), (0, 3)),
634 ((Axis::V, Direction::Backward, 1usize), (0, 3)),
638 for ((axis, direction, amount), coordinates) in moves_and_expected_coordinates {
637 for ((axis, direction, amount), coordinates) in moves_and_expected_coordinates {
639 range = match axis {
638 range = match axis {
640− Axis::H => move_horizontally(slice, range, direction, amount, Movement::Move),
639+ Axis::H => move_horizontally(slice, range, direction, amount, Movement::Move, 0),
641− Axis::V => move_vertically(slice, range, direction, amount, Movement::Move),
640+ Axis::V => move_vertically(slice, range, direction, amount, Movement::Move, 4),
643 assert_eq!(coords_at_pos(slice, range.head), coordinates.into());
642 assert_eq!(coords_at_pos(slice, range.head), coordinates.into());
644 assert_eq!(range.head, range.anchor);
643 assert_eq!(range.head, range.anchor);