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 QuickOpenFilesLoaded {
284 files: std::sync::Arc<Vec<crate::input::quick_open::providers::FileEntry>>,
285 complete: bool,
286 },
287}
288
289#[derive(Debug, Clone)]
291pub enum LspProgressValue {
292 Begin {
293 title: String,
294 message: Option<String>,
295 percentage: Option<u32>,
296 },
297 Report {
298 message: Option<String>,
299 percentage: Option<u32>,
300 },
301 End {
302 message: Option<String>,
303 },
304}
305
306#[derive(Debug, Clone, Copy, PartialEq, Eq)]
308pub enum LspMessageType {
309 Error = 1,
310 Warning = 2,
311 Info = 3,
312 Log = 4,
313}
314
315#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317pub enum LspServerStatus {
318 Starting,
319 Initializing,
320 Running,
321 Error,
322 Shutdown,
323}
324
325#[derive(Clone)]
332pub struct AsyncBridge {
333 sender: mpsc::Sender<AsyncMessage>,
334 receiver: std::sync::Arc<std::sync::Mutex<mpsc::Receiver<AsyncMessage>>>,
336}
337
338impl AsyncBridge {
339 pub fn new() -> Self {
346 let (sender, receiver) = mpsc::channel();
347 Self {
348 sender,
349 receiver: std::sync::Arc::new(std::sync::Mutex::new(receiver)),
350 }
351 }
352
353 pub fn sender(&self) -> mpsc::Sender<AsyncMessage> {
360 self.sender.clone()
361 }
362
363 pub fn try_recv_all(&self) -> Vec<AsyncMessage> {
368 let mut messages = Vec::new();
369
370 if let Ok(receiver) = self.receiver.lock() {
372 while let Ok(msg) = receiver.try_recv() {
373 messages.push(msg);
374 }
375 }
376
377 messages
378 }
379
380 pub fn has_messages(&self) -> bool {
382 if let Ok(receiver) = self.receiver.lock() {
384 receiver.try_recv().is_ok()
385 } else {
386 false
387 }
388 }
389}
390
391impl Default for AsyncBridge {
392 fn default() -> Self {
393 Self::new()
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400
401 #[test]
402 fn test_async_bridge_send_receive() {
403 let bridge = AsyncBridge::new();
404 let sender = bridge.sender();
405
406 sender
408 .send(AsyncMessage::LspInitialized {
409 language: "rust".to_string(),
410 server_name: "test".to_string(),
411 capabilities: Default::default(),
412 })
413 .unwrap();
414
415 let messages = bridge.try_recv_all();
417 assert_eq!(messages.len(), 1);
418
419 match &messages[0] {
420 AsyncMessage::LspInitialized {
421 language,
422 server_name,
423 ..
424 } => {
425 assert_eq!(language, "rust");
426 assert_eq!(server_name, "test");
427 }
428 _ => panic!("Wrong message type"),
429 }
430 }
431
432 #[test]
433 fn test_async_bridge_multiple_messages() {
434 let bridge = AsyncBridge::new();
435 let sender = bridge.sender();
436
437 sender
439 .send(AsyncMessage::LspInitialized {
440 language: "rust".to_string(),
441 server_name: "test".to_string(),
442 capabilities: Default::default(),
443 })
444 .unwrap();
445 sender
446 .send(AsyncMessage::LspInitialized {
447 language: "typescript".to_string(),
448 server_name: "test".to_string(),
449 capabilities: Default::default(),
450 })
451 .unwrap();
452
453 let messages = bridge.try_recv_all();
455 assert_eq!(messages.len(), 2);
456 }
457
458 #[test]
459 fn test_async_bridge_no_messages() {
460 let bridge = AsyncBridge::new();
461
462 let messages = bridge.try_recv_all();
464 assert_eq!(messages.len(), 0);
465 }
466
467 #[test]
468 fn test_async_bridge_clone_sender() {
469 let bridge = AsyncBridge::new();
470 let sender1 = bridge.sender();
471 let sender2 = sender1.clone();
472
473 sender1
475 .send(AsyncMessage::LspInitialized {
476 language: "rust".to_string(),
477 server_name: "test".to_string(),
478 capabilities: Default::default(),
479 })
480 .unwrap();
481 sender2
482 .send(AsyncMessage::LspInitialized {
483 language: "typescript".to_string(),
484 server_name: "test".to_string(),
485 capabilities: Default::default(),
486 })
487 .unwrap();
488
489 let messages = bridge.try_recv_all();
490 assert_eq!(messages.len(), 2);
491 }
492
493 #[test]
494 fn test_async_bridge_diagnostics() {
495 let bridge = AsyncBridge::new();
496 let sender = bridge.sender();
497
498 let diagnostics = vec![lsp_types::Diagnostic {
500 range: lsp_types::Range {
501 start: lsp_types::Position {
502 line: 0,
503 character: 0,
504 },
505 end: lsp_types::Position {
506 line: 0,
507 character: 5,
508 },
509 },
510 severity: Some(lsp_types::DiagnosticSeverity::ERROR),
511 code: None,
512 code_description: None,
513 source: Some("rust-analyzer".to_string()),
514 message: "test error".to_string(),
515 related_information: None,
516 tags: None,
517 data: None,
518 }];
519
520 sender
521 .send(AsyncMessage::LspDiagnostics {
522 uri: "file:///test.rs".to_string(),
523 diagnostics: diagnostics.clone(),
524 server_name: "rust-analyzer".to_string(),
525 })
526 .unwrap();
527
528 let messages = bridge.try_recv_all();
529 assert_eq!(messages.len(), 1);
530
531 match &messages[0] {
532 AsyncMessage::LspDiagnostics {
533 uri,
534 diagnostics: diags,
535 server_name,
536 } => {
537 assert_eq!(uri, "file:///test.rs");
538 assert_eq!(diags.len(), 1);
539 assert_eq!(diags[0].message, "test error");
540 assert_eq!(server_name, "rust-analyzer");
541 }
542 _ => panic!("Expected LspDiagnostics message"),
543 }
544 }
545
546 #[test]
547 fn test_async_bridge_error_message() {
548 let bridge = AsyncBridge::new();
549 let sender = bridge.sender();
550
551 sender
552 .send(AsyncMessage::LspError {
553 language: "rust".to_string(),
554 error: "Failed to initialize".to_string(),
555 stderr_log_path: None,
556 })
557 .unwrap();
558
559 let messages = bridge.try_recv_all();
560 assert_eq!(messages.len(), 1);
561
562 match &messages[0] {
563 AsyncMessage::LspError {
564 language,
565 error,
566 stderr_log_path,
567 } => {
568 assert_eq!(language, "rust");
569 assert_eq!(error, "Failed to initialize");
570 assert!(stderr_log_path.is_none());
571 }
572 _ => panic!("Expected LspError message"),
573 }
574 }
575
576 #[test]
577 fn test_async_bridge_clone_bridge() {
578 let bridge = AsyncBridge::new();
579 let bridge_clone = bridge.clone();
580 let sender = bridge.sender();
581
582 sender
584 .send(AsyncMessage::LspInitialized {
585 language: "rust".to_string(),
586 server_name: "test".to_string(),
587 capabilities: Default::default(),
588 })
589 .unwrap();
590
591 let messages = bridge_clone.try_recv_all();
593 assert_eq!(messages.len(), 1);
594 }
595
596 #[test]
597 fn test_async_bridge_multiple_calls_to_try_recv_all() {
598 let bridge = AsyncBridge::new();
599 let sender = bridge.sender();
600
601 sender
602 .send(AsyncMessage::LspInitialized {
603 language: "rust".to_string(),
604 server_name: "test".to_string(),
605 capabilities: Default::default(),
606 })
607 .unwrap();
608
609 let messages1 = bridge.try_recv_all();
611 assert_eq!(messages1.len(), 1);
612
613 let messages2 = bridge.try_recv_all();
615 assert_eq!(messages2.len(), 0);
616 }
617
618 #[test]
619 fn test_async_bridge_ordering() {
620 let bridge = AsyncBridge::new();
621 let sender = bridge.sender();
622
623 sender
625 .send(AsyncMessage::LspInitialized {
626 language: "rust".to_string(),
627 server_name: "test".to_string(),
628 capabilities: Default::default(),
629 })
630 .unwrap();
631 sender
632 .send(AsyncMessage::LspInitialized {
633 language: "typescript".to_string(),
634 server_name: "test".to_string(),
635 capabilities: Default::default(),
636 })
637 .unwrap();
638 sender
639 .send(AsyncMessage::LspInitialized {
640 language: "python".to_string(),
641 server_name: "test".to_string(),
642 capabilities: Default::default(),
643 })
644 .unwrap();
645
646 let messages = bridge.try_recv_all();
648 assert_eq!(messages.len(), 3);
649
650 match (&messages[0], &messages[1], &messages[2]) {
651 (
652 AsyncMessage::LspInitialized { language: l1, .. },
653 AsyncMessage::LspInitialized { language: l2, .. },
654 AsyncMessage::LspInitialized { language: l3, .. },
655 ) => {
656 assert_eq!(l1, "rust");
657 assert_eq!(l2, "typescript");
658 assert_eq!(l3, "python");
659 }
660 _ => panic!("Expected ordered LspInitialized messages"),
661 }
662 }
663}