From e27b04735c630140b45ac9fab1b3087ae831f34a Mon Sep 17 00:00:00 2001
From: Mike Trinkala <trink@acm.org>
Date: Thu, 7 Mar 2024 11:37:01 -0800
Subject: [PATCH] Fix panic in select_textobject_around (#9832)

Test Document
-------------
```
a)b
```

Steps to Reproduce
------------------
1. %   # select_all
1. ms( # surround_add
1. mam # select_textobject_around

Debug and Release
-----------------
`thread 'main' panicked at 'Attempt to index past end of RopeSlice:
char index 7, RopeSlice char length 6', ropey-1.6.1/src/slice.rs:796:13`

Description
-----------
An index was selected beyond the end of the slice with chars_at. The fix
adds a guard check to `find_nth_open_pair`, like in the other find_nth*
functions.
---
 helix-core/src/surround.rs | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/helix-core/src/surround.rs b/helix-core/src/surround.rs
index 513f8749..ed976488 100644
--- a/helix-core/src/surround.rs
+++ b/helix-core/src/surround.rs
@@ -167,6 +167,10 @@ fn find_nth_open_pair(
     mut pos: usize,
     n: usize,
 ) -> Option<usize> {
+    if pos >= text.len_chars() {
+        return None;
+    }
+
     let mut chars = text.chars_at(pos + 1);
 
     // Adjusts pos for the first iteration, and handles the case of the
@@ -383,6 +387,21 @@ mod test {
         )
     }
 
+    #[test]
+    fn test_find_nth_closest_pairs_pos_index_range_panic() {
+        #[rustfmt::skip]
+        let (doc, selection, _) =
+            rope_with_selections_and_expectations(
+                "(a)c)",
+                "^^^^^"
+            );
+
+        assert_eq!(
+            find_nth_closest_pairs_pos(doc.slice(..), selection.primary(), 1),
+            Err(Error::PairNotFound)
+        )
+    }
+
     // 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.