use tower_lsp::lsp_types::{Position, Range, TextDocumentContentChangeEvent, Url};
#[derive(Clone, Debug)]
pub struct Document {
pub uri: Url,
pub version: i32,
text: String,
}
impl Document {
pub fn new(uri: Url, version: i32, text: String) -> Self {
Self { uri, version, text }
}
pub fn text(&self) -> &str {
&self.text
}
pub fn apply_changes(&mut self, changes: Vec<TextDocumentContentChangeEvent>) {
for change in changes {
if let Some(range) = change.range {
let start = self.offset_at(range.start);
let end = self.offset_at(range.end);
if start <= end && end <= self.text.len() {
self.text.replace_range(start..end, &change.text);
}
} else {
self.text = change.text;
}
}
}
pub fn range_from_byte_range(&self, start: usize, end: usize) -> Range {
Range {
start: self.position_at(start),
end: self.position_at(end),
}
}
pub fn position_at(&self, offset: usize) -> Position {
let target = offset.min(self.text.len());
let mut line = 0_u32;
let mut character = 0_u32;
for (idx, ch) in self.text.char_indices() {
if idx >= target {
break;
}
if ch == '\n' {
line += 1;
character = 0;
} else {
character += ch.len_utf16() as u32;
}
}
Position { line, character }
}
pub fn offset_at(&self, position: Position) -> usize {
let mut line = 0_u32;
let mut character = 0_u32;
for (idx, ch) in self.text.char_indices() {
if line == position.line && character >= position.character {
return idx;
}
if ch == '\n' {
if line == position.line {
return idx;
}
line += 1;
character = 0;
} else {
character += ch.len_utf16() as u32;
}
}
self.text.len()
}
}
pub fn position_in_range(position: Position, range: Range) -> bool {
if position.line < range.start.line || position.line > range.end.line {
return false;
}
if position.line == range.start.line && position.character < range.start.character {
return false;
}
if position.line == range.end.line && position.character > range.end.character {
return false;
}
true
}