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}