Skip to main content

pactole_syntax/
span.rs

1//! Byte-offset and row/column spans for syntax nodes.
2
3use std::fmt;
4
5/// A single position in the source text, expressed both as a byte offset and
6/// as a zero-indexed (row, column) pair.
7///
8/// The `column` is a byte offset within its `row`, matching tree-sitter's
9/// [`tree_sitter::Point`] semantics.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct Point {
12    /// Zero-indexed byte offset of the position within the whole source text.
13    pub byte: usize,
14    /// Zero-indexed row (line number).
15    pub row: usize,
16    /// Zero-indexed column (byte offset within `row`).
17    pub column: usize,
18}
19
20impl Point {
21    /// Builds a [`Point`] from a tree-sitter [`tree_sitter::Point`] and a byte offset.
22    pub fn new(byte: usize, ts_point: tree_sitter::Point) -> Self {
23        Self {
24            byte,
25            row: ts_point.row,
26            column: ts_point.column,
27        }
28    }
29}
30
31impl fmt::Display for Point {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        write!(f, "{}:{}", self.row + 1, self.column + 1)
34    }
35}
36
37/// A contiguous range in the source text, delimited by a start and end [`Point`].
38///
39/// The range is half-open: `start` is inclusive and `end` is exclusive, mirroring
40/// tree-sitter's node range semantics.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42pub struct Span {
43    /// Inclusive start position of the span.
44    pub start: Point,
45    /// Exclusive end position of the span.
46    pub end: Point,
47}
48
49impl Span {
50    /// Builds a [`Span`] from a start and end [`Point`].
51    pub fn new(start: Point, end: Point) -> Self {
52        Self { start, end }
53    }
54
55    /// Builds a [`Span`] covering the full range of a tree-sitter [`tree_sitter::Node`].
56    pub fn from_node(node: &tree_sitter::Node) -> Self {
57        Self {
58            start: Point::new(node.start_byte(), node.start_position()),
59            end: Point::new(node.end_byte(), node.end_position()),
60        }
61    }
62
63    /// The byte range covered by this span, usable to slice the original source text.
64    pub fn byte_range(&self) -> std::ops::Range<usize> {
65        self.start.byte..self.end.byte
66    }
67}
68
69impl fmt::Display for Span {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        write!(f, "{}-{}", self.start, self.end)
72    }
73}