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 clock::Global;
7use text::{Anchor, OffsetRangeExt as _, Point};
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 pub snapshot: text::BufferSnapshot,
22 pub chunks: Arc<[RowChunk]>,
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("version", self.snapshot.version())
29 .field("chunks", &self.chunks)
30 .finish()
31 }
32}
33
34impl RowChunks {
35 pub fn new(snapshot: text::BufferSnapshot, max_rows_per_chunk: u32) -> Self {
36 let buffer_point_range = (0..snapshot.len()).to_point(&snapshot);
37 let last_row = buffer_point_range.end.row;
38 let chunks = (buffer_point_range.start.row..=last_row)
39 .step_by(max_rows_per_chunk as usize)
40 .enumerate()
41 .map(|(id, chunk_start)| RowChunk {
42 id,
43 start: chunk_start,
44 end_exclusive: (chunk_start + max_rows_per_chunk).min(last_row),
45 })
46 .collect::<Vec<_>>();
47 Self {
48 snapshot,
49 chunks: Arc::from(chunks),
50 }
51 }
52
53 pub fn version(&self) -> &Global {
54 self.snapshot.version()
55 }
56
57 pub fn len(&self) -> usize {
58 self.chunks.len()
59 }
60
61 pub fn applicable_chunks(
62 &self,
63 ranges: &[Range<text::Anchor>],
64 ) -> impl Iterator<Item = RowChunk> {
65 let row_ranges = ranges
66 .iter()
67 .map(|range| range.to_point(&self.snapshot))
68 .map(|point_range| point_range.start.row..=point_range.end.row)
69 .collect::<Vec<_>>();
70 self.chunks
71 .iter()
72 .filter(move |chunk| -> bool {
73 // Be lenient and yield multiple chunks if they "touch" the exclusive part of the range.
74 // This will result in LSP hints [re-]queried for more ranges, but also more hints already visible when scrolling around.
75 let chunk_range = chunk.row_range();
76 row_ranges.iter().any(|row_range| {
77 chunk_range.contains(&row_range.start())
78 || chunk_range.contains(&row_range.end())
79 })
80 })
81 .copied()
82 }
83
84 pub fn chunk_range(&self, chunk: RowChunk) -> Option<Range<Anchor>> {
85 if !self.chunks.contains(&chunk) {
86 return None;
87 }
88
89 let start = Point::new(chunk.start, 0);
90 let end = if self.chunks.last() == Some(&chunk) {
91 Point::new(
92 chunk.end_exclusive,
93 self.snapshot.line_len(chunk.end_exclusive),
94 )
95 } else {
96 Point::new(chunk.end_exclusive, 0)
97 };
98 Some(self.snapshot.anchor_before(start)..self.snapshot.anchor_after(end))
99 }
100
101 pub fn previous_chunk(&self, chunk: RowChunk) -> Option<RowChunk> {
102 if chunk.id == 0 {
103 None
104 } else {
105 self.chunks.get(chunk.id - 1).copied()
106 }
107 }
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111pub struct RowChunk {
112 pub id: usize,
113 pub start: BufferRow,
114 pub end_exclusive: BufferRow,
115}
116
117impl RowChunk {
118 pub fn row_range(&self) -> Range<BufferRow> {
119 self.start..self.end_exclusive
120 }
121}