@@ -1,7 +1,7 @@
11use ropey::RopeSlice;
22
3−use crate::chars::{categorize_char, CharCategory};
4−use crate::graphemes::{next_grapheme_boundary, prev_grapheme_boundary};
3+use crate::chars::{categorize_char, char_is_whitespace, CharCategory};
4+use crate::graphemes::next_grapheme_boundary;
55use crate::movement::Direction;
66use crate::surround;
77use crate::Range;
@@ -73,19 +73,23 @@ pub fn textobject_word(
7373
7474 match textobject {
7575 TextObject::Inside => Range::new(word_start, word_end),
76− TextObject::Around => Range::new(
77− match slice
78− .get_char(word_start.saturating_sub(1))
79− .map(categorize_char)
80− {
81− None | Some(CharCategory::Eol) => word_start,
82− _ => prev_grapheme_boundary(slice, word_start),
83− },
84− match slice.get_char(word_end).map(categorize_char) {
85− None | Some(CharCategory::Eol) => word_end,
86− _ => next_grapheme_boundary(slice, word_end),
87− },
88− ),
76+ TextObject::Around => {
77+ let whitespace_count_right = slice
78+ .chars_at(word_end)
79+ .take_while(|c| char_is_whitespace(*c))
80+ .count();
81+
82+ if whitespace_count_right > 0 {
83+ Range::new(word_start, word_end + whitespace_count_right)
84+ } else {
85+ let whitespace_count_left = {
86+ let mut iter = slice.chars_at(word_start);
87+ iter.reverse();
88+ iter.take_while(|c| char_is_whitespace(*c)).count()
89+ };
90+ Range::new(word_start - whitespace_count_left, word_end)
91+ }
92+ }
8993 }
9094}
9195
@@ -126,9 +130,9 @@ mod test {
126130 (13, Inside, (10, 16)),
127131 (10, Inside, (10, 16)),
128132 (15, Inside, (10, 16)),
129− (13, Around, (9, 17)),
130− (10, Around, (9, 17)),
131− (15, Around, (9, 17)),
133+ (13, Around, (10, 17)),
134+ (10, Around, (10, 17)),
135+ (15, Around, (10, 17)),
132136 ],
133137 ),
134138 (
@@ -167,9 +171,9 @@ mod test {
167171 (13, Inside, (10, 16)),
168172 (10, Inside, (10, 16)),
169173 (15, Inside, (10, 16)),
170− (13, Around, (9, 17)),
171− (10, Around, (9, 17)),
172− (15, Around, (9, 17)),
174+ (13, Around, (10, 17)),
175+ (10, Around, (10, 17)),
176+ (15, Around, (10, 17)),
173177 ],
174178 ),
175179 (
@@ -178,10 +182,9 @@ mod test {
178182 (14, Inside, (14, 21)),
179183 (20, Inside, (14, 21)),
180184 (17, Inside, (14, 21)),
181− (14, Around, (13, 22)),
182− // FIXME: edge case
183− // (20, Around, (14, 20)),
184− (17, Around, (13, 22)),
185+ (14, Around, (14, 21)),
186+ (20, Around, (14, 21)),
187+ (17, Around, (14, 21)),
185188 ],
186189 ),
187190 (
@@ -195,6 +198,14 @@ mod test {
195198 (11, Around, (11, 11)),
196199 ],
197200 ),
201+ (
202+ "cursor on word with extra whitespace",
203+ vec![(11, Inside, (10, 14)), (11, Around, (10, 17))],
204+ ),
205+ (
206+ "cursor at end with extra whitespace",
207+ vec![(28, Inside, (27, 37)), (28, Around, (24, 37))],
208+ ),
198209 (
199210 "cursor at end of doc",
200211 vec![(19, Inside, (17, 20)), (19, Around, (16, 20))],