Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the performance of bracket search in no language mode #2816

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 7 additions & 3 deletions lapce-app/src/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1596,14 +1596,18 @@ impl Document {
pub fn find_enclosing_brackets(&self, offset: usize) -> Option<(usize, usize)> {
self.syntax
.with_untracked(|syntax| {
(!syntax.text.is_empty()).then(|| syntax.find_enclosing_pair(offset))
if !syntax.text.is_empty() {
syntax.find_enclosing_pair(offset)
} else {
None
}
})
// If syntax.text is empty, either the buffer is empty or we don't have syntax support
// for the current language.
// Try a language unaware search for enclosing brackets in case it is the latter.
.unwrap_or_else(|| {
.or_else(|| {
self.buffer.with_untracked(|buffer| {
WordCursor::new(buffer.text(), offset).find_enclosing_pair()
WordCursor::find_enclosing_pair(buffer.text(), offset)
})
})
}
Expand Down
2 changes: 2 additions & 0 deletions lapce-core/src/syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@ impl Syntax {
builder.build()
}

/// Returns the matching bracket of the character at the given `offset`.
pub fn find_matching_pair(&self, offset: usize) -> Option<usize> {
let tree = self.layers.as_ref()?.try_tree()?;
let node = tree
Expand Down Expand Up @@ -748,6 +749,7 @@ impl Syntax {
} else {
node.next_sibling()
} {

if sibling.kind() == tag {
let offset = sibling.start_byte();
return Some(offset);
Expand Down
4 changes: 3 additions & 1 deletion lapce-core/src/syntax/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ impl<'a> TextProvider<'a> for RopeProvider<'a> {
}
}

/// If the character is an opening bracket return Some(true), if closing, return Some(false)
/// If the character `c` is an opening bracket return `Some(true)`.
/// If the character `c` is an closing bracket return `Some(false)`.
/// Otherwise return `None`.
pub fn matching_pair_direction(c: char) -> Option<bool> {
Some(match c {
'{' => true,
Expand Down
91 changes: 50 additions & 41 deletions lapce-core/src/word.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,40 +408,54 @@ impl<'a> WordCursor<'a> {
(start, end)
}

/// Return the enclosing brackets of the current position
///
/// **Example**:
///
///```rust
/// # use lapce_core::word::WordCursor;
/// # use lapce_xi_rope::Rope;
/// let text = "outer {{inner} world";
/// let rope = Rope::from(text);
/// let mut cursor = WordCursor::new(&rope, 10);
/// let (start, end) = cursor.find_enclosing_pair().unwrap();
/// assert_eq!(start, 7);
/// assert_eq!(end, 13)
///```
pub fn find_enclosing_pair(&mut self) -> Option<(usize, usize)> {
let old_offset = self.inner.pos();
while let Some(c) = self.inner.prev_codepoint() {
if matching_pair_direction(c) == Some(true) {
let opening_bracket_offset = self.inner.pos();
if let Some(closing_bracket_offset) = self.match_pairs() {
if (opening_bracket_offset..=closing_bracket_offset)
.contains(&old_offset)
{
return Some((
opening_bracket_offset,
closing_bracket_offset,
));
} else {
self.inner.set(opening_bracket_offset);
/// Return the enclosing brackets of the current position.
pub fn find_enclosing_pair<'b>(
text: &'a Rope,
offset: usize,
) -> Option<(usize, usize)> {
let mut left_cursor = Cursor::new(text, offset);
let mut right_cursor = Cursor::new(text, offset);
let mut right_cache: Vec<(usize, char)> = Vec::new();

// `backward_cursor` only goes backward while `forward_cursor` moves while storing the list of closing brackets.
loop {
let left_char = left_cursor.prev_codepoint()?;
let left_pos = left_cursor.pos();
let left_matching_char = matching_char(left_char);
let is_open_bracket = matching_pair_direction(left_char) == Some(true);

match (left_matching_char, is_open_bracket) {
(Some(left_matching_char), true) => {
// Find in the right cache first
let right_cache_exists =
right_cache.iter().find_map(|(idx, u)| {
if *u == left_matching_char {
return Some(*idx);
}
None
});

if let Some(idx) = right_cache_exists {
return Some((left_pos, idx));
}

// Otherwise walk the right cursor forward
loop {
let right_pos = right_cursor.pos();
let right_char = right_cursor.next_codepoint()?;
let is_closing_bracket =
matching_pair_direction(right_char) == Some(false);

if is_closing_bracket && left_matching_char == right_char {
return Some((left_pos, right_pos));
}

right_cache.push((left_cursor.pos(), right_char));
}
}
}
_ => continue,
};
}
None
}
}

Expand Down Expand Up @@ -679,8 +693,7 @@ mod test {
fn find_pair_should_return_positions() {
let text = "violet (are) blue";
let rope = Rope::from(text);
let mut cursor = WordCursor::new(&rope, 9);
let positions = cursor.find_enclosing_pair();
let positions = WordCursor::find_enclosing_pair(&rope, 9);
assert_eq!(positions, Some((7, 11)));
}

Expand All @@ -689,25 +702,21 @@ mod test {
let text = "violets {are (blue) }";
let rope = Rope::from(text);

let mut cursor = WordCursor::new(&rope, 11);
let positions = cursor.find_enclosing_pair();
let positions = WordCursor::find_enclosing_pair(&rope, 11);
assert_eq!(positions, Some((8, 23)));

let mut cursor = WordCursor::new(&rope, 20);
let positions = cursor.find_enclosing_pair();
let positions = WordCursor::find_enclosing_pair(&rope, 20);
assert_eq!(positions, Some((8, 23)));

let mut cursor = WordCursor::new(&rope, 18);
let positions = cursor.find_enclosing_pair();
let positions = WordCursor::find_enclosing_pair(&rope, 18);
assert_eq!(positions, Some((13, 18)));
}

#[test]
fn find_pair_should_return_none() {
let text = "violet (are) blue";
let rope = Rope::from(text);
let mut cursor = WordCursor::new(&rope, 1);
let positions = cursor.find_enclosing_pair();
let positions = WordCursor::find_enclosing_pair(&rope, 1);
assert_eq!(positions, None);
}
}