@@ -88,11 +88,11 @@ pub fn move_next_word_start(slice: RopeSlice, mut begin: usize, count: usize) ->
89 if is_word(ch) {
89 if is_word(ch) {
90 skip_over_next(slice, &mut end, is_word);
90 skip_over_next(slice, &mut end, is_word);
91− } else if ch.is_ascii_punctuation() {
91+ } else if is_punctuation(ch) {
92− skip_over_next(slice, &mut end, |ch| ch.is_ascii_punctuation());
92+ skip_over_next(slice, &mut end, is_punctuation);
95− skip_over_next(slice, &mut end, is_horiz_blank);
95+ skip_over_next(slice, &mut end, char::is_whitespace);
98 Some(Range::new(begin, end - 1))
98 Some(Range::new(begin, end - 1))
@@ -119,15 +119,15 @@ pub fn move_prev_word_start(slice: RopeSlice, mut begin: usize, count: usize) ->
120 end = begin;
120 end = begin;
122− with_end = skip_over_prev(slice, &mut end, is_horiz_blank);
122+ with_end = skip_over_prev(slice, &mut end, char::is_whitespace);
124 // refetch
124 // refetch
125 let ch = slice.char(end);
125 let ch = slice.char(end);
127 if is_word(ch) {
127 if is_word(ch) {
128 with_end = skip_over_prev(slice, &mut end, is_word);
128 with_end = skip_over_prev(slice, &mut end, is_word);
129− } else if ch.is_ascii_punctuation() {
129+ } else if is_punctuation(ch) {
130− with_end = skip_over_prev(slice, &mut end, |ch| ch.is_ascii_punctuation());
130+ with_end = skip_over_prev(slice, &mut end, is_punctuation);
@@ -155,15 +155,15 @@ pub fn move_next_word_end(slice: RopeSlice, mut begin: usize, count: usize) -> O
156 end = begin;
156 end = begin;
158− skip_over_next(slice, &mut end, is_horiz_blank);
158+ skip_over_next(slice, &mut end, char::is_whitespace);
160 // refetch
160 // refetch
161 let ch = slice.char(end);
161 let ch = slice.char(end);
163 if is_word(ch) {
163 if is_word(ch) {
164 skip_over_next(slice, &mut end, is_word);
164 skip_over_next(slice, &mut end, is_word);
165− } else if ch.is_ascii_punctuation() {
165+ } else if is_punctuation(ch) {
166− skip_over_next(slice, &mut end, |ch| ch.is_ascii_punctuation());
166+ skip_over_next(slice, &mut end, is_punctuation);
@@ -174,12 +174,28 @@ pub fn move_next_word_end(slice: RopeSlice, mut begin: usize, count: usize) -> O
175// used for by-word movement
175// used for by-word movement
177pub(crate) fn is_word(ch: char) -> bool {
178pub(crate) fn is_word(ch: char) -> bool {
178 ch.is_alphanumeric() || ch == '_'
179 ch.is_alphanumeric() || ch == '_'
181−pub(crate) fn is_horiz_blank(ch: char) -> bool {
183+pub(crate) fn is_punctuation(ch: char) -> bool {
184+ use unicode_general_category::{get_general_category, GeneralCategory};
182− matches!(ch, ' ' | '\t')
186+ matches!(
187+ get_general_category(ch),
188+ GeneralCategory::OtherPunctuation
189+ | GeneralCategory::OpenPunctuation
190+ | GeneralCategory::ClosePunctuation
191+ | GeneralCategory::InitialPunctuation
192+ | GeneralCategory::FinalPunctuation
193+ | GeneralCategory::ConnectorPunctuation
194+ | GeneralCategory::DashPunctuation
195+ | GeneralCategory::MathSymbol
196+ | GeneralCategory::CurrencySymbol
197+ | GeneralCategory::ModifierSymbol
185#[derive(Debug, Eq, PartialEq)]
201#[derive(Debug, Eq, PartialEq)]
@@ -191,14 +207,15 @@ pub(crate) enum Category {
194pub(crate) fn categorize(ch: char) -> Category {
211pub(crate) fn categorize(ch: char) -> Category {
195 if ch == '\n' {
212 if ch == '\n' {
196 Category::Eol
213 Category::Eol
197− } else if ch.is_ascii_whitespace() {
214+ } else if ch.is_whitespace() {
198 Category::Whitespace
215 Category::Whitespace
199 } else if is_word(ch) {
216 } else if is_word(ch) {
200 Category::Word
217 Category::Word
201− } else if ch.is_ascii_punctuation() {
218+ } else if is_punctuation(ch) {
202 Category::Punctuation
219 Category::Punctuation
204 Category::Unknown
221 Category::Unknown
@@ -213,6 +230,7 @@ where
214 let mut chars = slice.chars_at(*pos);
231 let mut chars = slice.chars_at(*pos);
233+ #[allow(clippy::while_let_on_iterator)]
216 while let Some(ch) = chars.next() {
234 while let Some(ch) = chars.next() {
217 if !fun(ch) {
235 if !fun(ch) {
@@ -231,6 +249,7 @@ where
231 // need to +1 so that prev() includes current char
249 // need to +1 so that prev() includes current char
232 let mut chars = slice.chars_at(*pos + 1);
250 let mut chars = slice.chars_at(*pos + 1);
252+ #[allow(clippy::while_let_on_iterator)]
234 while let Some(ch) = chars.prev() {
253 while let Some(ch) = chars.prev() {
235 if !fun(ch) {
254 if !fun(ch) {
@@ -259,4 +278,44 @@ mod test {
259 (1, 2).into()
278 (1, 2).into()
283+ fn test_categorize() {
284+ const WORD_TEST_CASE: &'static str =
285+ "_hello_world_あいうえおー12345678901234567890";
286+ const PUNCTUATION_TEST_CASE: &'static str = "!\"#$%&\'()*+,-./:;<=>?@[\\]^`{|}~!”#$%&’()*+、。:;<=>?@「」^`{|}~";
287+ const WHITESPACE_TEST_CASE: &'static str = " ";
289+ assert_eq!(Category::Eol, categorize('\n'));
291+ for ch in WHITESPACE_TEST_CASE.chars() {
293+ Category::Whitespace,
295+ "Testing '{}', but got `{:?}` instead of `Category::Whitespace`",
301+ for ch in WORD_TEST_CASE.chars() {
305+ "Testing '{}', but got `{:?}` instead of `Category::Word`",
311+ for ch in PUNCTUATION_TEST_CASE.chars() {
313+ Category::Punctuation,
315+ "Testing '{}', but got `{:?}` instead of `Category::Punctuation`",