row_chunk.rs

  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}