1//! A row chunk is an exclusive range of rows, [`BufferRow`] within a buffer of a certain version, [`Global`].
2//! All but the last chunk are of a constant, given size.
3
4use std::{ops::Range, sync::Arc};
5
6use text::{Anchor, OffsetRangeExt as _, Point};
7use util::RangeExt;
8
9use crate::BufferRow;
10
11/// An range of rows, exclusive as [`lsp::Range`] and
12/// <https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#range>
13/// denote.
14///
15/// Represents an area in a text editor, adjacent to other ones.
16/// Together, chunks form entire document at a particular version [`Global`].
17/// Each chunk is queried for inlays as `(start_row, 0)..(end_exclusive, 0)` via
18/// <https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#inlayHintParams>
19#[derive(Clone)]
20pub struct RowChunks {
21 chunks: Arc<[RowChunk]>,
22 version: clock::Global,
23}
24
25impl std::fmt::Debug for RowChunks {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 f.debug_struct("RowChunks")
28 .field("chunks", &self.chunks)
29 .finish()
30 }
31}
32
33impl RowChunks {
34 pub fn new(snapshot: text::BufferSnapshot, max_rows_per_chunk: u32) -> Self {
35 let buffer_point_range = (0..snapshot.len()).to_point(&snapshot);
36 let last_row = buffer_point_range.end.row;
37 let chunks = (buffer_point_range.start.row..=last_row)
38 .step_by(max_rows_per_chunk as usize)
39 .collect::<Vec<_>>();
40 let last_chunk_id = chunks.len() - 1;
41 let chunks = chunks
42 .into_iter()
43 .enumerate()
44 .map(|(id, chunk_start)| {
45 let start = Point::new(chunk_start, 0);
46 let end_exclusive = (chunk_start + max_rows_per_chunk).min(last_row);
47 let end = if id == last_chunk_id {
48 Point::new(end_exclusive, snapshot.line_len(end_exclusive))
49 } else {
50 Point::new(end_exclusive, 0)
51 };
52 RowChunk {
53 id,
54 start: chunk_start,
55 end_exclusive,
56 start_anchor: snapshot.anchor_before(start),
57 end_anchor: snapshot.anchor_after(end),
58 }
59 })
60 .collect::<Vec<_>>();
61 Self {
62 chunks: Arc::from(chunks),
63 version: snapshot.version().clone(),
64 }
65 }
66
67 pub fn version(&self) -> &clock::Global {
68 &self.version
69 }
70
71 pub fn len(&self) -> usize {
72 self.chunks.len()
73 }
74
75 pub fn applicable_chunks(&self, ranges: &[Range<Point>]) -> impl Iterator<Item = RowChunk> {
76 let row_ranges = ranges
77 .iter()
78 // Be lenient and yield multiple chunks if they "touch" the exclusive part of the range.
79 // This will result in LSP hints [re-]queried for more ranges, but also more hints already visible when scrolling around.
80 .map(|point_range| point_range.start.row..point_range.end.row + 1)
81 .collect::<Vec<_>>();
82 self.chunks
83 .iter()
84 .filter(move |chunk| -> bool {
85 let chunk_range = chunk.row_range().to_inclusive();
86 row_ranges
87 .iter()
88 .any(|row_range| chunk_range.overlaps(&row_range))
89 })
90 .copied()
91 }
92
93 pub fn previous_chunk(&self, chunk: RowChunk) -> Option<RowChunk> {
94 if chunk.id == 0 {
95 None
96 } else {
97 self.chunks.get(chunk.id - 1).copied()
98 }
99 }
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
103pub struct RowChunk {
104 pub id: usize,
105 pub start: BufferRow,
106 pub end_exclusive: BufferRow,
107 pub start_anchor: Anchor,
108 pub end_anchor: Anchor,
109}
110
111impl RowChunk {
112 pub fn row_range(&self) -> Range<BufferRow> {
113 self.start..self.end_exclusive
114 }
115
116 pub fn anchor_range(&self) -> Range<Anchor> {
117 self.start_anchor..self.end_anchor
118 }
119}