Add docs to marked range functions

Max Brunsfeld created

Change summary

crates/util/src/test/marked_text.rs | 77 ++++++++++++++++++++++--------
1 file changed, 56 insertions(+), 21 deletions(-)

Detailed changes

crates/util/src/test/marked_text.rs 🔗

@@ -1,5 +1,7 @@
 use std::{cmp::Ordering, collections::HashMap, ops::Range};
 
+/// Construct a string and a list of offsets within that string using a single
+/// string containing embedded position markers.
 pub fn marked_text_offsets_by(
     marked_text: &str,
     markers: Vec<char>,
@@ -19,6 +21,12 @@ pub fn marked_text_offsets_by(
     (unmarked_text, extracted_markers)
 }
 
+/// Construct a string and a list of ranges within that string using a single
+/// string containing embedded range markers, using arbitrary characters as
+/// range markers. By using multiple different range markers, you can construct
+/// ranges that overlap each other.
+///
+/// The returned ranges will be grouped by their range marking characters.
 pub fn marked_text_ranges_by(
     marked_text: &str,
     markers: Vec<TextRangeMarker>,
@@ -72,67 +80,94 @@ pub fn marked_text_ranges_by(
     (unmarked_text, range_lookup)
 }
 
-pub fn marked_text_ranges(input_text: &str, indicate_cursors: bool) -> (String, Vec<Range<usize>>) {
-    let mut output_text = String::with_capacity(input_text.len());
+/// Construct a string and a list of ranges within that string using a single
+/// string containing embedded range markers. The characters used to mark the
+/// ranges are as follows:
+///
+/// 1. To mark a range of text, surround it with the `«` and `»` angle brackets,
+///    which can be typed on a US keyboard with the `alt-|` and `alt-shift-|` keys.
+///
+///    ```
+///    foo «selected text» bar
+///    ```
+///
+/// 2. To mark a single position in the text, use the `ˇ` caron,
+///    which can be typed on a US keyboard with the `alt-shift-t` key.
+///
+///    ```
+///    the cursors are hereˇ and hereˇ.
+///    ```
+///
+/// 3. To mark a range whose direction is meaningful (like a selection),
+///    put a caron character beside one of its bounds, on the inside:
+///
+///    ```
+///    one «ˇreversed» selection and one «forwardˇ» selection
+///    ```
+pub fn marked_text_ranges(
+    marked_text: &str,
+    ranges_are_directed: bool,
+) -> (String, Vec<Range<usize>>) {
+    let mut unmarked_text = String::with_capacity(marked_text.len());
     let mut ranges = Vec::new();
-    let mut prev_input_ix = 0;
+    let mut prev_marked_ix = 0;
     let mut current_range_start = None;
     let mut current_range_cursor = None;
 
-    for (input_ix, marker) in input_text.match_indices(&['«', '»', 'ˇ']) {
-        output_text.push_str(&input_text[prev_input_ix..input_ix]);
-        let output_len = output_text.len();
+    for (marked_ix, marker) in marked_text.match_indices(&['«', '»', 'ˇ']) {
+        unmarked_text.push_str(&marked_text[prev_marked_ix..marked_ix]);
+        let unmarked_len = unmarked_text.len();
         let len = marker.len();
-        prev_input_ix = input_ix + len;
+        prev_marked_ix = marked_ix + len;
 
         match marker {
             "ˇ" => {
                 if current_range_start.is_some() {
                     if current_range_cursor.is_some() {
-                        panic!("duplicate point marker 'ˇ' at index {input_ix}");
+                        panic!("duplicate point marker 'ˇ' at index {marked_ix}");
                     } else {
-                        current_range_cursor = Some(output_len);
+                        current_range_cursor = Some(unmarked_len);
                     }
                 } else {
-                    ranges.push(output_len..output_len);
+                    ranges.push(unmarked_len..unmarked_len);
                 }
             }
             "«" => {
                 if current_range_start.is_some() {
-                    panic!("unexpected range start marker '«' at index {input_ix}");
+                    panic!("unexpected range start marker '«' at index {marked_ix}");
                 }
-                current_range_start = Some(output_len);
+                current_range_start = Some(unmarked_len);
             }
             "»" => {
                 let current_range_start = if let Some(start) = current_range_start.take() {
                     start
                 } else {
-                    panic!("unexpected range end marker '»' at index {input_ix}");
+                    panic!("unexpected range end marker '»' at index {marked_ix}");
                 };
 
                 let mut reversed = false;
                 if let Some(current_range_cursor) = current_range_cursor.take() {
                     if current_range_cursor == current_range_start {
                         reversed = true;
-                    } else if current_range_cursor != output_len {
+                    } else if current_range_cursor != unmarked_len {
                         panic!("unexpected 'ˇ' marker in the middle of a range");
                     }
-                } else if indicate_cursors {
+                } else if ranges_are_directed {
                     panic!("missing 'ˇ' marker to indicate range direction");
                 }
 
                 ranges.push(if reversed {
-                    output_len..current_range_start
+                    unmarked_len..current_range_start
                 } else {
-                    current_range_start..output_len
+                    current_range_start..unmarked_len
                 });
             }
             _ => unreachable!(),
         }
     }
 
-    output_text.push_str(&input_text[prev_input_ix..]);
-    (output_text, ranges)
+    unmarked_text.push_str(&marked_text[prev_marked_ix..]);
+    (unmarked_text, ranges)
 }
 
 pub fn marked_text_offsets(marked_text: &str) -> (String, Vec<usize>) {
@@ -150,11 +185,11 @@ pub fn marked_text_offsets(marked_text: &str) -> (String, Vec<usize>) {
 }
 
 pub fn generate_marked_text(
-    output_text: &str,
+    unmarked_text: &str,
     ranges: &[Range<usize>],
     indicate_cursors: bool,
 ) -> String {
-    let mut marked_text = output_text.to_string();
+    let mut marked_text = unmarked_text.to_string();
     for range in ranges.iter().rev() {
         if indicate_cursors {
             match range.start.cmp(&range.end) {