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 PluginsDirLoaded {
292 dir: std::path::PathBuf,
293 errors: Vec<String>,
294 discovered_plugins: std::collections::HashMap<String, fresh_core::config::PluginConfig>,
295 },
296
297 PluginDeclarationsReady { declarations: Vec<(String, String)> },
301
302 PluginInitScriptLoaded(PluginInitScriptOutcome),
307}
308
309#[derive(Debug, Clone)]
312pub enum PluginInitScriptOutcome {
313 NotFound,
314 Disabled,
315 CrashFused { failures: u32 },
316 Loaded,
317 Failed { message: String },
318}
319
320#[derive(Debug, Clone)]
322pub enum LspProgressValue {
323 Begin {
324 title: String,
325 message: Option<String>,
326 percentage: Option<u32>,
327 },
328 Report {
329 message: Option<String>,
330 percentage: Option<u32>,
331 },
332 End {
333 message: Option<String>,
334 },
335}
336
337#[derive(Debug, Clone, Copy, PartialEq, Eq)]
339pub enum LspMessageType {
340 Error = 1,
341 Warning = 2,
342 Info = 3,
343 Log = 4,
344}
345
346#[derive(Debug, Clone, Copy, PartialEq, Eq)]
348pub enum LspServerStatus {
349 Starting,
350 Initializing,
351 Running,
352 Error,
353 Shutdown,
354}
355
356#[derive(Clone)]
363pub struct AsyncBridge {
364 sender: mpsc::Sender<AsyncMessage>,
365 receiver: std::sync::Arc<std::sync::Mutex<mpsc::Receiver<AsyncMessage>>>,
367}
368
369impl AsyncBridge {
370 pub fn new() -> Self {
377 let (sender, receiver) = mpsc::channel();
378 Self {
379 sender,
380 receiver: std::sync::Arc::new(std::sync::Mutex::new(receiver)),
381 }
382 }
383
384 pub fn sender(&self) -> mpsc::Sender<AsyncMessage> {
391 self.sender.clone()
392 }
393
394 pub fn try_recv_all(&self) -> Vec<AsyncMessage> {
399 let mut messages = Vec::new();
400
401 if let Ok(receiver) = self.receiver.lock() {
403 while let Ok(msg) = receiver.try_recv() {
404 messages.push(msg);
405 }
406 }
407
408 messages
409 }
410
411 pub fn has_messages(&self) -> bool {
413 if let Ok(receiver) = self.receiver.lock() {
415 receiver.try_recv().is_ok()
416 } else {
417 false
418 }
419 }
420}
421
422impl Default for AsyncBridge {
423 fn default() -> Self {
424 Self::new()
425 }
426}
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431
432 #[test]
433 fn test_async_bridge_send_receive() {
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
446 let messages = bridge.try_recv_all();
448 assert_eq!(messages.len(), 1);
449
450 match &messages[0] {
451 AsyncMessage::LspInitialized {
452 language,
453 server_name,
454 ..
455 } => {
456 assert_eq!(language, "rust");
457 assert_eq!(server_name, "test");
458 }
459 _ => panic!("Wrong message type"),
460 }
461 }
462
463 #[test]
464 fn test_async_bridge_multiple_messages() {
465 let bridge = AsyncBridge::new();
466 let sender = bridge.sender();
467
468 sender
470 .send(AsyncMessage::LspInitialized {
471 language: "rust".to_string(),
472 server_name: "test".to_string(),
473 capabilities: Default::default(),
474 })
475 .unwrap();
476 sender
477 .send(AsyncMessage::LspInitialized {
478 language: "typescript".to_string(),
479 server_name: "test".to_string(),
480 capabilities: Default::default(),
481 })
482 .unwrap();
483
484 let messages = bridge.try_recv_all();
486 assert_eq!(messages.len(), 2);
487 }
488
489 #[test]
490 fn test_async_bridge_no_messages() {
491 let bridge = AsyncBridge::new();
492
493 let messages = bridge.try_recv_all();
495 assert_eq!(messages.len(), 0);
496 }
497
498 #[test]
499 fn test_async_bridge_clone_sender() {
500 let bridge = AsyncBridge::new();
501 let sender1 = bridge.sender();
502 let sender2 = sender1.clone();
503
504 sender1
506 .send(AsyncMessage::LspInitialized {
507 language: "rust".to_string(),
508 server_name: "test".to_string(),
509 capabilities: Default::default(),
510 })
511 .unwrap();
512 sender2
513 .send(AsyncMessage::LspInitialized {
514 language: "typescript".to_string(),
515 server_name: "test".to_string(),
516 capabilities: Default::default(),
517 })
518 .unwrap();
519
520 let messages = bridge.try_recv_all();
521 assert_eq!(messages.len(), 2);
522 }
523
524 #[test]
525 fn test_async_bridge_diagnostics() {
526 let bridge = AsyncBridge::new();
527 let sender = bridge.sender();
528
529 let diagnostics = vec![lsp_types::Diagnostic {
531 range: lsp_types::Range {
532 start: lsp_types::Position {
533 line: 0,
534 character: 0,
535 },
536 end: lsp_types::Position {
537 line: 0,
538 character: 5,
539 },
540 },
541 severity: Some(lsp_types::DiagnosticSeverity::ERROR),
542 code: None,
543 code_description: None,
544 source: Some("rust-analyzer".to_string()),
545 message: "test error".to_string(),
546 related_information: None,
547 tags: None,
548 data: None,
549 }];
550
551 sender
552 .send(AsyncMessage::LspDiagnostics {
553 uri: "file:///test.rs".to_string(),
554 diagnostics: diagnostics.clone(),
555 server_name: "rust-analyzer".to_string(),
556 })
557 .unwrap();
558
559 let messages = bridge.try_recv_all();
560 assert_eq!(messages.len(), 1);
561
562 match &messages[0] {
563 AsyncMessage::LspDiagnostics {
564 uri,
565 diagnostics: diags,
566 server_name,
567 } => {
568 assert_eq!(uri, "file:///test.rs");
569 assert_eq!(diags.len(), 1);
570 assert_eq!(diags[0].message, "test error");
571 assert_eq!(server_name, "rust-analyzer");
572 }
573 _ => panic!("Expected LspDiagnostics message"),
574 }
575 }
576
577 #[test]
578 fn test_async_bridge_error_message() {
579 let bridge = AsyncBridge::new();
580 let sender = bridge.sender();
581
582 sender
583 .send(AsyncMessage::LspError {
584 language: "rust".to_string(),
585 error: "Failed to initialize".to_string(),
586 stderr_log_path: None,
587 })
588 .unwrap();
589
590 let messages = bridge.try_recv_all();
591 assert_eq!(messages.len(), 1);
592
593 match &messages[0] {
594 AsyncMessage::LspError {
595 language,
596 error,
597 stderr_log_path,
598 } => {
599 assert_eq!(language, "rust");
600 assert_eq!(error, "Failed to initialize");
601 assert!(stderr_log_path.is_none());
602 }
603 _ => panic!("Expected LspError message"),
604 }
605 }
606
607 #[test]
608 fn test_async_bridge_clone_bridge() {
609 let bridge = AsyncBridge::new();
610 let bridge_clone = bridge.clone();
611 let sender = bridge.sender();
612
613 sender
615 .send(AsyncMessage::LspInitialized {
616 language: "rust".to_string(),
617 server_name: "test".to_string(),
618 capabilities: Default::default(),
619 })
620 .unwrap();
621
622 let messages = bridge_clone.try_recv_all();
624 assert_eq!(messages.len(), 1);
625 }
626
627 #[test]
628 fn test_async_bridge_multiple_calls_to_try_recv_all() {
629 let bridge = AsyncBridge::new();
630 let sender = bridge.sender();
631
632 sender
633 .send(AsyncMessage::LspInitialized {
634 language: "rust".to_string(),
635 server_name: "test".to_string(),
636 capabilities: Default::default(),
637 })
638 .unwrap();
639
640 let messages1 = bridge.try_recv_all();
642 assert_eq!(messages1.len(), 1);
643
644 let messages2 = bridge.try_recv_all();
646 assert_eq!(messages2.len(), 0);
647 }
648
649 #[test]
650 fn test_async_bridge_ordering() {
651 let bridge = AsyncBridge::new();
652 let sender = bridge.sender();
653
654 sender
656 .send(AsyncMessage::LspInitialized {
657 language: "rust".to_string(),
658 server_name: "test".to_string(),
659 capabilities: Default::default(),
660 })
661 .unwrap();
662 sender
663 .send(AsyncMessage::LspInitialized {
664 language: "typescript".to_string(),
665 server_name: "test".to_string(),
666 capabilities: Default::default(),
667 })
668 .unwrap();
669 sender
670 .send(AsyncMessage::LspInitialized {
671 language: "python".to_string(),
672 server_name: "test".to_string(),
673 capabilities: Default::default(),
674 })
675 .unwrap();
676
677 let messages = bridge.try_recv_all();
679 assert_eq!(messages.len(), 3);
680
681 match (&messages[0], &messages[1], &messages[2]) {
682 (
683 AsyncMessage::LspInitialized { language: l1, .. },
684 AsyncMessage::LspInitialized { language: l2, .. },
685 AsyncMessage::LspInitialized { language: l3, .. },
686 ) => {
687 assert_eq!(l1, "rust");
688 assert_eq!(l2, "typescript");
689 assert_eq!(l3, "python");
690 }
691 _ => panic!("Expected ordered LspInitialized messages"),
692 }
693 }
694}