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