1use crate::EditorStateManager;
13use std::collections::{BTreeMap, HashMap};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct DocumentId(u64);
18
19impl DocumentId {
20 pub fn get(self) -> u64 {
22 self.0
23 }
24}
25
26#[derive(Debug, Clone)]
28pub struct DocumentMetadata {
29 pub uri: Option<String>,
31}
32
33struct DocumentEntry {
34 meta: DocumentMetadata,
35 state: EditorStateManager,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum WorkspaceError {
41 UriAlreadyOpen(String),
43 DocumentNotFound(DocumentId),
45}
46
47#[derive(Default)]
49pub struct Workspace {
50 next_id: u64,
51 documents: BTreeMap<DocumentId, DocumentEntry>,
52 uri_to_id: HashMap<String, DocumentId>,
53 active: Option<DocumentId>,
54}
55
56impl std::fmt::Debug for Workspace {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.debug_struct("Workspace")
59 .field("next_id", &self.next_id)
60 .field("document_count", &self.documents.len())
61 .field("uri_count", &self.uri_to_id.len())
62 .field("active", &self.active)
63 .finish()
64 }
65}
66
67impl Workspace {
68 pub fn new() -> Self {
70 Self::default()
71 }
72
73 pub fn len(&self) -> usize {
75 self.documents.len()
76 }
77
78 pub fn is_empty(&self) -> bool {
80 self.documents.is_empty()
81 }
82
83 pub fn active_document_id(&self) -> Option<DocumentId> {
85 self.active
86 }
87
88 pub fn set_active_document(&mut self, id: DocumentId) -> Result<(), WorkspaceError> {
90 if !self.documents.contains_key(&id) {
91 return Err(WorkspaceError::DocumentNotFound(id));
92 }
93 self.active = Some(id);
94 Ok(())
95 }
96
97 pub fn open_document(
102 &mut self,
103 uri: Option<String>,
104 text: &str,
105 viewport_width: usize,
106 ) -> Result<DocumentId, WorkspaceError> {
107 if let Some(uri) = uri.as_ref()
108 && self.uri_to_id.contains_key(uri)
109 {
110 return Err(WorkspaceError::UriAlreadyOpen(uri.clone()));
111 }
112
113 let id = DocumentId(self.next_id);
114 self.next_id = self.next_id.saturating_add(1);
115
116 let state = EditorStateManager::new(text, viewport_width);
117 let meta = DocumentMetadata { uri: uri.clone() };
118
119 if let Some(uri) = uri {
120 self.uri_to_id.insert(uri, id);
121 }
122 self.documents.insert(id, DocumentEntry { meta, state });
123
124 if self.active.is_none() {
125 self.active = Some(id);
126 }
127
128 Ok(id)
129 }
130
131 pub fn close_document(&mut self, id: DocumentId) -> Result<(), WorkspaceError> {
133 let Some(entry) = self.documents.remove(&id) else {
134 return Err(WorkspaceError::DocumentNotFound(id));
135 };
136
137 if let Some(uri) = entry.meta.uri.as_ref() {
138 self.uri_to_id.remove(uri);
139 }
140
141 if self.active == Some(id) {
142 self.active = self.documents.keys().next().copied();
143 }
144
145 Ok(())
146 }
147
148 pub fn document_id_for_uri(&self, uri: &str) -> Option<DocumentId> {
150 self.uri_to_id.get(uri).copied()
151 }
152
153 pub fn document_metadata(&self, id: DocumentId) -> Option<&DocumentMetadata> {
155 self.documents.get(&id).map(|e| &e.meta)
156 }
157
158 pub fn set_document_uri(
160 &mut self,
161 id: DocumentId,
162 uri: Option<String>,
163 ) -> Result<(), WorkspaceError> {
164 let Some(entry) = self.documents.get_mut(&id) else {
165 return Err(WorkspaceError::DocumentNotFound(id));
166 };
167
168 if let Some(next) = uri.as_ref()
169 && self.uri_to_id.contains_key(next)
170 && entry.meta.uri.as_deref() != Some(next.as_str())
171 {
172 return Err(WorkspaceError::UriAlreadyOpen(next.clone()));
173 }
174
175 if let Some(prev) = entry.meta.uri.take() {
176 self.uri_to_id.remove(&prev);
177 }
178
179 if let Some(next) = uri.clone() {
180 self.uri_to_id.insert(next, id);
181 }
182
183 entry.meta.uri = uri;
184 Ok(())
185 }
186
187 pub fn document(&self, id: DocumentId) -> Option<&EditorStateManager> {
189 self.documents.get(&id).map(|e| &e.state)
190 }
191
192 pub fn document_mut(&mut self, id: DocumentId) -> Option<&mut EditorStateManager> {
194 self.documents.get_mut(&id).map(|e| &mut e.state)
195 }
196
197 pub fn active_document(&self) -> Option<&EditorStateManager> {
199 let id = self.active?;
200 self.document(id)
201 }
202
203 pub fn active_document_mut(&mut self) -> Option<&mut EditorStateManager> {
205 let id = self.active?;
206 self.document_mut(id)
207 }
208
209 pub fn iter(&self) -> impl Iterator<Item = (DocumentId, &EditorStateManager)> {
211 self.documents.iter().map(|(id, entry)| (*id, &entry.state))
212 }
213}