use rowan::{TextRange, TextSize};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Default, Serialize, Deserialize)]
pub struct Position {
pub index: u64,
pub line: u64,
pub column: u64,
}
impl Position {
#[must_use]
pub fn new(index: u64, line: u64, column: u64) -> Self {
Position {
index,
line,
column,
}
}
}
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default, Serialize, Deserialize)]
pub struct Range {
pub start: Position,
pub end: Position,
}
impl Range {
pub fn join(&self, other: &Range) -> Range {
let start = self.start.min(other.start);
let end = self.end.max(other.end);
Self { start, end }
}
}
#[derive(Debug, Clone, Copy)]
pub struct CharacterRange(u64, u64);
#[derive(Debug, Clone)]
pub struct Mapper {
offset_to_position: BTreeMap<TextSize, Position>,
position_to_offset: BTreeMap<Position, TextSize>,
lines: usize,
end: Position,
}
impl Mapper {
#[must_use]
pub fn new_utf16(source: &str, one_based: bool) -> Self {
Self::new_impl(source, true, if one_based { 1 } else { 0 })
}
#[must_use]
pub fn new_utf8(source: &str, one_based: bool) -> Self {
Self::new_impl(source, false, if one_based { 1 } else { 0 })
}
#[must_use]
pub fn offset(&self, position: Position) -> Option<TextSize> {
self.position_to_offset.get(&position).copied()
}
#[must_use]
pub fn text_range(&self, range: Range) -> Option<TextRange> {
self.offset(range.start)
.and_then(|start| self.offset(range.end).map(|end| TextRange::new(start, end)))
}
#[must_use]
pub fn position(&self, offset: TextSize) -> Option<Position> {
self.offset_to_position.get(&offset).copied()
}
#[must_use]
pub fn range(&self, range: TextRange) -> Option<Range> {
self.position(range.start())
.and_then(|start| self.position(range.end()).map(|end| Range { start, end }))
}
#[must_use]
pub fn mappings(&self) -> (&BTreeMap<TextSize, Position>, &BTreeMap<Position, TextSize>) {
(&self.offset_to_position, &self.position_to_offset)
}
#[must_use]
pub fn line_count(&self) -> usize {
self.lines
}
#[must_use]
pub fn all_range(&self) -> Range {
Range {
start: Position {
index: 0,
line: 0,
column: 0,
},
end: self.end,
}
}
fn new_impl(source: &str, utf16: bool, base: u64) -> Self {
let mut offset_to_position = BTreeMap::new();
let mut position_to_offset = BTreeMap::new();
let mut line: u64 = base;
let mut column: u64 = base;
let mut last_offset = 0;
let mut index: u64 = 0;
for (i, c) in source.chars().enumerate() {
index = i as u64;
let new_offset = last_offset + c.len_utf8();
let character_size = if utf16 { c.len_utf16() } else { 1 };
offset_to_position.extend((last_offset..new_offset).map(|b| {
(
TextSize::from(b as u32),
Position {
index,
line,
column,
},
)
}));
position_to_offset.extend((last_offset..new_offset).map(|b| {
(
Position {
index,
line,
column,
},
TextSize::from(b as u32),
)
}));
last_offset = new_offset;
column += character_size as u64;
if c == '\n' {
line += 1;
column = base;
}
}
offset_to_position.insert(
TextSize::from(last_offset as u32),
Position {
index,
line,
column,
},
);
position_to_offset.insert(
Position {
index,
line,
column,
},
TextSize::from(last_offset as u32),
);
Self {
offset_to_position,
position_to_offset,
lines: line as usize,
end: Position {
index,
line,
column,
},
}
}
}