diff --git a/crates/tinymist-query/src/upstream/complete.rs b/crates/tinymist-query/src/upstream/complete.rs index 91b5c500..11561900 100644 --- a/crates/tinymist-query/src/upstream/complete.rs +++ b/crates/tinymist-query/src/upstream/complete.rs @@ -995,8 +995,11 @@ impl<'a, 'w> CompletionContext<'a, 'w> { /// A small window of context before the cursor. fn before_window(&self, size: usize) -> &str { - // todo: bad slicing - &self.before[self.cursor.saturating_sub(size)..] + safe_str_slice( + self.before, + self.cursor.saturating_sub(size), + self.before.len(), + ) } /// Add a prefix and suffix to all applications. @@ -1265,3 +1268,20 @@ impl<'a, 'w> CompletionContext<'a, 'w> { } } } + +fn safe_str_slice(s: &str, mut start: usize, mut end: usize) -> &str { + // todo: bad slicing + // &self.before[self.cursor.saturating_sub(size)..] + while start < s.len() && !s.is_char_boundary(start) { + start += 1; + } + while end > 0 && !s.is_char_boundary(end) { + end -= 1; + } + + if end >= start { + &s[start..end] + } else { + "" + } +} diff --git a/crates/tinymist-query/src/upstream/complete/ext.rs b/crates/tinymist-query/src/upstream/complete/ext.rs index a2e99af2..7a007207 100644 --- a/crates/tinymist-query/src/upstream/complete/ext.rs +++ b/crates/tinymist-query/src/upstream/complete/ext.rs @@ -814,3 +814,19 @@ pub fn complete_path( .collect_vec(), ) } + +#[cfg(test)] + +mod tests { + use crate::upstream::complete::safe_str_slice; + + #[test] + fn test_before() { + const TEST_UTF8_STR: &str = "我们"; + for i in 0..=TEST_UTF8_STR.len() { + for j in 0..=TEST_UTF8_STR.len() { + let _s = std::hint::black_box(safe_str_slice(TEST_UTF8_STR, i, j)); + } + } + } +}