use std::sync::Arc;
use dashmap::DashMap;
use ropey::Rope;
use tower_lsp::lsp_types::{
Position,
Range,
TextDocumentContentChangeEvent,
Url,
};
#[derive(Debug, Clone)]
pub struct Document {
pub version: i32,
pub rope: Rope,
pub ignored: bool,
}
impl Document {
pub fn new(text: String, version: i32, ignored: bool) -> Self {
Self {
version,
rope: Rope::from_str(&text),
ignored,
}
}
pub fn text(&self) -> String {
self.rope.to_string()
}
pub fn apply_change(&mut self, change: TextDocumentContentChangeEvent) {
match change.range {
None => {
self.rope = Rope::from_str(&change.text);
}
Some(range) => {
let start = position_to_char(&self.rope, range.start);
let end = position_to_char(&self.rope, range.end);
self.rope.remove(start..end);
self.rope.insert(start, &change.text);
}
}
}
}
fn position_to_char(rope: &Rope, pos: Position) -> usize {
let line = (pos.line as usize).min(rope.len_lines().saturating_sub(1));
let line_start = rope.line_to_char(line);
let line_slice = rope.line(line);
let col = (pos.character as usize).min(line_slice.len_chars());
line_start + col
}
#[allow(dead_code)]
pub fn range_to_chars(rope: &Rope, range: Range) -> std::ops::Range<usize> {
position_to_char(rope, range.start)..position_to_char(rope, range.end)
}
#[derive(Debug, Default, Clone)]
pub struct DocumentStore {
inner: Arc<DashMap<Url, Document>>,
}
impl DocumentStore {
pub fn new() -> Self {
Self::default()
}
pub fn open(&self, uri: Url, text: String, version: i32, ignored: bool) {
self
.inner
.insert(uri, Document::new(text, version, ignored));
}
pub fn is_ignored(&self, uri: &Url) -> bool {
self.inner.get(uri).map(|d| d.ignored).unwrap_or(false)
}
pub fn close(&self, uri: &Url) {
self.inner.remove(uri);
}
pub fn update(
&self,
uri: &Url,
version: i32,
changes: Vec<TextDocumentContentChangeEvent>,
) -> Option<String> {
let mut entry = self.inner.get_mut(uri)?;
for change in changes {
entry.apply_change(change);
}
entry.version = version;
Some(entry.text())
}
pub fn get_text(&self, uri: &Url) -> Option<String> {
self.inner.get(uri).map(|d| d.text())
}
}