assemble_excerpts.rs

  1use language::{BufferSnapshot, OffsetRangeExt as _, Point};
  2use std::ops::Range;
  3use zeta_prompt::RelatedExcerpt;
  4
  5#[cfg(not(test))]
  6const MAX_OUTLINE_ITEM_BODY_SIZE: usize = 512;
  7#[cfg(test)]
  8const MAX_OUTLINE_ITEM_BODY_SIZE: usize = 24;
  9
 10pub fn assemble_excerpts(
 11    buffer: &BufferSnapshot,
 12    mut input_ranges: Vec<Range<Point>>,
 13) -> Vec<RelatedExcerpt> {
 14    merge_ranges(&mut input_ranges);
 15
 16    let mut outline_ranges = Vec::new();
 17    let outline_items = buffer.outline_items_as_points_containing(0..buffer.len(), false, None);
 18    let mut outline_ix = 0;
 19    for input_range in &mut input_ranges {
 20        *input_range = clip_range_to_lines(input_range, false, buffer);
 21
 22        while let Some(outline_item) = outline_items.get(outline_ix) {
 23            let item_range = clip_range_to_lines(&outline_item.range, false, buffer);
 24
 25            if item_range.start > input_range.start {
 26                break;
 27            }
 28
 29            if item_range.end > input_range.start {
 30                let body_range = outline_item
 31                    .body_range(buffer)
 32                    .map(|body| clip_range_to_lines(&body, true, buffer))
 33                    .filter(|body_range| {
 34                        body_range.to_offset(buffer).len() > MAX_OUTLINE_ITEM_BODY_SIZE
 35                    });
 36
 37                add_outline_item(
 38                    item_range.clone(),
 39                    body_range.clone(),
 40                    buffer,
 41                    &mut outline_ranges,
 42                );
 43
 44                if let Some(body_range) = body_range
 45                    && input_range.start < body_range.start
 46                {
 47                    let mut child_outline_ix = outline_ix + 1;
 48                    while let Some(next_outline_item) = outline_items.get(child_outline_ix) {
 49                        if next_outline_item.range.end > body_range.end {
 50                            break;
 51                        }
 52                        if next_outline_item.depth == outline_item.depth + 1 {
 53                            let next_item_range =
 54                                clip_range_to_lines(&next_outline_item.range, false, buffer);
 55
 56                            add_outline_item(
 57                                next_item_range,
 58                                next_outline_item
 59                                    .body_range(buffer)
 60                                    .map(|body| clip_range_to_lines(&body, true, buffer)),
 61                                buffer,
 62                                &mut outline_ranges,
 63                            );
 64                        }
 65                        child_outline_ix += 1;
 66                    }
 67                }
 68            }
 69
 70            outline_ix += 1;
 71        }
 72    }
 73
 74    input_ranges.extend_from_slice(&outline_ranges);
 75    merge_ranges(&mut input_ranges);
 76
 77    input_ranges
 78        .into_iter()
 79        .map(|range| RelatedExcerpt {
 80            row_range: range.start.row..range.end.row,
 81            text: buffer.text_for_range(range).collect(),
 82        })
 83        .collect()
 84}
 85
 86fn clip_range_to_lines(
 87    range: &Range<Point>,
 88    inward: bool,
 89    buffer: &BufferSnapshot,
 90) -> Range<Point> {
 91    let mut range = range.clone();
 92    if inward {
 93        if range.start.column > 0 {
 94            range.start.column = buffer.line_len(range.start.row);
 95        }
 96        range.end.column = 0;
 97    } else {
 98        range.start.column = 0;
 99        if range.end.column > 0 {
100            range.end.column = buffer.line_len(range.end.row);
101        }
102    }
103    range
104}
105
106fn add_outline_item(
107    mut item_range: Range<Point>,
108    body_range: Option<Range<Point>>,
109    buffer: &BufferSnapshot,
110    outline_ranges: &mut Vec<Range<Point>>,
111) {
112    if let Some(mut body_range) = body_range {
113        if body_range.start.column > 0 {
114            body_range.start.column = buffer.line_len(body_range.start.row);
115        }
116        body_range.end.column = 0;
117
118        let head_range = item_range.start..body_range.start;
119        if head_range.start < head_range.end {
120            outline_ranges.push(head_range);
121        }
122
123        let tail_range = body_range.end..item_range.end;
124        if tail_range.start < tail_range.end {
125            outline_ranges.push(tail_range);
126        }
127    } else {
128        item_range.start.column = 0;
129        item_range.end.column = buffer.line_len(item_range.end.row);
130        outline_ranges.push(item_range);
131    }
132}
133
134pub fn merge_ranges(ranges: &mut Vec<Range<Point>>) {
135    ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start).then(b.end.cmp(&a.end)));
136
137    let mut index = 1;
138    while index < ranges.len() {
139        let mut prev_range_end = ranges[index - 1].end;
140        if prev_range_end.column > 0 {
141            prev_range_end += Point::new(1, 0);
142        }
143
144        if (prev_range_end + Point::new(1, 0))
145            .cmp(&ranges[index].start)
146            .is_ge()
147        {
148            let removed = ranges.remove(index);
149            if removed.end.cmp(&ranges[index - 1].end).is_gt() {
150                ranges[index - 1].end = removed.end;
151            }
152        } else {
153            index += 1;
154        }
155    }
156}