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 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.
 71            .map(|point_range| point_range.start.row..point_range.end.row + 1)
 72            .collect::<Vec<_>>();
 73        self.chunks
 74            .iter()
 75            .filter(move |chunk| -> bool {
 76                let chunk_range = chunk.row_range();
 77                row_ranges
 78                    .iter()
 79                    .any(|row_range| chunk_range.overlaps(&row_range))
 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}