1use crate::services::terminal::TerminalId;
14use crate::view::file_tree::{FileTreeView, NodeId};
15use lsp_types::{
16 CodeActionOrCommand, CompletionItem, Diagnostic, FoldingRange, InlayHint, Location,
17 SemanticTokensFullDeltaResult, SemanticTokensRangeResult, SemanticTokensResult, SignatureHelp,
18};
19use serde_json::Value;
20use std::sync::mpsc;
21
22#[derive(Debug)]
24pub enum LspSemanticTokensResponse {
25 Full(Result<Option<SemanticTokensResult>, String>),
26 FullDelta(Result<Option<SemanticTokensFullDeltaResult>, String>),
27 Range(Result<Option<SemanticTokensRangeResult>, String>),
28}
29
30#[derive(Debug)]
32pub enum AsyncMessage {
33 LspDiagnostics {
35 uri: String,
36 diagnostics: Vec<Diagnostic>,
37 server_name: String,
39 },
40
41 LspInitialized {
43 language: String,
44 server_name: String,
46 capabilities: crate::services::lsp::manager::ServerCapabilitySummary,
48 },
49
50 LspError {
52 language: String,
53 error: String,
54 stderr_log_path: Option<std::path::PathBuf>,
56 },
57
58 LspCompletion {
60 request_id: u64,
61 items: Vec<CompletionItem>,
62 },
63
64 LspGotoDefinition {
66 request_id: u64,
67 locations: Vec<Location>,
68 },
69
70 LspRename {
72 request_id: u64,
73 result: Result<lsp_types::WorkspaceEdit, String>,
74 },
75
76 LspHover {
78 request_id: u64,
79 contents: String,
81 is_markdown: bool,
83 range: Option<((u32, u32), (u32, u32))>,
86 },
87
88 LspReferences {
90 request_id: u64,
91 locations: Vec<Location>,
92 },
93
94 LspSignatureHelp {
96 request_id: u64,
97 signature_help: Option<SignatureHelp>,
98 },
99
100 LspCodeActions {
102 request_id: u64,
103 actions: Vec<CodeActionOrCommand>,
104 },
105
106 LspCompletionResolved {
108 request_id: u64,
109 item: Result<lsp_types::CompletionItem, String>,
110 },
111
112 LspFormatting {
114 request_id: u64,
115 uri: String,
116 edits: Vec<lsp_types::TextEdit>,
117 },
118
119 LspPrepareRename {
121 request_id: u64,
122 result: Result<serde_json::Value, String>,
123 },
124
125 LspPulledDiagnostics {
127 request_id: u64,
128 uri: String,
129 result_id: Option<String>,
131 diagnostics: Vec<Diagnostic>,
133 unchanged: bool,
135 },
136
137 LspInlayHints {
139 request_id: u64,
140 uri: String,
141 hints: Vec<InlayHint>,
143 },
144
145 LspFoldingRanges {
147 request_id: u64,
148 uri: String,
149 ranges: Vec<FoldingRange>,
150 },
151
152 LspSemanticTokens {
154 request_id: u64,
155 uri: String,
156 response: LspSemanticTokensResponse,
157 },
158
159 LspServerQuiescent { language: String },
162
163 LspDiagnosticRefresh { language: String },
166
167 FileChanged { path: String },
169
170 GitStatusChanged { status: String },
172
173 FileExplorerInitialized(FileTreeView),
175
176 FileExplorerToggleNode(NodeId),
178
179 FileExplorerRefreshNode(NodeId),
181
182 FileExplorerExpandedToPath(FileTreeView),
185
186 Plugin(fresh_core::api::PluginAsyncMessage),
188
189 FileOpenDirectoryLoaded(std::io::Result<Vec<crate::services::fs::DirEntry>>),
191
192 FileOpenShortcutsLoaded(Vec<crate::app::file_open::NavigationShortcut>),
194
195 TerminalOutput { terminal_id: TerminalId },
197
198 TerminalExited { terminal_id: TerminalId },
200
201 LspProgress {
203 language: String,
204 token: String,
205 value: LspProgressValue,
206 },
207
208 LspWindowMessage {
210 language: String,
211 message_type: LspMessageType,
212 message: String,
213 },
214
215 LspLogMessage {
217 language: String,
218 message_type: LspMessageType,
219 message: String,
220 },
221
222 LspApplyEdit {
225 edit: lsp_types::WorkspaceEdit,
226 label: Option<String>,
227 },
228
229 LspCodeActionResolved {
231 request_id: u64,
232 action: Result<lsp_types::CodeAction, String>,
233 },
234
235 LspServerRequest {
238 language: String,
239 server_command: String,
240 method: String,
241 params: Option<Value>,
242 },
243
244 PluginLspResponse {
246 language: String,
247 request_id: u64,
248 result: Result<Value, String>,
249 },
250
251 PluginProcessOutput {
253 process_id: u64,
255 stdout: String,
257 stderr: String,
259 exit_code: i32,
261 },
262
263 LspStatusUpdate {
265 language: String,
266 server_name: String,
268 status: LspServerStatus,
269 message: Option<String>,
270 },
271
272 GrammarRegistryBuilt {
276 registry: std::sync::Arc<crate::primitives::grammar::GrammarRegistry>,
277 callback_ids: Vec<fresh_core::api::JsCallbackId>,
278 },
279}
280
281#[derive(Debug, Clone)]
283pub enum LspProgressValue {
284 Begin {
285 title: String,
286 message: Option<String>,
287 percentage: Option<u32>,
288 },
289 Report {
290 message: Option<String>,
291 percentage: Option<u32>,
292 },
293 End {
294 message: Option<String>,
295 },
296}
297
298#[derive(Debug, Clone, Copy, PartialEq, Eq)]
300pub enum LspMessageType {
301 Error = 1,
302 Warning = 2,
303 Info = 3,
304 Log = 4,
305}
306
307#[derive(Debug, Clone, Copy, PartialEq, Eq)]
309pub enum LspServerStatus {
310 Starting,
311 Initializing,
312 Running,
313 Error,
314 Shutdown,
315}
316
317#[derive(Clone)]
324pub struct AsyncBridge {
325 sender: mpsc::Sender<AsyncMessage>,
326 receiver: std::sync::Arc<std::sync::Mutex<mpsc::Receiver<AsyncMessage>>>,
328}
329
330impl AsyncBridge {
331 pub fn new() -> Self {
338 let (sender, receiver) = mpsc::channel();
339 Self {
340 sender,
341 receiver: std::sync::Arc::new(std::sync::Mutex::new(receiver)),
342 }
343 }
344
345 pub fn sender(&self) -> mpsc::Sender<AsyncMessage> {
352 self.sender.clone()
353 }
354
355 pub fn try_recv_all(&self) -> Vec<AsyncMessage> {
360 let mut messages = Vec::new();
361
362 if let Ok(receiver) = self.receiver.lock() {
364 while let Ok(msg) = receiver.try_recv() {
365 messages.push(msg);
366 }
367 }
368
369 messages
370 }
371
372 pub fn has_messages(&self) -> bool {
374 if let Ok(receiver) = self.receiver.lock() {
376 receiver.try_recv().is_ok()
377 } else {
378 false
379 }
380 }
381}
382
383impl Default for AsyncBridge {
384 fn default() -> Self {
385 Self::new()
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
394 fn test_async_bridge_send_receive() {
395 let bridge = AsyncBridge::new();
396 let sender = bridge.sender();
397
398 sender
400 .send(AsyncMessage::LspInitialized {
401 language: "rust".to_string(),
402 server_name: "test".to_string(),
403 capabilities: Default::default(),
404 })
405 .unwrap();
406
407 let messages = bridge.try_recv_all();
409 assert_eq!(messages.len(), 1);
410
411 match &messages[0] {
412 AsyncMessage::LspInitialized {
413 language,
414 server_name,
415 ..
416 } => {
417 assert_eq!(language, "rust");
418 assert_eq!(server_name, "test");
419 }
420 _ => panic!("Wrong message type"),
421 }
422 }
423
424 #[test]
425 fn test_async_bridge_multiple_messages() {
426 let bridge = AsyncBridge::new();
427 let sender = bridge.sender();
428
429 sender
431 .send(AsyncMessage::LspInitialized {
432 language: "rust".to_string(),
433 server_name: "test".to_string(),
434 capabilities: Default::default(),
435 })
436 .unwrap();
437 sender
438 .send(AsyncMessage::LspInitialized {
439 language: "typescript".to_string(),
440 server_name: "test".to_string(),
441 capabilities: Default::default(),
442 })
443 .unwrap();
444
445 let messages = bridge.try_recv_all();
447 assert_eq!(messages.len(), 2);
448 }
449
450 #[test]
451 fn test_async_bridge_no_messages() {
452 let bridge = AsyncBridge::new();
453
454 let messages = bridge.try_recv_all();
456 assert_eq!(messages.len(), 0);
457 }
458
459 #[test]
460 fn test_async_bridge_clone_sender() {
461 let bridge = AsyncBridge::new();
462 let sender1 = bridge.sender();
463 let sender2 = sender1.clone();
464
465 sender1
467 .send(AsyncMessage::LspInitialized {
468 language: "rust".to_string(),
469 server_name: "test".to_string(),
470 capabilities: Default::default(),
471 })
472 .unwrap();
473 sender2
474 .send(AsyncMessage::LspInitialized {
475 language: "typescript".to_string(),
476 server_name: "test".to_string(),
477 capabilities: Default::default(),
478 })
479 .unwrap();
480
481 let messages = bridge.try_recv_all();
482 assert_eq!(messages.len(), 2);
483 }
484
485 #[test]
486 fn test_async_bridge_diagnostics() {
487 let bridge = AsyncBridge::new();
488 let sender = bridge.sender();
489
490 let diagnostics = vec![lsp_types::Diagnostic {
492 range: lsp_types::Range {
493 start: lsp_types::Position {
494 line: 0,
495 character: 0,
496 },
497 end: lsp_types::Position {
498 line: 0,
499 character: 5,
500 },
501 },
502 severity: Some(lsp_types::DiagnosticSeverity::ERROR),
503 code: None,
504 code_description: None,
505 source: Some("rust-analyzer".to_string()),
506 message: "test error".to_string(),
507 related_information: None,
508 tags: None,
509 data: None,
510 }];
511
512 sender
513 .send(AsyncMessage::LspDiagnostics {
514 uri: "file:///test.rs".to_string(),
515 diagnostics: diagnostics.clone(),
516 server_name: "rust-analyzer".to_string(),
517 })
518 .unwrap();
519
520 let messages = bridge.try_recv_all();
521 assert_eq!(messages.len(), 1);
522
523 match &messages[0] {
524 AsyncMessage::LspDiagnostics {
525 uri,
526 diagnostics: diags,
527 server_name,
528 } => {
529 assert_eq!(uri, "file:///test.rs");
530 assert_eq!(diags.len(), 1);
531 assert_eq!(diags[0].message, "test error");
532 assert_eq!(server_name, "rust-analyzer");
533 }
534 _ => panic!("Expected LspDiagnostics message"),
535 }
536 }
537
538 #[test]
539 fn test_async_bridge_error_message() {
540 let bridge = AsyncBridge::new();
541 let sender = bridge.sender();
542
543 sender
544 .send(AsyncMessage::LspError {
545 language: "rust".to_string(),
546 error: "Failed to initialize".to_string(),
547 stderr_log_path: None,
548 })
549 .unwrap();
550
551 let messages = bridge.try_recv_all();
552 assert_eq!(messages.len(), 1);
553
554 match &messages[0] {
555 AsyncMessage::LspError {
556 language,
557 error,
558 stderr_log_path,
559 } => {
560 assert_eq!(language, "rust");
561 assert_eq!(error, "Failed to initialize");
562 assert!(stderr_log_path.is_none());
563 }
564 _ => panic!("Expected LspError message"),
565 }
566 }
567
568 #[test]
569 fn test_async_bridge_clone_bridge() {
570 let bridge = AsyncBridge::new();
571 let bridge_clone = bridge.clone();
572 let sender = bridge.sender();
573
574 sender
576 .send(AsyncMessage::LspInitialized {
577 language: "rust".to_string(),
578 server_name: "test".to_string(),
579 capabilities: Default::default(),
580 })
581 .unwrap();
582
583 let messages = bridge_clone.try_recv_all();
585 assert_eq!(messages.len(), 1);
586 }
587
588 #[test]
589 fn test_async_bridge_multiple_calls_to_try_recv_all() {
590 let bridge = AsyncBridge::new();
591 let sender = bridge.sender();
592
593 sender
594 .send(AsyncMessage::LspInitialized {
595 language: "rust".to_string(),
596 server_name: "test".to_string(),
597 capabilities: Default::default(),
598 })
599 .unwrap();
600
601 let messages1 = bridge.try_recv_all();
603 assert_eq!(messages1.len(), 1);
604
605 let messages2 = bridge.try_recv_all();
607 assert_eq!(messages2.len(), 0);
608 }
609
610 #[test]
611 fn test_async_bridge_ordering() {
612 let bridge = AsyncBridge::new();
613 let sender = bridge.sender();
614
615 sender
617 .send(AsyncMessage::LspInitialized {
618 language: "rust".to_string(),
619 server_name: "test".to_string(),
620 capabilities: Default::default(),
621 })
622 .unwrap();
623 sender
624 .send(AsyncMessage::LspInitialized {
625 language: "typescript".to_string(),
626 server_name: "test".to_string(),
627 capabilities: Default::default(),
628 })
629 .unwrap();
630 sender
631 .send(AsyncMessage::LspInitialized {
632 language: "python".to_string(),
633 server_name: "test".to_string(),
634 capabilities: Default::default(),
635 })
636 .unwrap();
637
638 let messages = bridge.try_recv_all();
640 assert_eq!(messages.len(), 3);
641
642 match (&messages[0], &messages[1], &messages[2]) {
643 (
644 AsyncMessage::LspInitialized { language: l1, .. },
645 AsyncMessage::LspInitialized { language: l2, .. },
646 AsyncMessage::LspInitialized { language: l3, .. },
647 ) => {
648 assert_eq!(l1, "rust");
649 assert_eq!(l2, "typescript");
650 assert_eq!(l3, "python");
651 }
652 _ => panic!("Expected ordered LspInitialized messages"),
653 }
654 }
655}