From 12d9fcee0e84657f3c0788dfc7bc1881249a4e95 Mon Sep 17 00:00:00 2001 From: Nikita Revenco <154856872+nikitarevenco@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:20:38 +0000 Subject: [PATCH] feat: add function to get next tag's position and test for it --- helix-core/src/surround.rs | 147 ++++++++++++++++++++++++------------- helix-term/src/commands.rs | 11 ++- 2 files changed, 104 insertions(+), 54 deletions(-) diff --git a/helix-core/src/surround.rs b/helix-core/src/surround.rs index e3d2d2253..7c3394e7e 100644 --- a/helix-core/src/surround.rs +++ b/helix-core/src/surround.rs @@ -154,6 +154,29 @@ fn find_nth_closest_pairs_plain( Err(Error::PairNotFound) } +/// Find the position of surrounding tags around cursor +/// `n` will skip n - 1 tags (e.g. n=2 will discard only the first tag found) +/// and keep looking +// pub fn find_nth_tag_pos( +// text: RopeSlice, +// range: Range, +// n: usize, +// ) -> Result<((usize, usize), (usize, usize))> { +// // 7 is the minimum possible length for a tag, e.g. . +// if text.len_chars() < 7 { +// return Err(Error::PairNotFound); +// } + +// if range.to() >= text.len_chars() { +// return Err(Error::RangeExceedsText); +// } + +// let cursor_position = range.cursor(text); + +// let open = (); +// let close = (); +// } + /// Find the position of surround pairs of `ch` which can be either a closing /// or opening pair. `n` will skip n - 1 pairs (eg. n=2 will discard (only) /// the first pair found and keep looking) @@ -314,54 +337,82 @@ pub fn get_surround_pos( Ok(change_pos) } -fn find_nth_close_tag( - text: RopeSlice, - tag: &str, - mut pos: usize, - n: usize, -) -> Result<(Vec, Vec)> { - // if text.len_chars() < 2 { - // return Err(Error::PairNotFound); - // } - // if range.to() >= text.len_chars() { - // return Err(Error::RangeExceedsText); - // } - // - Ok((vec![4, 7], vec![10, 13])) -} - /// like get_surround_pos, but for Tags pub fn get_surround_pos_tag( text: RopeSlice, selection: &Selection, - tag: &str, skip: usize, -) -> Result> { - let mut change_pos = Vec::new(); +) -> Result> { + // // let change_pos: Vec<(usize, usize)> = vec![(4, 10), (13, 20), (24, 30), (33, 40)]; + // let mut change_pos = Vec::new(); + + // // for &range in selection { + // // let (start1, end1, start2, end2) = { + // // let cursor_pos = range.cursor(text); + // // // let range_raw = find_nth_close_tag(text, cursor_pos, skip).unwrap(); + + // // let first = range_raw.0; + // // let second = range_raw.1; + // // let first_1 = first.first().unwrap(); + // // let first_2 = second.last().unwrap(); + // // let second_1 = first.first().unwrap(); + // // let second_2 = second.last().unwrap(); + // // let range_first = Range::new(*first_1, *first_2); + // // let range_last = Range::new(*second_1, *second_2); + // // ( + // // range_first.from(), + // // range_first.to(), + // // range_last.from(), + // // range_last.to(), + // // ) + // // }; + // // change_pos.push((start1, end1)); + // // change_pos.push((start2, end2)); + // // } + Ok(vec![(14, 24)]) +} - for &range in selection { - let (start1, end1, start2, end2) = { - let pos = range.cursor(text); - let range_raw = find_nth_close_tag(text, tag, pos, skip).unwrap(); - - let first = range_raw.0; - let second = range_raw.1; - let first_1 = first.first().unwrap(); - let first_2 = second.last().unwrap(); - let second_1 = first.first().unwrap(); - let second_2 = second.last().unwrap(); - let range_first = Range::new(*first_1, *first_2); - let range_last = Range::new(*second_1, *second_2); - ( - range_first.from(), - range_first.to(), - range_last.from(), - range_last.to(), - ) - }; - change_pos.extend_from_slice(&[start1, end1, start2, end2]); +pub fn is_valid_tagname_char(ch: char) -> bool { + ch.is_alphanumeric() || ch == '_' || ch == '-' || ch == '.' +} + +pub fn find_next_tag(text: RopeSlice, mut pos: usize, n: usize) -> Option<(usize, usize)> { + if pos >= text.len_chars() || n == 0 { + return None; } - Ok(change_pos) + + let mut chars = text.chars_at(pos); + let mut possible_tag = String::new(); + + 'outer: loop { + // look forward, try to find something that looks like a closing tag e.g. + // extract the name so e.g. "html". + // set current_tag_name to this "html" string, then break. + let next_char = chars.next()?; + pos += 1; + if next_char == '<' { + let char_after_that = chars.next()?; + pos += 1; + if char_after_that == '/' { + let mut possible_tag_name = String::new(); + loop { + let current_char = chars.next()?; + pos += 1; + if is_valid_tagname_char(current_char) { + possible_tag_name.push(current_char); + } else if current_char == '>' && possible_tag_name.len() != 0 { + // victory! + possible_tag.push_str(&possible_tag_name[..]); + break 'outer; + } else { + break; + } + } + } + } + } + + return Some((pos - possible_tag.len() - 1, pos - 2)); } #[cfg(test)] @@ -388,18 +439,10 @@ mod test { } #[test] - fn test_get_surround_pos_tag_simple() { - #[rustfmt::skip] - let (doc, selection, expectations) = - rope_with_selections_and_expectations_tags( - "hello lol", - " ___ ^ ___ _______ ^ _______ " - ); + fn test_find_next_tag() { + let doc = Rope::from("hello"); - assert_eq!( - get_tag_surround_pos(None, doc.slice(..), &selection, 1).unwrap(), - expectations - ); + assert_eq!(find_next_tag(doc.slice(..), 7, 1).unwrap(), (14, 24)); } #[test] diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index b2e47bae2..7fcdfaeee 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -5716,7 +5716,7 @@ fn surround_replace(cx: &mut Context) { let selection = doc.selection(view.id); if false { - let change_pos = match surround::get_surround_pos_tag(text, selection, "lol", layer) { + let change_pos = match surround::get_surround_pos_tag(text, selection, layer) { Ok(c) => c, Err(err) => { cx.editor.set_error(err.to_string()); @@ -5726,7 +5726,14 @@ fn surround_replace(cx: &mut Context) { // TODO: add back the logic for other surround changes } else { // TODO: obtain these values from a function - let change_pos: Vec<(usize, usize)> = vec![(4, 10), (13, 20), (24, 30), (33, 40)]; + // let change_pos: Vec<(usize, usize)> = vec![(4, 10), (13, 20), (24, 30), (33, 40)]; + let change_pos = match surround::get_surround_pos_tag(text, selection, layer) { + Ok(c) => c, + Err(err) => { + cx.editor.set_error(err.to_string()); + return; + } + }; let selection = selection.clone(); let ranges: SmallVec<[Range; 1]> =