@@ -1,7 +1,7 @@
1use ropey::RopeSlice;
1use ropey::RopeSlice;
3−use crate::chars::{categorize_char, CharCategory};
3+use crate::chars::{categorize_char, char_is_whitespace, CharCategory};
4−use crate::graphemes::{next_grapheme_boundary, prev_grapheme_boundary};
4+use crate::graphemes::next_grapheme_boundary;
5use crate::movement::Direction;
5use crate::movement::Direction;
6use crate::surround;
6use crate::surround;
7use crate::Range;
7use crate::Range;
@@ -73,19 +73,23 @@ pub fn textobject_word(
74 match textobject {
74 match textobject {
75 TextObject::Inside => Range::new(word_start, word_end),
75 TextObject::Inside => Range::new(word_start, word_end),
76− TextObject::Around => Range::new(
76+ TextObject::Around => {
77− match slice
77+ let whitespace_count_right = slice
78− .get_char(word_start.saturating_sub(1))
78+ .chars_at(word_end)
79− .map(categorize_char)
79+ .take_while(|c| char_is_whitespace(*c))
81− None | Some(CharCategory::Eol) => word_start,
81+
82− _ => prev_grapheme_boundary(slice, word_start),
82+ if whitespace_count_right > 0 {
83− },
83+ Range::new(word_start, word_end + whitespace_count_right)
84− match slice.get_char(word_end).map(categorize_char) {
84+ } else {
85− None | Some(CharCategory::Eol) => word_end,
85+ let whitespace_count_left = {
86− _ => next_grapheme_boundary(slice, word_end),
86+ let mut iter = slice.chars_at(word_start);
88+ iter.take_while(|c| char_is_whitespace(*c)).count()
90+ Range::new(word_start - whitespace_count_left, word_end)
@@ -126,9 +130,9 @@ mod test {
126 (13, Inside, (10, 16)),
130 (13, Inside, (10, 16)),
127 (10, Inside, (10, 16)),
131 (10, Inside, (10, 16)),
128 (15, Inside, (10, 16)),
132 (15, Inside, (10, 16)),
129− (13, Around, (9, 17)),
133+ (13, Around, (10, 17)),
130− (10, Around, (9, 17)),
134+ (10, Around, (10, 17)),
131− (15, Around, (9, 17)),
135+ (15, Around, (10, 17)),
@@ -167,9 +171,9 @@ mod test {
167 (13, Inside, (10, 16)),
171 (13, Inside, (10, 16)),
168 (10, Inside, (10, 16)),
172 (10, Inside, (10, 16)),
169 (15, Inside, (10, 16)),
173 (15, Inside, (10, 16)),
170− (13, Around, (9, 17)),
174+ (13, Around, (10, 17)),
171− (10, Around, (9, 17)),
175+ (10, Around, (10, 17)),
172− (15, Around, (9, 17)),
176+ (15, Around, (10, 17)),
@@ -178,10 +182,9 @@ mod test {
178 (14, Inside, (14, 21)),
182 (14, Inside, (14, 21)),
179 (20, Inside, (14, 21)),
183 (20, Inside, (14, 21)),
180 (17, Inside, (14, 21)),
184 (17, Inside, (14, 21)),
181− (14, Around, (13, 22)),
185+ (14, Around, (14, 21)),
183− // (20, Around, (14, 20)),
186+ (20, Around, (14, 21)),
184− (17, Around, (13, 22)),
187+ (17, Around, (14, 21)),
@@ -195,6 +198,14 @@ mod test {
195 (11, Around, (11, 11)),
198 (11, Around, (11, 11)),
202+ "cursor on word with extra whitespace",
203+ vec![(11, Inside, (10, 14)), (11, Around, (10, 17))],
206+ "cursor at end with extra whitespace",
207+ vec![(28, Inside, (27, 37)), (28, Around, (24, 37))],
199 "cursor at end of doc",
210 "cursor at end of doc",
200 vec![(19, Inside, (17, 20)), (19, Around, (16, 20))],
211 vec![(19, Inside, (17, 20)), (19, Around, (16, 20))],