From 3849ca4c2abdb1c6076d391eab4c3f43dda30234 Mon Sep 17 00:00:00 2001
From: Kyle Smith <askreet@gmail.com>
Date: Tue, 7 Mar 2023 21:10:55 -0500
Subject: [PATCH] Add test cases for existing pair matching logic. (#6027)

* Add test cases for existing pair matching logic.

* fix clippy
---
 helix-core/src/surround.rs | 145 +++++++++++++++++++++++++++++--------
 1 file changed, 116 insertions(+), 29 deletions(-)

diff --git a/helix-core/src/surround.rs b/helix-core/src/surround.rs
index 64d48c13..f430aee8 100644
--- a/helix-core/src/surround.rs
+++ b/helix-core/src/surround.rs
@@ -275,51 +275,138 @@ mod test {
 
     #[test]
     fn test_get_surround_pos() {
-        let doc = Rope::from("(some) (chars)\n(newline)");
-        let slice = doc.slice(..);
-        let selection = Selection::new(
-            SmallVec::from_slice(&[Range::point(2), Range::point(9), Range::point(20)]),
-            0,
-        );
+        #[rustfmt::skip]
+        let (doc, selection, expectations) =
+            rope_with_selections_and_expectations(
+                "(some) (chars)\n(newline)",
+                "_ ^  _ _ ^   _\n_    ^  _"
+            );
 
-        // cursor on s[o]me, c[h]ars, newl[i]ne
         assert_eq!(
-            get_surround_pos(slice, &selection, Some('('), 1)
-                .unwrap()
-                .as_slice(),
-            &[0, 5, 7, 13, 15, 23]
+            get_surround_pos(doc.slice(..), &selection, Some('('), 1).unwrap(),
+            expectations
         );
     }
 
     #[test]
-    fn test_get_surround_pos_bail() {
-        let doc = Rope::from("[some]\n(chars)xx\n(newline)");
-        let slice = doc.slice(..);
+    fn test_get_surround_pos_bail_different_surround_chars() {
+        #[rustfmt::skip]
+        let (doc, selection, _) =
+            rope_with_selections_and_expectations(
+                "[some]\n(chars)xx\n(newline)",
+                "  ^   \n  ^      \n         "
+            );
 
-        let selection =
-            Selection::new(SmallVec::from_slice(&[Range::point(2), Range::point(9)]), 0);
-        // cursor on s[o]me, c[h]ars
         assert_eq!(
-            get_surround_pos(slice, &selection, Some('('), 1),
-            Err(Error::PairNotFound) // different surround chars
+            get_surround_pos(doc.slice(..), &selection, Some('('), 1),
+            Err(Error::PairNotFound)
         );
+    }
+
+    #[test]
+    fn test_get_surround_pos_bail_overlapping_surround_chars() {
+        #[rustfmt::skip]
+        let (doc, selection, _) =
+            rope_with_selections_and_expectations(
+                "[some]\n(chars)xx\n(newline)",
+                "      \n       ^ \n      ^  "
+            );
 
-        let selection = Selection::new(
-            SmallVec::from_slice(&[Range::point(14), Range::point(24)]),
-            0,
-        );
-        // cursor on [x]x, newli[n]e
         assert_eq!(
-            get_surround_pos(slice, &selection, Some('('), 1),
+            get_surround_pos(doc.slice(..), &selection, Some('('), 1),
             Err(Error::PairNotFound) // overlapping surround chars
         );
+    }
+
+    #[test]
+    fn test_get_surround_pos_bail_cursor_overlap() {
+        #[rustfmt::skip]
+        let (doc, selection, _) =
+            rope_with_selections_and_expectations(
+                "[some]\n(chars)xx\n(newline)",
+                "  ^^  \n         \n         "
+            );
 
-        let selection =
-            Selection::new(SmallVec::from_slice(&[Range::point(2), Range::point(3)]), 0);
-        // cursor on s[o][m]e
         assert_eq!(
-            get_surround_pos(slice, &selection, Some('['), 1),
+            get_surround_pos(doc.slice(..), &selection, Some('['), 1),
             Err(Error::CursorOverlap)
         );
     }
+
+    #[test]
+    fn test_find_nth_pairs_pos_quote_success() {
+        #[rustfmt::skip]
+        let (doc, selection, expectations) =
+            rope_with_selections_and_expectations(
+                "some 'quoted text' on this 'line'\n'and this one'",
+                "     _        ^  _               \n              "
+            );
+
+        assert_eq!(2, expectations.len());
+        assert_eq!(
+            find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 1)
+                .expect("find should succeed"),
+            (expectations[0], expectations[1])
+        )
+    }
+
+    #[test]
+    fn test_find_nth_pairs_pos_nested_quote_success() {
+        #[rustfmt::skip]
+        let (doc, selection, expectations) =
+            rope_with_selections_and_expectations(
+                "some 'nested 'quoted' text' on this 'line'\n'and this one'",
+                "     _           ^        _               \n              "
+            );
+
+        assert_eq!(2, expectations.len());
+        assert_eq!(
+            find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 2)
+                .expect("find should succeed"),
+            (expectations[0], expectations[1])
+        )
+    }
+
+    #[test]
+    fn test_find_nth_pairs_pos_inside_quote_ambiguous() {
+        #[rustfmt::skip]
+        let (doc, selection, _) =
+            rope_with_selections_and_expectations(
+                "some 'nested 'quoted' text' on this 'line'\n'and this one'",
+                "                    ^                     \n              "
+            );
+
+        assert_eq!(
+            find_nth_pairs_pos(doc.slice(..), '\'', selection.primary(), 1),
+            Err(Error::CursorOnAmbiguousPair)
+        )
+    }
+
+    // Create a Rope and a matching Selection using a specification language.
+    // ^ is a single-point selection.
+    // _ is an expected index. These are returned as a Vec<usize> for use in assertions.
+    fn rope_with_selections_and_expectations(
+        text: &str,
+        spec: &str,
+    ) -> (Rope, Selection, Vec<usize>) {
+        if text.len() != spec.len() {
+            panic!("specification must match text length -- are newlines aligned?");
+        }
+
+        let rope = Rope::from(text);
+
+        let selections: SmallVec<[Range; 1]> = spec
+            .match_indices('^')
+            .into_iter()
+            .map(|(i, _)| Range::point(i))
+            .collect();
+
+        let expectations: Vec<usize> = spec
+            .match_indices('_')
+            .into_iter()
+            .map(|(i, _)| i)
+            .collect();
+
+        (rope, Selection::new(selections, 0), expectations)
+    }
 }