From 953125d3f381880f288afa2b59f9936e76d534fa Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Thu, 5 Aug 2021 17:32:33 -0700 Subject: [PATCH] Fix around-word text-object selection. (#546) * Fix around-word text-object selection. * Text object around-word: select to the left if no whitespace on the right. Also only select around when there's whitespace at all. * Make select-word-around select all white space on a side. * Update commented-out test case. * Fix unused import warning from rebase. --- helix-core/src/textobject.rs | 61 +++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/helix-core/src/textobject.rs b/helix-core/src/textobject.rs index b59ff6fa923e..b965f6dfccc0 100644 --- a/helix-core/src/textobject.rs +++ b/helix-core/src/textobject.rs @@ -1,7 +1,7 @@ use ropey::RopeSlice; -use crate::chars::{categorize_char, CharCategory}; -use crate::graphemes::{next_grapheme_boundary, prev_grapheme_boundary}; +use crate::chars::{categorize_char, char_is_whitespace, CharCategory}; +use crate::graphemes::next_grapheme_boundary; use crate::movement::Direction; use crate::surround; use crate::Range; @@ -73,19 +73,23 @@ pub fn textobject_word( match textobject { TextObject::Inside => Range::new(word_start, word_end), - TextObject::Around => Range::new( - match slice - .get_char(word_start.saturating_sub(1)) - .map(categorize_char) - { - None | Some(CharCategory::Eol) => word_start, - _ => prev_grapheme_boundary(slice, word_start), - }, - match slice.get_char(word_end).map(categorize_char) { - None | Some(CharCategory::Eol) => word_end, - _ => next_grapheme_boundary(slice, word_end), - }, - ), + TextObject::Around => { + let whitespace_count_right = slice + .chars_at(word_end) + .take_while(|c| char_is_whitespace(*c)) + .count(); + + if whitespace_count_right > 0 { + Range::new(word_start, word_end + whitespace_count_right) + } else { + let whitespace_count_left = { + let mut iter = slice.chars_at(word_start); + iter.reverse(); + iter.take_while(|c| char_is_whitespace(*c)).count() + }; + Range::new(word_start - whitespace_count_left, word_end) + } + } } } @@ -126,9 +130,9 @@ mod test { (13, Inside, (10, 16)), (10, Inside, (10, 16)), (15, Inside, (10, 16)), - (13, Around, (9, 17)), - (10, Around, (9, 17)), - (15, Around, (9, 17)), + (13, Around, (10, 17)), + (10, Around, (10, 17)), + (15, Around, (10, 17)), ], ), ( @@ -167,9 +171,9 @@ mod test { (13, Inside, (10, 16)), (10, Inside, (10, 16)), (15, Inside, (10, 16)), - (13, Around, (9, 17)), - (10, Around, (9, 17)), - (15, Around, (9, 17)), + (13, Around, (10, 17)), + (10, Around, (10, 17)), + (15, Around, (10, 17)), ], ), ( @@ -178,10 +182,9 @@ mod test { (14, Inside, (14, 21)), (20, Inside, (14, 21)), (17, Inside, (14, 21)), - (14, Around, (13, 22)), - // FIXME: edge case - // (20, Around, (14, 20)), - (17, Around, (13, 22)), + (14, Around, (14, 21)), + (20, Around, (14, 21)), + (17, Around, (14, 21)), ], ), ( @@ -195,6 +198,14 @@ mod test { (11, Around, (11, 11)), ], ), + ( + "cursor on word with extra whitespace", + vec![(11, Inside, (10, 14)), (11, Around, (10, 17))], + ), + ( + "cursor at end with extra whitespace", + vec![(28, Inside, (27, 37)), (28, Around, (24, 37))], + ), ( "cursor at end of doc", vec![(19, Inside, (17, 20)), (19, Around, (16, 20))],