1use std::sync::Arc;
9
10use dashmap::DashMap;
11use ropey::Rope;
12
13use crate::edit::{self, EditError, TextEdit};
14use crate::encoding::PositionEncoding;
15
16#[non_exhaustive]
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20pub struct DocumentUri(String);
21
22impl DocumentUri {
23 #[must_use]
25 pub fn new(uri: impl Into<String>) -> Self {
26 Self(uri.into())
27 }
28
29 #[must_use]
31 pub fn as_str(&self) -> &str {
32 &self.0
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
38pub struct DocumentVersion(i32);
39
40impl DocumentVersion {
41 #[must_use]
43 pub const fn new(value: i32) -> Self {
44 Self(value)
45 }
46
47 #[must_use]
49 pub const fn get(self) -> i32 {
50 self.0
51 }
52}
53
54#[non_exhaustive]
56#[derive(Debug, thiserror::Error)]
57pub enum VfsError {
58 #[error("document not found: {0}")]
60 NotFound(String),
61 #[error("stale version: have {have}, got {got}")]
63 StaleVersion {
64 have: i32,
66 got: i32,
68 },
69 #[error(transparent)]
71 Edit(#[from] EditError),
72}
73
74#[derive(Debug)]
75struct DocumentEntry {
76 rope: Rope,
77 version: DocumentVersion,
78}
79
80#[derive(Debug, Default, Clone)]
82pub struct Vfs {
83 docs: Arc<DashMap<DocumentUri, DocumentEntry>>,
84 encoding: PositionEncoding,
85}
86
87impl Vfs {
88 #[must_use]
90 pub fn new(encoding: PositionEncoding) -> Self {
91 Self {
92 docs: Arc::new(DashMap::new()),
93 encoding,
94 }
95 }
96
97 pub fn open(&self, uri: DocumentUri, text: &str, version: DocumentVersion) {
99 let entry = DocumentEntry {
100 rope: Rope::from_str(text),
101 version,
102 };
103 self.docs.insert(uri, entry);
104 }
105
106 pub fn change(
113 &self,
114 uri: &DocumentUri,
115 edits: &[TextEdit],
116 version: DocumentVersion,
117 ) -> Result<(), VfsError> {
118 let mut entry = self
119 .docs
120 .get_mut(uri)
121 .ok_or_else(|| VfsError::NotFound(uri.as_str().to_owned()))?;
122 if version.get() <= entry.version.get() {
123 return Err(VfsError::StaleVersion {
124 have: entry.version.get(),
125 got: version.get(),
126 });
127 }
128 edit::apply(&mut entry.rope, edits, self.encoding)?;
129 entry.version = version;
130 Ok(())
131 }
132
133 pub fn close(&self, uri: &DocumentUri) {
135 self.docs.remove(uri);
136 }
137
138 #[must_use]
140 pub fn text(&self, uri: &DocumentUri) -> Option<String> {
141 self.docs.get(uri).map(|entry| entry.rope.to_string())
142 }
143
144 #[must_use]
146 pub fn version(&self, uri: &DocumentUri) -> Option<DocumentVersion> {
147 self.docs.get(uri).map(|entry| entry.version)
148 }
149
150 #[must_use]
152 pub fn encoding(&self) -> PositionEncoding {
153 self.encoding
154 }
155
156 #[must_use]
158 pub fn len(&self) -> usize {
159 self.docs.len()
160 }
161
162 #[must_use]
164 pub fn is_empty(&self) -> bool {
165 self.docs.is_empty()
166 }
167}