1use crate::module_cache::ModuleCache;
6use crate::symbols::SymbolInfo;
7use dashmap::DashMap;
8use ropey::Rope;
9use std::collections::HashMap;
10use std::path::PathBuf;
11use std::sync::Arc;
12use tower_lsp_server::ls_types::Uri;
13
14#[derive(Debug, Clone)]
16pub struct Document {
17 pub uri: Uri,
19 pub version: i32,
21 pub rope: Rope,
23 pub cached_symbols: Vec<SymbolInfo>,
25 pub cached_types: HashMap<String, String>,
27}
28
29impl Document {
30 pub fn new(uri: Uri, version: i32, text: String) -> Self {
32 Self {
33 uri,
34 version,
35 rope: Rope::from_str(&text),
36 cached_symbols: Vec::new(),
37 cached_types: HashMap::new(),
38 }
39 }
40
41 pub fn update_cached_symbols(&mut self, symbols: Vec<SymbolInfo>) {
43 self.cached_symbols = symbols;
44 }
45
46 pub fn update_cached_types(&mut self, types: HashMap<String, String>) {
48 self.cached_types = types;
49 }
50
51 pub fn get_cached_symbols(&self) -> &[SymbolInfo] {
53 &self.cached_symbols
54 }
55
56 pub fn get_cached_types(&self) -> &HashMap<String, String> {
58 &self.cached_types
59 }
60
61 pub fn text(&self) -> String {
63 self.rope.to_string()
64 }
65
66 pub fn line_count(&self) -> usize {
68 self.rope.len_lines()
69 }
70
71 pub fn line(&self, line_idx: usize) -> Option<String> {
73 if line_idx >= self.line_count() {
74 return None;
75 }
76
77 let start = self.rope.line_to_char(line_idx);
78 let end = if line_idx + 1 < self.line_count() {
79 self.rope.line_to_char(line_idx + 1)
80 } else {
81 self.rope.len_chars()
82 };
83
84 Some(self.rope.slice(start..end).to_string())
85 }
86
87 pub fn position_to_offset(&self, line: u32, character: u32) -> Option<usize> {
89 let line_idx = line as usize;
90 if line_idx >= self.line_count() {
91 return None;
92 }
93
94 let line_start = self.rope.line_to_char(line_idx);
95 let offset = line_start + character as usize;
96
97 if offset > self.rope.len_chars() {
98 return None;
99 }
100
101 Some(offset)
102 }
103
104 pub fn offset_to_position(&self, offset: usize) -> Option<(u32, u32)> {
106 if offset > self.rope.len_chars() {
107 return None;
108 }
109
110 let line = self.rope.char_to_line(offset);
111 let line_start = self.rope.line_to_char(line);
112 let column = offset - line_start;
113
114 Some((line as u32, column as u32))
115 }
116}
117
118#[derive(Debug)]
120pub struct DocumentManager {
121 documents: DashMap<Uri, Document>,
123 module_cache: Arc<ModuleCache>,
125}
126
127impl Default for DocumentManager {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133impl DocumentManager {
134 pub fn new() -> Self {
136 Self {
137 documents: DashMap::new(),
138 module_cache: Arc::new(ModuleCache::new()),
139 }
140 }
141
142 pub fn get_module_cache(&self) -> Arc<ModuleCache> {
144 self.module_cache.clone()
145 }
146
147 pub fn open(&self, uri: Uri, version: i32, text: String) {
149 let doc = Document::new(uri.clone(), version, text);
150 self.documents.insert(uri, doc);
151 }
152
153 pub fn close(&self, uri: &Uri) {
155 let path = PathBuf::from(uri.path().as_str());
157 self.module_cache.invalidate(&path);
158
159 self.documents.remove(uri);
160 }
161
162 pub fn update(&self, uri: &Uri, version: i32, text: String) {
164 let path = PathBuf::from(uri.path().as_str());
166 self.module_cache.invalidate(&path);
167
168 if let Some(mut doc) = self.documents.get_mut(uri) {
169 doc.version = version;
170 doc.rope = Rope::from_str(&text);
171 }
172 }
173
174 pub fn get(&self, uri: &Uri) -> Option<Document> {
176 self.documents.get(uri).map(|doc| doc.clone())
177 }
178
179 pub fn contains(&self, uri: &Uri) -> bool {
181 self.documents.contains_key(uri)
182 }
183
184 pub fn all_uris(&self) -> Vec<Uri> {
186 self.documents
187 .iter()
188 .map(|entry| entry.key().clone())
189 .collect()
190 }
191
192 pub fn update_cached_symbols(&self, uri: &Uri, symbols: Vec<SymbolInfo>) {
194 if let Some(mut doc) = self.documents.get_mut(uri) {
195 doc.update_cached_symbols(symbols);
196 }
197 }
198
199 pub fn update_cached_types(&self, uri: &Uri, types: HashMap<String, String>) {
201 if let Some(mut doc) = self.documents.get_mut(uri) {
202 doc.update_cached_types(types);
203 }
204 }
205
206 pub fn get_cached_symbols(&self, uri: &Uri) -> Vec<SymbolInfo> {
208 self.documents
209 .get(uri)
210 .map(|doc| doc.get_cached_symbols().to_vec())
211 .unwrap_or_default()
212 }
213
214 pub fn get_cached_types(&self, uri: &Uri) -> HashMap<String, String> {
216 self.documents
217 .get(uri)
218 .map(|doc| doc.get_cached_types().clone())
219 .unwrap_or_default()
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 #[test]
228 fn test_document_creation() {
229 let uri = Uri::from_file_path("/test.shape").unwrap();
230 let doc = Document::new(uri.clone(), 1, "let x = 5;\nlet y = 10;".to_string());
231
232 assert_eq!(doc.version, 1);
233 assert_eq!(doc.line_count(), 2);
234 assert_eq!(doc.text(), "let x = 5;\nlet y = 10;");
235 }
236
237 #[test]
238 fn test_position_conversion() {
239 let uri = Uri::from_file_path("/test.shape").unwrap();
240 let doc = Document::new(uri, 1, "let x = 5;\nlet y = 10;".to_string());
241
242 let offset = doc.position_to_offset(0, 4).unwrap();
244 assert_eq!(doc.text().chars().nth(offset), Some('x'));
245
246 let (line, col) = doc.offset_to_position(4).unwrap();
248 assert_eq!(line, 0);
249 assert_eq!(col, 4);
250 }
251
252 #[test]
253 fn test_document_manager() {
254 let manager = DocumentManager::new();
255 let uri = Uri::from_file_path("/test.shape").unwrap();
256
257 manager.open(uri.clone(), 1, "let x = 5;".to_string());
259 assert!(manager.contains(&uri));
260
261 let doc = manager.get(&uri).unwrap();
263 assert_eq!(doc.version, 1);
264 assert_eq!(doc.text(), "let x = 5;");
265
266 manager.update(&uri, 2, "let x = 10;".to_string());
268 let doc = manager.get(&uri).unwrap();
269 assert_eq!(doc.version, 2);
270 assert_eq!(doc.text(), "let x = 10;");
271
272 manager.close(&uri);
274 assert!(!manager.contains(&uri));
275 }
276}