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