1pub mod capabilities;
7pub mod code_actions;
8pub mod completion;
9pub mod diagnostics;
10pub mod formatting;
11pub mod hover;
12pub mod protocol;
13pub mod server;
14pub mod text_document;
15
16use serde::{Deserialize, Serialize};
17use std::collections::HashMap;
18use std::sync::Arc;
19use tokio::sync::RwLock;
20
21pub use server::LspServer;
22
23pub struct LspState {
25 pub documents: Arc<RwLock<HashMap<String, TextDocument>>>,
27
28 pub voices: Arc<RwLock<Vec<VoiceInfo>>>,
30
31 pub capabilities: ServerCapabilities,
33}
34
35impl LspState {
36 pub fn new() -> Self {
38 Self {
39 documents: Arc::new(RwLock::new(HashMap::new())),
40 voices: Arc::new(RwLock::new(Vec::new())),
41 capabilities: ServerCapabilities::default(),
42 }
43 }
44
45 pub async fn open_document(&self, uri: String, text: String, language_id: String) {
47 let document = TextDocument::new(uri.clone(), text, language_id);
48 self.documents.write().await.insert(uri, document);
49 }
50
51 pub async fn update_document(&self, uri: &str, text: String, version: i32) {
53 if let Some(doc) = self.documents.write().await.get_mut(uri) {
54 doc.update(text, version);
55 }
56 }
57
58 pub async fn close_document(&self, uri: &str) {
60 self.documents.write().await.remove(uri);
61 }
62
63 pub async fn get_document(&self, uri: &str) -> Option<TextDocument> {
65 self.documents.read().await.get(uri).cloned()
66 }
67}
68
69impl Default for LspState {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75#[derive(Debug, Clone)]
77pub struct TextDocument {
78 pub uri: String,
80
81 pub text: String,
83
84 pub language_id: String,
86
87 pub version: i32,
89}
90
91impl TextDocument {
92 pub fn new(uri: String, text: String, language_id: String) -> Self {
94 Self {
95 uri,
96 text,
97 language_id,
98 version: 1,
99 }
100 }
101
102 pub fn update(&mut self, text: String, version: i32) {
104 self.text = text;
105 self.version = version;
106 }
107
108 pub fn line_count(&self) -> usize {
110 self.text.lines().count()
111 }
112
113 pub fn get_line(&self, line: usize) -> Option<&str> {
115 self.text.lines().nth(line)
116 }
117
118 pub fn get_char_at(&self, line: usize, character: usize) -> Option<char> {
120 self.get_line(line)?.chars().nth(character)
121 }
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct VoiceInfo {
127 pub id: String,
129
130 pub name: String,
132
133 pub language: String,
135
136 pub description: String,
138
139 pub features: Vec<String>,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct ServerCapabilities {
146 pub text_document_sync: TextDocumentSyncKind,
148
149 pub completion_provider: bool,
151
152 pub hover_provider: bool,
154
155 pub diagnostic_provider: bool,
157
158 pub code_action_provider: bool,
160}
161
162impl Default for ServerCapabilities {
163 fn default() -> Self {
164 Self {
165 text_document_sync: TextDocumentSyncKind::Full,
166 completion_provider: true,
167 hover_provider: true,
168 diagnostic_provider: true,
169 code_action_provider: true,
170 }
171 }
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
176pub enum TextDocumentSyncKind {
177 None,
179
180 Full,
182
183 Incremental,
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
189pub struct Position {
190 pub line: u32,
192
193 pub character: u32,
195}
196
197impl Position {
198 pub fn new(line: u32, character: u32) -> Self {
200 Self { line, character }
201 }
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
206pub struct Range {
207 pub start: Position,
209
210 pub end: Position,
212}
213
214impl Range {
215 pub fn new(start: Position, end: Position) -> Self {
217 Self { start, end }
218 }
219
220 pub fn single_line(line: u32, start_char: u32, end_char: u32) -> Self {
222 Self {
223 start: Position::new(line, start_char),
224 end: Position::new(line, end_char),
225 }
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 #[tokio::test]
234 async fn test_lsp_state_creation() {
235 let state = LspState::new();
236 assert!(state.documents.read().await.is_empty());
237 }
238
239 #[tokio::test]
240 async fn test_open_document() {
241 let state = LspState::new();
242 state
243 .open_document(
244 "file:///test.ssml".to_string(),
245 "<speak>Hello</speak>".to_string(),
246 "ssml".to_string(),
247 )
248 .await;
249
250 let docs = state.documents.read().await;
251 assert_eq!(docs.len(), 1);
252 assert!(docs.contains_key("file:///test.ssml"));
253 }
254
255 #[tokio::test]
256 async fn test_update_document() {
257 let state = LspState::new();
258 state
259 .open_document(
260 "file:///test.ssml".to_string(),
261 "<speak>Hello</speak>".to_string(),
262 "ssml".to_string(),
263 )
264 .await;
265
266 state
267 .update_document("file:///test.ssml", "<speak>World</speak>".to_string(), 2)
268 .await;
269
270 let doc = state.get_document("file:///test.ssml").await.unwrap();
271 assert_eq!(doc.text, "<speak>World</speak>");
272 assert_eq!(doc.version, 2);
273 }
274
275 #[tokio::test]
276 async fn test_close_document() {
277 let state = LspState::new();
278 state
279 .open_document(
280 "file:///test.ssml".to_string(),
281 "<speak>Hello</speak>".to_string(),
282 "ssml".to_string(),
283 )
284 .await;
285
286 state.close_document("file:///test.ssml").await;
287
288 assert!(state.get_document("file:///test.ssml").await.is_none());
289 }
290
291 #[test]
292 fn test_text_document_line_count() {
293 let doc = TextDocument::new(
294 "file:///test.txt".to_string(),
295 "line 1\nline 2\nline 3".to_string(),
296 "text".to_string(),
297 );
298 assert_eq!(doc.line_count(), 3);
299 }
300
301 #[test]
302 fn test_position_creation() {
303 let pos = Position::new(5, 10);
304 assert_eq!(pos.line, 5);
305 assert_eq!(pos.character, 10);
306 }
307
308 #[test]
309 fn test_range_creation() {
310 let range = Range::single_line(3, 5, 15);
311 assert_eq!(range.start.line, 3);
312 assert_eq!(range.start.character, 5);
313 assert_eq!(range.end.line, 3);
314 assert_eq!(range.end.character, 15);
315 }
316
317 #[test]
318 fn test_server_capabilities_default() {
319 let caps = ServerCapabilities::default();
320 assert!(caps.completion_provider);
321 assert!(caps.hover_provider);
322 assert!(caps.diagnostic_provider);
323 assert_eq!(caps.text_document_sync, TextDocumentSyncKind::Full);
324 }
325}