diff --git a/helix-core/src/surround.rs b/helix-core/src/surround.rs index 917072b3a8c36..45989218a84fa 100644 --- a/helix-core/src/surround.rs +++ b/helix-core/src/surround.rs @@ -256,150 +256,6 @@ mod test { use ropey::Rope; use smallvec::SmallVec; - #[allow(clippy::type_complexity)] - fn check_find_nth_pair_pos( - text: &str, - cases: Vec<(usize, char, usize, Result<(usize, usize)>)>, - ) { - let doc = Rope::from(text); - let slice = doc.slice(..); - - for (cursor_pos, ch, n, expected_range) in cases { - let range = find_nth_pairs_pos(slice, ch, (cursor_pos, cursor_pos + 1).into(), n); - assert_eq!( - range, expected_range, - "Cursor position {:?}), character {:?}, skip {:?}", - cursor_pos, ch, n - ); - } - } - - #[allow(clippy::type_complexity)] - fn check_closest_pair_pos( - text: &str, - cases: Vec<(usize, usize, usize, Result<(usize, usize)>)>, - ) { - let doc = Rope::from(text); - let slice = doc.slice(..); - - for (beginning_range_start, beginning_range_end, n, expected_range) in cases { - let computed_range = find_nth_closest_pairs_pos( - slice, - (beginning_range_start, beginning_range_end).into(), - n, - ); - assert_eq!( - computed_range, expected_range, - "Beginning range ({:?}, {:?}), skip {:?}", - beginning_range_start, beginning_range_end, n - ); - } - } - - #[test] - fn test_find_nth_pairs_pos() { - check_find_nth_pair_pos( - "some (text) here", - vec![ - // cursor on [t]ext - (6, '(', 1, Ok((5, 10))), - (6, ')', 1, Ok((5, 10))), - // cursor on so[m]e - (2, '(', 1, Err(Error::PairNotFound)), - // cursor on bracket itself - (5, '(', 1, Ok((5, 10))), - (10, '(', 1, Ok((5, 10))), - ], - ); - } - - #[test] - fn test_find_nth_pairs_pos_skip() { - check_find_nth_pair_pos( - "(so (many (good) text) here)", - vec![ - // cursor on go[o]d - (13, '(', 1, Ok((10, 15))), - (13, '(', 2, Ok((4, 21))), - (13, '(', 3, Ok((0, 27))), - ], - ); - } - - #[test] - fn test_find_nth_pairs_pos_same() { - check_find_nth_pair_pos( - "'so 'many 'good' text' here'", - vec![ - // cursor on go[o]d - (13, '\'', 1, Ok((10, 15))), - (13, '\'', 2, Ok((4, 21))), - (13, '\'', 3, Ok((0, 27))), - // cursor on the quotes - (10, '\'', 1, Err(Error::CursorOnAmbiguousPair)), - ], - ) - } - - #[test] - fn test_find_nth_pairs_pos_step() { - check_find_nth_pair_pos( - "((so)((many) good (text))(here))", - vec![ - // cursor on go[o]d - (15, '(', 1, Ok((5, 24))), - (15, '(', 2, Ok((0, 31))), - ], - ) - } - - #[test] - fn test_find_nth_pairs_pos_mixed() { - check_find_nth_pair_pos( - "(so [many {good} text] here)", - vec![ - // cursor on go[o]d - (13, '{', 1, Ok((10, 15))), - (13, '[', 1, Ok((4, 21))), - (13, '(', 1, Ok((0, 27))), - ], - ) - } - - #[test] - fn test_surround_pairs_range() { - check_closest_pair_pos( - "(so (many (good) text) here)", - vec![ - // range is (go[od) ] - (13, 17, 1, Ok((4, 21))), - // range is [ (go]od) - (9, 13, 1, Ok((4, 21))), - // range is [ (many (go]od)) - (3, 13, 1, Ok((0, 27))), - // range is (go[od) text) ] - (13, 23, 1, Ok((0, 27))), - ], - ); - } - - #[test] - fn test_surround_mixed_pairs_range() { - check_closest_pair_pos( - "(so [many {good} text] here)", - vec![ - // range is {go|od} | - (13, 17, 1, Ok((4, 21))), - // range is | {go|od} - (9, 13, 1, Ok((4, 21))), - // range is | [many {go|od} - (3, 13, 1, Ok((0, 27))), - // range is {go|od} text] | - (13, 23, 1, Ok((0, 27))), - ], - ); - } - #[test] fn test_get_surround_pos() { let doc = Rope::from("(some) (chars)\n(newline)"); diff --git a/helix-term/tests/test/movement.rs b/helix-term/tests/test/movement.rs index 088685dfc0051..957633161b89a 100644 --- a/helix-term/tests/test/movement.rs +++ b/helix-term/tests/test/movement.rs @@ -64,6 +64,57 @@ async fn insert_to_normal_mode_cursor_position() -> anyhow::Result<()> { Ok(()) } +#[tokio::test] +async fn surround_by_character() -> anyhow::Result<()> { + // Only pairs matching the passed character count + test(("(so [many {go#[o|]#d} text] here)", "mi{", "(so [many {#[good|]#} text] here)")).await?; + test(("(so [many {go#[o|]#d} text] here)", "mi[", "(so [#[many {good} text|]#] here)")).await?; + test(("(so [many {go#[o|]#d} text] here)", "mi(", "(#[so [many {good} text] here|]#)")).await?; + + // Works with characters that aren't pairs too + test(("'so 'many 'go#[o|]#d' text' here'", "mi'", "'so 'many '#[good|]#' text' here'")).await?; + test(("'so 'many 'go#[o|]#d' text' here'", "2mi'", "'so '#[many 'good' text|]#' here'")).await?; + test(("'so \"many 'go#[o|]#d' text\" here'", "mi\"", "'so \"#[many 'good|]#' text|]#\" here'")).await?; + + Ok(()) +} + +#[tokio::test] +async fn surround_pair() -> anyhow::Result<()> { + // Inside a valid pair selects pair + test(("some (#[t|]#ext) here", "mim", "some (#[text|]#) here")).await?; + + // On pair character selects pair + // Opening pair character is a known failure case that needs addressing + // test(("some #[(|]#text) here", "mim", "some (#[text|]#) here")).await?; + test(("some (text#[)|]# here", "mim", "some (#[text|]#) here")).await?; + + // No valid pair does nothing + test(("so#[m|]#e (text) here", "mim", "so#[m|]#e (text) here")).await?; + + // Count skips to outer pairs + test(("(so (many (go#[o|]#d) text) here)", "1mim", "(so (many (#[good|]#) text) here)")).await?; + test(("(so (many (go#[o|]#d) text) here)", "2mim", "(so (#[many (good) text|]#) here)")).await?; + test(("(so (many (go#[o|]#d) text) here)", "3mim", "(#[so (many (good) text) here|]#)")).await?; + + // Matching paris outside selection don't match + test(("((so)((many) go#[o|]#d (text))(here))", "mim", "((so)(#[(many) good (text)|]#)(here))")).await?; + test(("((so)((many) go#[o|]#d (text))(here))", "2mim", "(#[(so)((many) good (text))(here)|]#)")).await?; + + // Works with mixed braces + test(("(so [many {go#[o|]#d} text] here)", "mim", "(so [many {#[good|]#} text] here)")).await?; + test(("(so [many {go#[o|]#d} text] here)", "2mim", "(so [#[many {good} text|]#] here)")).await?; + test(("(so [many {go#[o|]#d} text] here)", "3mim", "(#[so [many {good} text] here|]#)")).await?; + + // Only pairs outside of full selection range are considered + test(("(so (many (go#[od) |]#text) here)", "mim", "(so (#[many (good) text|]#) here)")).await?; + test(("(so (many#[ (go|]#od) text) here)", "mim", "(so (#[many (good) text|]#) here)")).await?; + test(("(so#[ (many (go|]#od) text) here)", "mim", "(#[so (many (good) text) here|]#)")).await?; + test(("(so (many (go#[od) text) |]#here)", "mim", "(#[so (many (good) text) here|]#)")).await?; + + Ok(()) +} + /// Ensure the very initial cursor in an opened file is the width of /// the first grapheme #[tokio::test]