use core::fmt;
use core::ops;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct TextSize(u32);
impl TextSize {
pub const fn new(raw: u32) -> Self {
Self(raw)
}
pub const fn raw(self) -> u32 {
self.0
}
}
impl From<u32> for TextSize {
fn from(raw: u32) -> Self {
Self(raw)
}
}
impl From<TextSize> for u32 {
fn from(size: TextSize) -> Self {
size.0
}
}
impl From<TextSize> for usize {
fn from(size: TextSize) -> Self {
size.0 as usize
}
}
impl From<usize> for TextSize {
fn from(raw: usize) -> Self {
Self(raw as u32)
}
}
impl ops::Add for TextSize {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self(self.0 + rhs.0)
}
}
impl ops::Sub for TextSize {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self(self.0 - rhs.0)
}
}
impl fmt::Display for TextSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct TextRange {
start: TextSize,
end: TextSize,
}
impl TextRange {
pub const fn new(start: TextSize, end: TextSize) -> Self {
Self { start, end }
}
pub const fn start(self) -> TextSize {
self.start
}
pub const fn end(self) -> TextSize {
self.end
}
pub const fn len(self) -> TextSize {
TextSize::new(self.end.0 - self.start.0)
}
pub const fn is_empty(self) -> bool {
self.start.0 == self.end.0
}
pub const fn contains(self, offset: TextSize) -> bool {
self.start.0 <= offset.0 && offset.0 < self.end.0
}
pub const fn from_offset_len(offset: usize, len: usize) -> Self {
Self {
start: TextSize::new(offset as u32),
end: TextSize::new((offset + len) as u32),
}
}
pub fn source_text<'a>(&self, source: &'a str) -> &'a str {
let start = self.start.0 as usize;
let end = self.end.0 as usize;
if start <= end && end <= source.len() {
&source[start..end]
} else {
""
}
}
pub fn extend(&mut self, other: TextRange) {
self.end = other.end;
}
}
impl fmt::Display for TextRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}..{}", self.start, self.end)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LineColumn {
pub lineno: u32,
pub col: u32,
}
impl fmt::Display for LineColumn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.lineno, self.col)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LineIndex {
line_starts: Vec<u32>,
}
impl LineIndex {
pub fn new(source: &str) -> Self {
let mut line_starts = vec![0u32];
for (i, b) in source.bytes().enumerate() {
if b == b'\n' {
line_starts.push((i + 1) as u32);
}
}
Self { line_starts }
}
pub fn line_col(&self, offset: TextSize) -> LineColumn {
let offset = offset.raw();
let line = self.line_starts.partition_point(|&s| s <= offset) - 1;
let col = offset - self.line_starts[line];
LineColumn {
lineno: line as u32 + 1,
col,
}
}
}