From a6ab0fe1e4d7392ee331c14a69eb27b51fbe84e4 Mon Sep 17 00:00:00 2001 From: Nikita Revenco <154856872+nikitarevenco@users.noreply.github.com> Date: Wed, 13 Nov 2024 04:01:18 +0000 Subject: [PATCH] feat: complete feature for find_nth_nearest_tag --- helix-core/src/surround.rs | 138 +++++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 27 deletions(-) diff --git a/helix-core/src/surround.rs b/helix-core/src/surround.rs index e67ace4a5..af01522f2 100644 --- a/helix-core/src/surround.rs +++ b/helix-core/src/surround.rs @@ -325,7 +325,9 @@ pub fn get_surround_pos_tag( for &range in selection { let cursor_pos = range.cursor(text); - let ((prev_tag, next_tag), _) = find_nearest_tag(text, cursor_pos, skip)?; + + let ((prev_tag, next_tag), tag_name) = find_nth_nearest_tag(text, cursor_pos, skip)?; + change_pos.push((prev_tag.from(), prev_tag.to())); change_pos.push((next_tag.from(), next_tag.to())); } @@ -341,36 +343,81 @@ pub fn is_valid_tagname_char(ch: char) -> bool { ch.is_alphanumeric() || ch == '_' || ch == '-' || ch == '.' } -/// Get the two `Range`s corresponding to matching tags surrounding the cursor, as well as the name of the tags. -pub fn find_nearest_tag( - text: RopeSlice, +/// Get the two sorted `Range`s corresponding to nth matching tags surrounding the cursor, as well as the name of the tags. +pub fn find_nth_nearest_tag( + forward_text: RopeSlice, cursor_pos: usize, skip: usize, ) -> Result<((Range, Range), String)> { - let mut next_tag_counter = 0; + let backward_text = forward_text.clone(); - let forward_cursor_pos = cursor_pos.clone(); - let forward_text = text.clone(); + let mut forward_tags = vec![]; + let mut previous_forward_pos = cursor_pos; - loop { - let (next_tag_range, next_tag) = find_next_tag(forward_text, forward_cursor_pos, skip)?; - next_tag_counter += 1; - if next_tag_counter == skip { - let mut backward_search_idx = cursor_pos; + /// the maximum length of chars we will search forward and backward to find the tags, provided we don't hit the end or the beginning of the document + const SEARCH_CHARS: usize = 2000; - loop { - let (prev_tag_range, prev_tag, last_search_idx) = - find_prev_tag(text, backward_search_idx, skip)?; + while (previous_forward_pos - cursor_pos) < SEARCH_CHARS + && previous_forward_pos < forward_text.len_chars() + { + let (forward_tag_range, forward_tag_name, forward_search_idx) = + find_next_tag(forward_text, previous_forward_pos, skip)?; + + forward_tags.push((forward_tag_range, forward_tag_name)); + previous_forward_pos = forward_search_idx; + } - backward_search_idx = last_search_idx; + let mut backward_tags = vec![]; + let mut previous_backward_pos = cursor_pos; - dbg!(&prev_tag_range, &prev_tag); + while (cursor_pos - previous_backward_pos) < SEARCH_CHARS && previous_backward_pos > 0 { + let (backward_tag_range, backward_tag_name, backward_search_idx) = + find_prev_tag(backward_text, previous_backward_pos, skip)?; - if prev_tag == next_tag { - return Ok(((prev_tag_range, next_tag_range), prev_tag)); - } - } - } + backward_tags.push((backward_tag_range, backward_tag_name)); + previous_backward_pos = backward_search_idx; + } + + // only consider the tags which are common in both vectors + let backward_tags: Vec<(Range, String)> = backward_tags + .into_iter() + .filter(|(_, backward_tag_name)| { + forward_tags + .iter() + .any(|(_, forward_tag_name)| backward_tag_name == forward_tag_name) + }) + .collect(); + let forward_tags: Vec<(Range, String)> = forward_tags + .into_iter() + .filter(|(_, forward_tag_name)| { + backward_tags + .iter() + .any(|(_, backward_tag_name)| forward_tag_name == backward_tag_name) + }) + .collect(); + + // improperly ordered tags such as