1#[allow(unused_imports)]
32use std::{
33 collections::HashMap,
34 sync::Arc,
35 time::{SystemTime, UNIX_EPOCH},
36};
37
38use async_trait::async_trait;
39use CommonLibrary::{
40 Command::CommandExecutor::CommandExecutor,
41 LanguageFeature::{
42 DTO::{PositionDTO::PositionDTO, ProviderType::ProviderType},
43 LanguageFeatureProviderRegistry::LanguageFeatureProviderRegistry,
44 },
45 Secret::SecretProvider::SecretProvider,
46 Terminal::TerminalProvider::TerminalProvider,
47 UserInterface::{
48 DTO::{
49 InputBoxOptionsDTO::InputBoxOptionsDTO,
50 QuickPickItemDTO::QuickPickItemDTO,
51 QuickPickOptionsDTO::QuickPickOptionsDTO,
52 },
53 UserInterfaceProvider::UserInterfaceProvider,
54 },
55};
56use serde_json::json;
57use tokio::sync::RwLock;
58use tonic::{Request, Response, Status};
59use url::Url;
60
61use crate::{
62 ApplicationState::DTO::{
63 ProviderRegistrationDTO::ProviderRegistrationDTO,
64 WorkspaceFolderStateDTO::WorkspaceFolderStateDTO,
65 },
66 Environment::MountainEnvironment::MountainEnvironment,
67};
68use crate::dev_log;
70use crate::Vine::Generated::{
71 AppendOutputRequest,
74 ApplyEditRequest,
75 ApplyEditResponse,
76 Argument,
77 CancelOperationRequest,
78
79 ClearOutputRequest,
80 CloseTerminalRequest,
81 CodeAction,
82
83 CompletionItem,
84 CopyFileRequest,
85 CreateDirectoryRequest,
86 CreateOutputChannelRequest,
87 CreateOutputChannelResponse,
88 CreateStatusBarItemRequest,
89 CreateStatusBarItemResponse,
90 CreateWebviewPanelRequest,
91 CreateWebviewPanelResponse,
92 DebugConfiguration,
93 DeleteFileRequest,
94 DeleteSecretRequest,
95 DisposeOutputRequest,
96 DisposeWebviewPanelRequest,
97 Empty,
99 ExecuteCommandRequest,
100 ExecuteCommandResponse,
101 ExecuteTaskRequest,
102 ExecuteTaskResponse,
103 ExtensionInfo,
104 FindFilesRequest,
106 FindFilesResponse,
107 FindTextInFilesRequest,
108 FindTextInFilesResponse,
109 GenericNotification,
110 GenericRequest,
112 GenericResponse,
113 GetAllExtensionsResponse,
114 GetAuthenticationSessionRequest,
115 GetAuthenticationSessionResponse,
116 GetConfigurationRequest,
117 GetConfigurationResponse,
118 GetExtensionRequest,
119 GetExtensionResponse,
120 GetSecretRequest,
122 GetSecretResponse,
123 GetTreeChildrenRequest,
124 GetTreeChildrenResponse,
125 GitExecRequest,
126 GitExecResponse,
127
128 InitExtensionHostRequest,
130
131 Location,
132 OnDidReceiveMessageRequest,
133
134 OpenDocumentRequest,
135 OpenDocumentResponse,
136 OpenExternalRequest,
137 OpenTerminalRequest,
139 ParticipateInSaveRequest,
141 ParticipateInSaveResponse,
142 Position,
143 PostWebviewMessageRequest,
144 ProvideCallHierarchyRequest,
145 ProvideCallHierarchyResponse,
146 ProvideCodeActionsRequest,
147 ProvideCodeActionsResponse,
148 ProvideCodeLensesRequest,
149 ProvideCodeLensesResponse,
150 ProvideCompletionItemsRequest,
151 ProvideCompletionItemsResponse,
152 ProvideDefinitionRequest,
153 ProvideDefinitionResponse,
154 ProvideDocumentFormattingRequest,
155 ProvideDocumentFormattingResponse,
156 ProvideDocumentHighlightsRequest,
157 ProvideDocumentHighlightsResponse,
158 ProvideDocumentRangeFormattingRequest,
159 ProvideDocumentRangeFormattingResponse,
160 ProvideDocumentSymbolsRequest,
161 ProvideDocumentSymbolsResponse,
162 ProvideFoldingRangesRequest,
163 ProvideFoldingRangesResponse,
164 ProvideHoverRequest,
165 ProvideHoverResponse,
166 ProvideInlayHintsRequest,
167 ProvideInlayHintsResponse,
168 ProvideLinkedEditingRangesRequest,
169 ProvideLinkedEditingRangesResponse,
170 ProvideOnTypeFormattingRequest,
171 ProvideOnTypeFormattingResponse,
172 ProvideReferencesRequest,
173 ProvideReferencesResponse,
174 ProvideRenameEditsRequest,
175 ProvideRenameEditsResponse,
176 ProvideSelectionRangesRequest,
177 ProvideSelectionRangesResponse,
178 ProvideSemanticTokensRequest,
179 ProvideSemanticTokensResponse,
180 ProvideSignatureHelpRequest,
181 ProvideSignatureHelpResponse,
182 ProvideTypeHierarchyRequest,
183 ProvideTypeHierarchyResponse,
184 ProvideWorkspaceSymbolsRequest,
185 ProvideWorkspaceSymbolsResponse,
186 Range,
187 ReadFileRequest,
189 ReadFileResponse,
190 ReaddirRequest,
191 ReaddirResponse,
192 RegisterAuthenticationProviderRequest,
193 RegisterCommandRequest,
195 RegisterDebugAdapterRequest,
197 RegisterOnTypeFormattingProviderRequest,
198 RegisterProviderRequest,
200 RegisterScmProviderRequest,
202 RegisterSemanticTokensProviderRequest,
203 RegisterSignatureHelpProviderRequest,
204 RegisterTaskProviderRequest,
205 RegisterTreeViewProviderRequest,
207 RenameFileRequest,
208 ReportProgressRequest,
209 ResizeTerminalRequest,
210 RpcError,
211 SaveAllRequest,
212 SaveAllResponse,
213 SetStatusBarTextRequest,
214 SetWebviewHtmlRequest,
215 ShowInputBoxRequest,
216 ShowInputBoxResponse,
217 ShowMessageRequest,
218 ShowMessageResponse,
219 ShowOutputRequest,
220 ShowProgressRequest,
221 ShowProgressResponse,
222 ShowQuickPickRequest,
223 ShowQuickPickResponse,
224 ShowTextDocumentRequest,
226 ShowTextDocumentResponse,
227 SourceControlResourceState,
228 StartDebuggingRequest,
229 StartDebuggingResponse,
230
231 StatRequest,
232 StatResponse,
233 StopDebuggingRequest,
234 StoreSecretRequest,
235 TerminalClosedNotification,
236 TerminalDataNotification,
237
238 TerminalInputRequest,
239 TerminalOpenedNotification,
240 TerminalProcessIdNotification,
241 TerminateTaskRequest,
242 TextDocumentSaveReason,
243 TextEdit,
244 TextEditForSave,
245
246 TextMatch,
247 TreeItem,
248
249 UnregisterCommandRequest,
250
251 UpdateConfigurationRequest,
252 UpdateScmGroupRequest,
253 UpdateWorkspaceFoldersRequest,
254
255 Uri,
256 ViewColumn,
257
258 WatchFileRequest,
259
260 WorkspaceFolder,
261 WriteFileRequest,
262 cocoon_service_server::CocoonService,
263 on_did_receive_message_request,
264 post_webview_message_request,
265};
266
267#[derive(Clone)]
272pub struct CocoonServiceImpl {
273 environment:Arc<MountainEnvironment>,
275
276 ActiveOperations:Arc<RwLock<HashMap<u64, tokio_util::sync::CancellationToken>>>,
279}
280
281impl CocoonServiceImpl {
282 pub fn new(environment:Arc<MountainEnvironment>) -> Self {
290 dev_log!("cocoon", "[CocoonService] New instance created");
291
292 Self { environment, ActiveOperations:Arc::new(RwLock::new(HashMap::new())) }
293 }
294
295 pub async fn RegisterOperation(&self, request_id:u64) -> tokio_util::sync::CancellationToken {
303 let token = tokio_util::sync::CancellationToken::new();
304 self.ActiveOperations.write().await.insert(request_id, token.clone());
305 dev_log!("cocoon", "[CocoonService] Registered operation {} for cancellation", request_id);
306 token
307 }
308
309 pub async fn UnregisterOperation(&self, request_id:u64) {
314 self.ActiveOperations.write().await.remove(&request_id);
315 dev_log!("cocoon", "[CocoonService] Unregistered operation {}", request_id);
316 }
317
318 fn RegisterProvider(&self, handle:u32, provider_type:ProviderType, language_selector:&str, extension_id:&str) {
329 let dto = ProviderRegistrationDTO {
334 Handle:handle,
335 ProviderType:provider_type,
336 Selector:json!([{ "language": language_selector }]),
337 SideCarIdentifier:"cocoon-main".to_string(),
338 ExtensionIdentifier:json!(extension_id),
339 Options:None,
340 };
341 self.environment
342 .ApplicationState
343 .Extension
344 .ProviderRegistration
345 .RegisterProvider(handle, dto);
346 dev_log!(
347 "cocoon",
348 "[CocoonService] Provider {:?} registered: handle={}, language={}",
349 provider_type,
350 handle,
351 language_selector
352 );
353 }
354
355 fn UriToPath(uri_opt:Option<&Uri>) -> Option<std::path::PathBuf> {
360 let value = uri_opt?.value.as_str();
361 if value.is_empty() {
362 return None;
363 }
364 let path_str = if let Some(Stripped) = value.strip_prefix("file://") {
366 Stripped
367 } else if value.starts_with('/') || (value.len() > 1 && value.as_bytes()[1] == b':') {
368 value
370 } else {
371 value
373 };
374 Some(std::path::PathBuf::from(path_str))
375 }
376}
377
378#[async_trait]
379impl CocoonService for CocoonServiceImpl {
380 async fn process_mountain_request(
389 &self,
390 request:Request<GenericRequest>,
391 ) -> Result<Response<GenericResponse>, Status> {
392 let Req = request.into_inner();
393 let RequestId = Req.request_identifier;
394
395 dev_log!(
396 "cocoon",
397 "[CocoonService] generic request: method={} id={}",
398 Req.method,
399 RequestId
400 );
401
402 fn OkResponse(RequestId:u64, Value:&impl serde::Serialize) -> Response<GenericResponse> {
404 let Bytes = serde_json::to_vec(Value).unwrap_or_default();
405 Response::new(GenericResponse { request_identifier:RequestId, result:Bytes, error:None })
406 }
407
408 fn ErrResponse(RequestId:u64, Code:i32, Message:String) -> Response<GenericResponse> {
410 Response::new(GenericResponse {
411 request_identifier:RequestId,
412 result:Vec::new(),
413 error:Some(RpcError { code:Code, message:Message, data:Vec::new() }),
414 })
415 }
416
417 let Params:serde_json::Value = if Req.parameter.is_empty() {
419 serde_json::Value::Null
420 } else {
421 serde_json::from_slice(&Req.parameter).unwrap_or(serde_json::Value::Null)
422 };
423
424 match Req.method.as_str() {
425 "fs.readFile" | "file:read" => {
427 let Path = Params
428 .as_str()
429 .or_else(|| Params.get("path").and_then(|V| V.as_str()))
430 .unwrap_or("");
431 match tokio::fs::read(Path).await {
432 Ok(Content) => Ok(OkResponse(RequestId, &Content)),
433 Err(Error) => Ok(ErrResponse(RequestId, -32000, format!("fs.readFile: {}", Error))),
434 }
435 },
436 "fs.writeFile" | "file:write" => {
437 let Path = Params.get("path").and_then(|V| V.as_str()).unwrap_or("");
438 let Content:Vec<u8> = Params
439 .get("content")
440 .and_then(|V| V.as_array())
441 .map(|A| A.iter().filter_map(|B| B.as_u64().map(|N| N as u8)).collect())
442 .unwrap_or_default();
443 match tokio::fs::write(Path, &Content).await {
444 Ok(()) => Ok(OkResponse(RequestId, &serde_json::Value::Null)),
445 Err(Error) => Ok(ErrResponse(RequestId, -32000, format!("fs.writeFile: {}", Error))),
446 }
447 },
448 "fs.stat" | "file:stat" => {
449 let Path = Params
450 .as_str()
451 .or_else(|| Params.get("path").and_then(|V| V.as_str()))
452 .unwrap_or("");
453 match tokio::fs::metadata(Path).await {
454 Ok(Meta) => {
455 let Mtime = Meta
456 .modified()
457 .ok()
458 .and_then(|T| T.duration_since(UNIX_EPOCH).ok())
459 .map(|D| D.as_millis() as u64)
460 .unwrap_or(0);
461 Ok(OkResponse(
462 RequestId,
463 &json!({
464 "type": if Meta.is_dir() { 2 } else { 1 },
465 "is_file": Meta.is_file(),
466 "is_directory": Meta.is_dir(),
467 "size": Meta.len(),
468 "mtime": Mtime,
469 }),
470 ))
471 },
472 Err(Error) => Ok(ErrResponse(RequestId, -32000, format!("fs.stat: {}", Error))),
473 }
474 },
475 "fs.listDir" | "fs.readdir" | "file:readdir" => {
476 let Path = Params
477 .as_str()
478 .or_else(|| Params.get("path").and_then(|V| V.as_str()))
479 .unwrap_or("");
480 match tokio::fs::read_dir(Path).await {
481 Ok(mut Entries) => {
482 let mut Items:Vec<serde_json::Value> = Vec::new();
484 while let Ok(Some(Entry)) = Entries.next_entry().await {
485 if let Some(Name) = Entry.file_name().to_str() {
486 let IsDir = Entry.file_type().await.map(|T| T.is_dir()).unwrap_or(false);
487 Items.push(json!({ "name": Name, "type": if IsDir { 2u32 } else { 1u32 } }));
488 }
489 }
490 Ok(OkResponse(RequestId, &Items))
491 },
492 Err(Error) => Ok(ErrResponse(RequestId, -32000, format!("fs.listDir: {}", Error))),
493 }
494 },
495 "fs.createDir" | "file:mkdir" => {
496 let Path = Params
497 .as_str()
498 .or_else(|| Params.get("path").and_then(|V| V.as_str()))
499 .unwrap_or("");
500 match tokio::fs::create_dir_all(Path).await {
501 Ok(()) => Ok(OkResponse(RequestId, &serde_json::Value::Null)),
502 Err(Error) => Ok(ErrResponse(RequestId, -32000, format!("fs.createDir: {}", Error))),
503 }
504 },
505 "fs.delete" | "file:delete" => {
506 let Path = Params
507 .as_str()
508 .or_else(|| Params.get("path").and_then(|V| V.as_str()))
509 .unwrap_or("");
510 let Result = if std::path::Path::new(Path).is_dir() {
511 tokio::fs::remove_dir_all(Path).await
512 } else {
513 tokio::fs::remove_file(Path).await
514 };
515 match Result {
516 Ok(()) => Ok(OkResponse(RequestId, &serde_json::Value::Null)),
517 Err(Error) => Ok(ErrResponse(RequestId, -32000, format!("fs.delete: {}", Error))),
518 }
519 },
520 "fs.rename" | "file:move" => {
521 let From = Params.get("from").and_then(|V| V.as_str()).unwrap_or("");
522 let To = Params.get("to").and_then(|V| V.as_str()).unwrap_or("");
523 match tokio::fs::rename(From, To).await {
524 Ok(()) => Ok(OkResponse(RequestId, &serde_json::Value::Null)),
525 Err(Error) => Ok(ErrResponse(RequestId, -32000, format!("fs.rename: {}", Error))),
526 }
527 },
528 "commands.execute" => {
530 let CommandId = Params.get("id").and_then(|V| V.as_str()).unwrap_or("").to_string();
531 let Arg = Params.get("arg").cloned().unwrap_or(serde_json::Value::Null);
532 match self.environment.ExecuteCommand(CommandId, Arg).await {
533 Ok(Value) => Ok(OkResponse(RequestId, &Value)),
534 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
535 }
536 },
537 "executeCommand" => {
539 let CommandId = Params.get("commandId").and_then(|V| V.as_str()).unwrap_or("").to_string();
540 let Arg = Params
541 .get("arguments")
542 .and_then(|A| A.as_array())
543 .and_then(|A| A.first())
544 .cloned()
545 .unwrap_or(serde_json::Value::Null);
546 match self.environment.ExecuteCommand(CommandId, Arg).await {
547 Ok(Value) => Ok(OkResponse(RequestId, &json!({ "result": Value }))),
548 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
549 }
550 },
551 "unregisterCommand" => {
552 let ExtensionId = Params.get("extensionId").and_then(|V| V.as_str()).unwrap_or("").to_string();
553 let CommandId = Params.get("commandId").and_then(|V| V.as_str()).unwrap_or("").to_string();
554 match self.environment.UnregisterCommand(ExtensionId, CommandId).await {
555 Ok(()) => Ok(OkResponse(RequestId, &json!({ "success": true }))),
556 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
557 }
558 },
559 "UserInterface.ShowOpenDialog" => {
561 use CommonLibrary::UserInterface::{
562 DTO::OpenDialogOptionsDTO::OpenDialogOptionsDTO,
563 UserInterfaceProvider::UserInterfaceProvider,
564 };
565 let Title = Params
566 .get(0)
567 .and_then(|V| V.get("title"))
568 .and_then(|T| T.as_str())
569 .map(|S| S.to_string());
570 let Options = OpenDialogOptionsDTO {
571 Base:CommonLibrary::UserInterface::DTO::DialogOptionsDTO::DialogOptionsDTO {
572 Title,
573 ..Default::default()
574 },
575 ..OpenDialogOptionsDTO::default()
576 };
577 match self.environment.ShowOpenDialog(Some(Options)).await {
578 Ok(Some(Paths)) => {
579 let Uris:Vec<String> = Paths.iter().map(|P| format!("file://{}", P.display())).collect();
580 Ok(OkResponse(RequestId, &json!(Uris)))
581 },
582 Ok(None) => Ok(OkResponse(RequestId, &json!(serde_json::Value::Array(vec![])))),
583 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
584 }
585 },
586 "UserInterface.ShowSaveDialog" => {
587 use CommonLibrary::UserInterface::{
588 DTO::SaveDialogOptionsDTO::SaveDialogOptionsDTO,
589 UserInterfaceProvider::UserInterfaceProvider,
590 };
591 let Title = Params
592 .get(0)
593 .and_then(|V| V.get("title"))
594 .and_then(|T| T.as_str())
595 .map(|S| S.to_string());
596 let Options = SaveDialogOptionsDTO {
597 Base:CommonLibrary::UserInterface::DTO::DialogOptionsDTO::DialogOptionsDTO {
598 Title,
599 ..Default::default()
600 },
601 ..SaveDialogOptionsDTO::default()
602 };
603 match self.environment.ShowSaveDialog(Some(Options)).await {
604 Ok(Some(Path)) => Ok(OkResponse(RequestId, &json!(format!("file://{}", Path.display())))),
605 Ok(None) => Ok(OkResponse(RequestId, &serde_json::Value::Null)),
606 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
607 }
608 },
609 "UserInterface.ShowInputBox" => {
610 use CommonLibrary::UserInterface::{
611 DTO::InputBoxOptionsDTO::InputBoxOptionsDTO,
612 UserInterfaceProvider::UserInterfaceProvider,
613 };
614 let Opts = Params.get(0);
615 let Options = InputBoxOptionsDTO {
616 Prompt:Opts
617 .and_then(|V| V.get("prompt"))
618 .and_then(|P| P.as_str())
619 .map(|S| S.to_string()),
620 PlaceHolder:Opts
621 .and_then(|V| V.get("placeHolder"))
622 .and_then(|P| P.as_str())
623 .map(|S| S.to_string()),
624 IsPassword:Some(Opts.and_then(|V| V.get("password")).and_then(|B| B.as_bool()).unwrap_or(false)),
625 Value:Opts
626 .and_then(|V| V.get("value"))
627 .and_then(|V| V.as_str())
628 .map(|S| S.to_string()),
629 Title:None,
630 IgnoreFocusOut:None,
631 };
632 match self.environment.ShowInputBox(Some(Options)).await {
633 Ok(Some(Text)) => Ok(OkResponse(RequestId, &json!(Text))),
634 Ok(None) => Ok(OkResponse(RequestId, &serde_json::Value::Null)),
635 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
636 }
637 },
638 "openExternal" => {
640 use tauri::Emitter;
641 let Url = Params
642 .as_str()
643 .or_else(|| Params.get("url").and_then(|V| V.as_str()))
644 .unwrap_or("")
645 .to_string();
646 let _ = self
648 .environment
649 .ApplicationHandle
650 .emit("sky://native/openExternal", json!({ "url": Url }));
651 Ok(OkResponse(RequestId, &json!({ "success": true })))
652 },
653 "showTextDocument" => {
655 use tauri::Emitter;
656 let Uri = Params
657 .get("uri")
658 .and_then(|V| V.get("value").or(Some(V)))
659 .and_then(|V| V.as_str())
660 .unwrap_or("")
661 .to_string();
662 let ViewColumn = Params.get("viewColumn").and_then(|V| V.as_i64()).map(|N| N + 2);
663 let PreserveFocus = Params.get("preserveFocus").and_then(|V| V.as_bool()).unwrap_or(false);
664 let _ = self.environment.ApplicationHandle.emit(
665 "sky://editor/openDocument",
666 json!({ "uri": Uri, "viewColumn": ViewColumn, "preserveFocus": PreserveFocus }),
667 );
668 Ok(OkResponse(RequestId, &json!({ "success": true })))
669 },
670 "showInformation" => {
671 use CommonLibrary::UserInterface::{
672 DTO::MessageSeverity::MessageSeverity,
673 UserInterfaceProvider::UserInterfaceProvider,
674 };
675 let Message = Params.get("message").and_then(|V| V.as_str()).unwrap_or("").to_string();
676 let Items:Option<serde_json::Value> = Params
677 .get("items")
678 .cloned()
679 .filter(|V| V.is_array() && !V.as_array().unwrap().is_empty());
680 match self.environment.ShowMessage(MessageSeverity::Info, Message, Items).await {
681 Ok(Some(Selected)) => Ok(OkResponse(RequestId, &json!({ "selectedItem": Selected }))),
682 Ok(None) => Ok(OkResponse(RequestId, &serde_json::Value::Null)),
683 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
684 }
685 },
686 "showWarning" => {
687 use CommonLibrary::UserInterface::{
688 DTO::MessageSeverity::MessageSeverity,
689 UserInterfaceProvider::UserInterfaceProvider,
690 };
691 let Message = Params.get("message").and_then(|V| V.as_str()).unwrap_or("").to_string();
692 let Items:Option<serde_json::Value> = Params
693 .get("items")
694 .cloned()
695 .filter(|V| V.is_array() && !V.as_array().unwrap().is_empty());
696 match self.environment.ShowMessage(MessageSeverity::Warning, Message, Items).await {
697 Ok(Some(Selected)) => Ok(OkResponse(RequestId, &json!({ "selectedItem": Selected }))),
698 Ok(None) => Ok(OkResponse(RequestId, &serde_json::Value::Null)),
699 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
700 }
701 },
702 "showError" => {
703 use CommonLibrary::UserInterface::{
704 DTO::MessageSeverity::MessageSeverity,
705 UserInterfaceProvider::UserInterfaceProvider,
706 };
707 let Message = Params.get("message").and_then(|V| V.as_str()).unwrap_or("").to_string();
708 let Items:Option<serde_json::Value> = Params
709 .get("items")
710 .cloned()
711 .filter(|V| V.is_array() && !V.as_array().unwrap().is_empty());
712 match self.environment.ShowMessage(MessageSeverity::Error, Message, Items).await {
713 Ok(Some(Selected)) => Ok(OkResponse(RequestId, &json!({ "selectedItem": Selected }))),
714 Ok(None) => Ok(OkResponse(RequestId, &serde_json::Value::Null)),
715 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
716 }
717 },
718 "createStatusBarItem" => {
719 use tauri::Emitter;
720 let Id = Params.get("id").and_then(|V| V.as_str()).unwrap_or("").to_string();
721 let Text = Params.get("text").and_then(|V| V.as_str()).unwrap_or("").to_string();
722 let Tooltip = Params.get("tooltip").and_then(|V| V.as_str()).unwrap_or("").to_string();
723 let _ = self
724 .environment
725 .ApplicationHandle
726 .emit("sky://statusbar/create", json!({ "id": Id, "text": Text, "tooltip": Tooltip }));
727 Ok(OkResponse(RequestId, &json!({ "itemId": Id })))
728 },
729 "setStatusBarText" => {
730 use tauri::Emitter;
731 let ItemId = Params.get("itemId").and_then(|V| V.as_str()).unwrap_or("").to_string();
732 let Text = Params.get("text").and_then(|V| V.as_str()).unwrap_or("").to_string();
733 let _ = self
734 .environment
735 .ApplicationHandle
736 .emit("sky://statusbar/update", json!({ "id": ItemId, "text": Text }));
737 Ok(OkResponse(RequestId, &json!({ "success": true })))
738 },
739 "createWebviewPanel" => {
740 use tauri::Emitter;
741 let ViewType = Params.get("viewType").and_then(|V| V.as_str()).unwrap_or("").to_string();
742 let Title = Params.get("title").and_then(|V| V.as_str()).unwrap_or("").to_string();
743 let Handle = std::time::SystemTime::now()
744 .duration_since(std::time::UNIX_EPOCH)
745 .map(|D| D.as_millis() as u64)
746 .unwrap_or(0);
747 let _ = self.environment.ApplicationHandle.emit("sky://webview/create", json!({ "handle": Handle, "viewType": ViewType, "title": Title, "viewColumn": Params.get("viewColumn"), "preserveFocus": Params.get("preserveFocus").and_then(|V| V.as_bool()).unwrap_or(false) }));
748 Ok(OkResponse(RequestId, &json!({ "handle": Handle })))
749 },
750 "setWebviewHtml" => {
751 use tauri::Emitter;
752 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0);
753 let Html = Params.get("html").and_then(|V| V.as_str()).unwrap_or("").to_string();
754 let _ = self
755 .environment
756 .ApplicationHandle
757 .emit("sky://webview/setHtml", json!({ "handle": Handle, "html": Html }));
758 Ok(OkResponse(RequestId, &json!({ "success": true })))
759 },
760 "findFiles" => {
762 use std::path::PathBuf;
763
764 use globset::GlobBuilder;
765 let Pattern = Params.get("pattern").and_then(|V| V.as_str()).unwrap_or("**").to_string();
766 let WorkspaceFolders = self.environment.ApplicationState.Workspace.GetWorkspaceFolders();
767 if WorkspaceFolders.is_empty() {
768 return Ok(OkResponse(RequestId, &json!({ "uris": Vec::<String>::new() })));
769 }
770 let RootPath = PathBuf::from(&WorkspaceFolders[0].URI.to_string().replace("file://", ""));
771 let Matcher = match GlobBuilder::new(&Pattern).literal_separator(false).build() {
772 Ok(G) => G.compile_matcher(),
773 Err(E) => return Ok(ErrResponse(RequestId, -32000, format!("Invalid glob: {}", E))),
774 };
775 let mut Files:Vec<String> = Vec::new();
776 let mut Stack = vec![RootPath.clone()];
777 'find_outer: while let Some(Dir) = Stack.pop() {
778 let mut Entries = match tokio::fs::read_dir(&Dir).await {
779 Ok(E) => E,
780 Err(_) => continue,
781 };
782 while let Ok(Some(Entry)) = Entries.next_entry().await {
783 let Path = Entry.path();
784 if Path.file_name().map(|N| N.to_string_lossy().starts_with('.')).unwrap_or(false) {
785 continue;
786 }
787 if Path.is_dir() {
788 Stack.push(Path);
789 continue;
790 }
791 let Rel = Path.strip_prefix(&RootPath).unwrap_or(&Path).to_string_lossy().to_string();
792 if Matcher.is_match(&Rel) {
793 Files.push(format!("file://{}", Path.to_string_lossy()));
794 if Files.len() >= 500 {
795 break 'find_outer;
796 }
797 }
798 }
799 }
800 Ok(OkResponse(RequestId, &json!({ "uris": Files })))
801 },
802 "findTextInFiles" => {
803 use std::path::PathBuf;
804
805 use globset::GlobBuilder;
806 let Pattern = Params.get("pattern").and_then(|V| V.as_str()).unwrap_or("").to_string();
807 let IncludeStr = Params
808 .get("include")
809 .and_then(|V| V.as_array())
810 .and_then(|A| A.first())
811 .and_then(|V| V.as_str())
812 .map(|S| S.to_string())
813 .unwrap_or_else(|| "**".to_string());
814 let WorkspaceFolders = self.environment.ApplicationState.Workspace.GetWorkspaceFolders();
815 if WorkspaceFolders.is_empty() {
816 return Ok(OkResponse(RequestId, &json!({ "matches": Vec::<serde_json::Value>::new() })));
817 }
818 let RootPath = PathBuf::from(&WorkspaceFolders[0].URI.to_string().replace("file://", ""));
819 let Matcher = GlobBuilder::new(&IncludeStr)
820 .literal_separator(false)
821 .build()
822 .map(|G| G.compile_matcher())
823 .ok();
824 let PatternLower = Pattern.to_lowercase();
825 let mut Matches:Vec<serde_json::Value> = Vec::new();
826 let mut Stack = vec![RootPath.clone()];
827 'text_outer: while let Some(Dir) = Stack.pop() {
828 let mut Entries = match tokio::fs::read_dir(&Dir).await {
829 Ok(E) => E,
830 Err(_) => continue,
831 };
832 while let Ok(Some(Entry)) = Entries.next_entry().await {
833 let Path = Entry.path();
834 if Path.file_name().map(|N| N.to_string_lossy().starts_with('.')).unwrap_or(false) {
835 continue;
836 }
837 if Path.is_dir() {
838 Stack.push(Path);
839 continue;
840 }
841 let Rel = Path.strip_prefix(&RootPath).unwrap_or(&Path).to_string_lossy().to_string();
842 if let Some(Ref) = &Matcher {
843 if !Ref.is_match(&Rel) {
844 continue;
845 }
846 }
847 let Content = match tokio::fs::read_to_string(&Path).await {
848 Ok(C) => C,
849 Err(_) => continue,
850 };
851 for (LineIdx, Line) in Content.lines().enumerate() {
852 if Line.to_lowercase().contains(&PatternLower) {
853 Matches.push(json!({ "uri": format!("file://{}", Path.to_string_lossy()), "lineNumber": LineIdx + 1, "preview": Line.trim() }));
854 if Matches.len() >= 1000 {
855 break 'text_outer;
856 }
857 }
858 }
859 }
860 }
861 Ok(OkResponse(RequestId, &json!({ "matches": Matches })))
862 },
863 "openDocument" => {
864 use tauri::Emitter;
865 let Uri = Params
866 .get("uri")
867 .and_then(|V| V.get("value").or(Some(V)))
868 .and_then(|V| V.as_str())
869 .unwrap_or("")
870 .to_string();
871 let ViewColumn = Params.get("viewColumn").and_then(|V| V.as_i64());
872 let _ = self
873 .environment
874 .ApplicationHandle
875 .emit("sky://editor/openDocument", json!({ "uri": Uri, "viewColumn": ViewColumn }));
876 Ok(OkResponse(RequestId, &json!({ "success": true })))
877 },
878 "saveAll" => {
879 use tauri::Emitter;
880 let IncludeUntitled = Params.get("includeUntitled").and_then(|V| V.as_bool()).unwrap_or(false);
881 let _ = self
882 .environment
883 .ApplicationHandle
884 .emit("sky://editor/saveAll", json!({ "includeUntitled": IncludeUntitled }));
885 Ok(OkResponse(RequestId, &json!({ "success": true })))
886 },
887 "applyEdit" => {
888 use tauri::Emitter;
889 let Uri = Params
890 .get("uri")
891 .and_then(|V| V.get("value").or(Some(V)))
892 .and_then(|V| V.as_str())
893 .unwrap_or("")
894 .to_string();
895 let Edits = Params.get("edits").cloned().unwrap_or(json!([]));
896 let _ = self
897 .environment
898 .ApplicationHandle
899 .emit("sky://editor/applyEdits", json!({ "uri": Uri, "edits": Edits }));
900 Ok(OkResponse(RequestId, &json!({ "success": true })))
901 },
902 "getSecret" => {
904 use CommonLibrary::Secret::SecretProvider::SecretProvider;
905 let ExtensionId = Params.get("extensionId").and_then(|V| V.as_str()).unwrap_or("").to_string();
906 let Key = Params.get("key").and_then(|V| V.as_str()).unwrap_or("").to_string();
907 match self.environment.GetSecret(ExtensionId, Key).await {
908 Ok(Some(Value)) => Ok(OkResponse(RequestId, &json!({ "value": Value }))),
909 Ok(None) => Ok(OkResponse(RequestId, &serde_json::Value::Null)),
910 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
911 }
912 },
913 "storeSecret" => {
914 use CommonLibrary::Secret::SecretProvider::SecretProvider;
915 let ExtensionId = Params.get("extensionId").and_then(|V| V.as_str()).unwrap_or("").to_string();
916 let Key = Params.get("key").and_then(|V| V.as_str()).unwrap_or("").to_string();
917 let Value = Params.get("value").and_then(|V| V.as_str()).unwrap_or("").to_string();
918 match self.environment.StoreSecret(ExtensionId, Key, Value).await {
919 Ok(()) => Ok(OkResponse(RequestId, &json!({ "success": true }))),
920 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
921 }
922 },
923 "deleteSecret" => {
924 use CommonLibrary::Secret::SecretProvider::SecretProvider;
925 let ExtensionId = Params.get("extensionId").and_then(|V| V.as_str()).unwrap_or("").to_string();
926 let Key = Params.get("key").and_then(|V| V.as_str()).unwrap_or("").to_string();
927 match self.environment.DeleteSecret(ExtensionId, Key).await {
928 Ok(()) => Ok(OkResponse(RequestId, &json!({ "success": true }))),
929 Err(Error) => Ok(ErrResponse(RequestId, -32000, Error.to_string())),
930 }
931 },
932 "readFile" => {
934 let Uri = Params
935 .get("uri")
936 .and_then(|V| V.as_str())
937 .or_else(|| Params.as_str())
938 .unwrap_or("")
939 .replace("file://", "");
940 match tokio::fs::read(&Uri).await {
941 Ok(Content) => Ok(OkResponse(RequestId, &Content)),
942 Err(Error) => Ok(ErrResponse(RequestId, -32000, format!("readFile: {}", Error))),
943 }
944 },
945 "writeFile" => {
946 let Uri = Params.get("uri").and_then(|V| V.as_str()).unwrap_or("").replace("file://", "");
947 let Content:Vec<u8> = Params
948 .get("content")
949 .and_then(|V| V.as_array())
950 .map(|A| A.iter().filter_map(|B| B.as_u64().map(|N| N as u8)).collect())
951 .unwrap_or_default();
952 match tokio::fs::write(&Uri, &Content).await {
953 Ok(()) => Ok(OkResponse(RequestId, &serde_json::Value::Null)),
954 Err(Error) => Ok(ErrResponse(RequestId, -32000, format!("writeFile: {}", Error))),
955 }
956 },
957 "stat" => {
958 let Uri = Params
959 .get("uri")
960 .and_then(|V| V.as_str())
961 .or_else(|| Params.as_str())
962 .unwrap_or("")
963 .replace("file://", "");
964 match tokio::fs::metadata(&Uri).await {
965 Ok(Meta) => {
966 let Mtime = Meta
967 .modified()
968 .ok()
969 .and_then(|T| T.duration_since(UNIX_EPOCH).ok())
970 .map(|D| D.as_millis() as u64)
971 .unwrap_or(0);
972 Ok(OkResponse(
973 RequestId,
974 &json!({ "type": if Meta.is_dir() { 2 } else { 1 }, "is_file": Meta.is_file(), "is_directory": Meta.is_dir(), "size": Meta.len(), "mtime": Mtime }),
975 ))
976 },
977 Err(Error) => Ok(ErrResponse(RequestId, -32000, format!("stat: {}", Error))),
978 }
979 },
980 "readdir" => {
981 let Uri = Params
982 .get("uri")
983 .and_then(|V| V.as_str())
984 .or_else(|| Params.as_str())
985 .unwrap_or("")
986 .replace("file://", "");
987 match tokio::fs::read_dir(&Uri).await {
988 Ok(mut Entries) => {
989 let mut Names:Vec<String> = Vec::new();
990 while let Ok(Some(Entry)) = Entries.next_entry().await {
991 if let Some(Name) = Entry.file_name().to_str() {
992 Names.push(Name.to_string());
993 }
994 }
995 Ok(OkResponse(RequestId, &Names))
996 },
997 Err(Error) => Ok(ErrResponse(RequestId, -32000, format!("readdir: {}", Error))),
998 }
999 },
1000 _ => {
1002 dev_log!("cocoon", "warn: [CocoonService] Unknown generic method: {}", Req.method);
1003 Ok(ErrResponse(RequestId, -32601, format!("Method '{}' not found", Req.method)))
1004 },
1005 }
1006 }
1007
1008 async fn send_mountain_notification(
1013 &self,
1014 request:Request<GenericNotification>,
1015 ) -> Result<Response<Empty>, Status> {
1016 let notification = request.into_inner();
1017 dev_log!(
1018 "cocoon",
1019 "[CocoonService] Notification router: method='{}'",
1020 notification.method
1021 );
1022
1023 let Params:serde_json::Value = if notification.parameter.is_empty() {
1025 serde_json::Value::Null
1026 } else {
1027 serde_json::from_slice(¬ification.parameter).unwrap_or(serde_json::Value::Null)
1028 };
1029
1030 match notification.method.as_str() {
1031 "registerCommand" => {
1033 let CommandId = Params.get("commandId").and_then(|V| V.as_str()).unwrap_or("").to_string();
1034 let ExtensionId = Params.get("extensionId").and_then(|V| V.as_str()).unwrap_or("").to_string();
1035 if let Err(Error) = self.environment.RegisterCommand(ExtensionId, CommandId.clone()).await {
1036 dev_log!(
1037 "cocoon",
1038 "warn: [CocoonService] notification: registerCommand '{}' failed: {:?}",
1039 CommandId,
1040 Error
1041 );
1042 }
1043 },
1044 "unregisterCommand" => {
1045 let ExtensionId = Params.get("extensionId").and_then(|V| V.as_str()).unwrap_or("").to_string();
1046 let CommandId = Params.get("commandId").and_then(|V| V.as_str()).unwrap_or("").to_string();
1047 let _ = self.environment.UnregisterCommand(ExtensionId, CommandId).await;
1048 },
1049 "register_hover_provider" => {
1051 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1052 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1053 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1054 self.RegisterProvider(Handle, ProviderType::Hover, Selector, ExtId);
1055 },
1056 "register_completion_item_provider" => {
1057 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1058 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1059 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1060 self.RegisterProvider(Handle, ProviderType::Completion, Selector, ExtId);
1061 },
1062 "register_definition_provider" => {
1063 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1064 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1065 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1066 self.RegisterProvider(Handle, ProviderType::Definition, Selector, ExtId);
1067 },
1068 "register_reference_provider" => {
1069 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1070 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1071 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1072 self.RegisterProvider(Handle, ProviderType::References, Selector, ExtId);
1073 },
1074 "register_code_actions_provider" => {
1075 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1076 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1077 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1078 self.RegisterProvider(Handle, ProviderType::CodeAction, Selector, ExtId);
1079 },
1080 "register_document_highlight_provider" => {
1081 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1082 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1083 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1084 self.RegisterProvider(Handle, ProviderType::DocumentHighlight, Selector, ExtId);
1085 },
1086 "register_document_symbol_provider" => {
1087 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1088 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1089 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1090 self.RegisterProvider(Handle, ProviderType::DocumentSymbol, Selector, ExtId);
1091 },
1092 "register_workspace_symbol_provider" => {
1093 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1094 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1095 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1096 self.RegisterProvider(Handle, ProviderType::WorkspaceSymbol, Selector, ExtId);
1097 },
1098 "register_rename_provider" => {
1099 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1100 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1101 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1102 self.RegisterProvider(Handle, ProviderType::Rename, Selector, ExtId);
1103 },
1104 "register_document_formatting_provider" => {
1105 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1106 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1107 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1108 self.RegisterProvider(Handle, ProviderType::DocumentFormatting, Selector, ExtId);
1109 },
1110 "register_document_range_formatting_provider" => {
1111 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1112 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1113 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1114 self.RegisterProvider(Handle, ProviderType::DocumentRangeFormatting, Selector, ExtId);
1115 },
1116 "register_on_type_formatting_provider" => {
1117 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1118 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1119 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1120 self.RegisterProvider(Handle, ProviderType::OnTypeFormatting, Selector, ExtId);
1121 },
1122 "register_signature_help_provider" => {
1123 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1124 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1125 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1126 self.RegisterProvider(Handle, ProviderType::SignatureHelp, Selector, ExtId);
1127 },
1128 "register_code_lens_provider" => {
1129 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1130 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1131 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1132 self.RegisterProvider(Handle, ProviderType::CodeLens, Selector, ExtId);
1133 },
1134 "register_folding_range_provider" => {
1135 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1136 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1137 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1138 self.RegisterProvider(Handle, ProviderType::FoldingRange, Selector, ExtId);
1139 },
1140 "register_selection_range_provider" => {
1141 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1142 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1143 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1144 self.RegisterProvider(Handle, ProviderType::SelectionRange, Selector, ExtId);
1145 },
1146 "register_semantic_tokens_provider" => {
1147 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1148 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1149 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1150 self.RegisterProvider(Handle, ProviderType::SemanticTokens, Selector, ExtId);
1151 },
1152 "register_inlay_hints_provider" => {
1153 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1154 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1155 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1156 self.RegisterProvider(Handle, ProviderType::InlayHint, Selector, ExtId);
1157 },
1158 "register_type_hierarchy_provider" => {
1159 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1160 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1161 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1162 self.RegisterProvider(Handle, ProviderType::TypeHierarchy, Selector, ExtId);
1163 },
1164 "register_call_hierarchy_provider" => {
1165 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1166 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1167 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1168 self.RegisterProvider(Handle, ProviderType::CallHierarchy, Selector, ExtId);
1169 },
1170 "register_linked_editing_range_provider" => {
1171 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1172 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1173 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1174 self.RegisterProvider(Handle, ProviderType::LinkedEditingRange, Selector, ExtId);
1175 },
1176 "register_document_link_provider" => {
1177 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1178 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1179 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1180 self.RegisterProvider(Handle, ProviderType::DocumentLink, Selector, ExtId);
1181 },
1182 "register_color_provider" => {
1183 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1184 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1185 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1186 self.RegisterProvider(Handle, ProviderType::Color, Selector, ExtId);
1187 },
1188 "register_implementation_provider" => {
1189 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1190 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1191 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1192 self.RegisterProvider(Handle, ProviderType::Implementation, Selector, ExtId);
1193 },
1194 "register_type_definition_provider" => {
1195 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1196 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1197 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1198 self.RegisterProvider(Handle, ProviderType::TypeDefinition, Selector, ExtId);
1199 },
1200 "register_declaration_provider" => {
1201 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1202 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1203 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1204 self.RegisterProvider(Handle, ProviderType::Declaration, Selector, ExtId);
1205 },
1206 "register_evaluatable_expression_provider" => {
1207 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1208 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1209 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1210 self.RegisterProvider(Handle, ProviderType::EvaluatableExpression, Selector, ExtId);
1211 },
1212 "register_inline_values_provider" => {
1213 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0) as u32;
1214 let Selector = Params.get("language_selector").and_then(|V| V.as_str()).unwrap_or("*");
1215 let ExtId = Params.get("extension_id").and_then(|V| V.as_str()).unwrap_or("");
1216 self.RegisterProvider(Handle, ProviderType::InlineValues, Selector, ExtId);
1217 },
1218 "onDidReceiveMessage" => {
1220 use tauri::Emitter;
1221 let Handle = Params.get("handle").and_then(|V| V.as_u64()).unwrap_or(0);
1222 let Message = Params
1223 .get("stringMessage")
1224 .and_then(|V| V.as_str())
1225 .map(|S| S.to_string())
1226 .or_else(|| Params.get("bytesMessage").map(|_| "[binary]".to_string()))
1227 .unwrap_or_default();
1228 let _ = self
1229 .environment
1230 .ApplicationHandle
1231 .emit("sky://webview/message", json!({ "handle": Handle, "message": Message }));
1232 },
1233 "storeSecret" => {
1235 use CommonLibrary::Secret::SecretProvider::SecretProvider;
1236 let ExtensionId = Params.get("extensionId").and_then(|V| V.as_str()).unwrap_or("").to_string();
1237 let Key = Params.get("key").and_then(|V| V.as_str()).unwrap_or("").to_string();
1238 let Value = Params.get("value").and_then(|V| V.as_str()).unwrap_or("").to_string();
1239 let _ = self.environment.StoreSecret(ExtensionId, Key, Value).await;
1240 },
1241 "deleteSecret" => {
1242 use CommonLibrary::Secret::SecretProvider::SecretProvider;
1243 let ExtensionId = Params.get("extensionId").and_then(|V| V.as_str()).unwrap_or("").to_string();
1244 let Key = Params.get("key").and_then(|V| V.as_str()).unwrap_or("").to_string();
1245 let _ = self.environment.DeleteSecret(ExtensionId, Key).await;
1246 },
1247 "writeFile" => {
1249 let Uri = Params
1250 .get("uri")
1251 .and_then(|V| V.get("value").or(Some(V)))
1252 .and_then(|V| V.as_str())
1253 .unwrap_or("")
1254 .replace("file://", "");
1255 let Content:Vec<u8> = Params
1256 .get("content")
1257 .and_then(|V| V.as_array())
1258 .map(|A| A.iter().filter_map(|B| B.as_u64().map(|N| N as u8)).collect())
1259 .unwrap_or_default();
1260 let _ = tokio::fs::write(&Uri, &Content).await;
1261 },
1262 "webview.postMessage" => {
1264 use tauri::Emitter;
1265 let PanelId = Params.get("panelId").and_then(|V| V.as_str()).unwrap_or("").to_string();
1266 let Method = Params.get("method").and_then(|V| V.as_str()).unwrap_or("").to_string();
1267 let MsgParams = Params.get("params").cloned().unwrap_or(serde_json::Value::Null);
1268 let _ = self.environment.ApplicationHandle.emit(
1269 "sky://webview/message",
1270 json!({ "panelId": PanelId, "method": Method, "params": MsgParams }),
1271 );
1272 },
1273 "webview.dispose" => {
1274 use tauri::Emitter;
1275 let PanelId = Params.get("panelId").and_then(|V| V.as_str()).unwrap_or("").to_string();
1276 let _ = self
1277 .environment
1278 .ApplicationHandle
1279 .emit("sky://webview/dispose", json!({ "panelId": PanelId }));
1280 },
1281 "progress.start" => {
1283 use tauri::Emitter;
1284 let Id = Params.get("id").and_then(|V| V.as_str()).unwrap_or("").to_string();
1285 let Title = Params.get("title").and_then(|V| V.as_str()).map(|S| S.to_string());
1286 let Location = Params.get("location").cloned();
1287 let Cancellable = Params.get("cancellable").and_then(|V| V.as_bool()).unwrap_or(false);
1288 let _ = self.environment.ApplicationHandle.emit(
1289 "sky://progress/start",
1290 json!({ "id": Id, "title": Title, "location": Location, "cancellable": Cancellable }),
1291 );
1292 },
1293 "progress.update" => {
1294 use tauri::Emitter;
1295 let Id = Params.get("id").and_then(|V| V.as_str()).unwrap_or("").to_string();
1296 let Message = Params.get("message").and_then(|V| V.as_str()).map(|S| S.to_string());
1297 let Increment = Params.get("increment").and_then(|V| V.as_f64());
1298 let _ = self.environment.ApplicationHandle.emit(
1299 "sky://progress/update",
1300 json!({ "id": Id, "message": Message, "increment": Increment }),
1301 );
1302 },
1303 "progress.complete" => {
1304 use tauri::Emitter;
1305 let Id = Params.get("id").and_then(|V| V.as_str()).unwrap_or("").to_string();
1306 let _ = self
1307 .environment
1308 .ApplicationHandle
1309 .emit("sky://progress/complete", json!({ "id": Id }));
1310 },
1311 "openExternal" => {
1313 use tauri::Emitter;
1314 let Url = Params.get("url").and_then(|V| V.as_str()).unwrap_or("").to_string();
1315 let _ = self
1316 .environment
1317 .ApplicationHandle
1318 .emit("sky://native/openExternal", json!({ "url": Url }));
1319 },
1320 "setStatusBarText" | "statusBar.setText" => {
1322 use tauri::Emitter;
1323 let ItemId = Params.get("itemId").and_then(|V| V.as_str()).unwrap_or("").to_string();
1324 let Text = Params.get("text").and_then(|V| V.as_str()).unwrap_or("").to_string();
1325 let _ = self
1326 .environment
1327 .ApplicationHandle
1328 .emit("sky://statusbar/update", json!({ "id": ItemId, "text": Text }));
1329 },
1330 "disposeStatusBarItem" | "statusBar.dispose" => {
1331 use tauri::Emitter;
1332 let ItemId = Params.get("itemId").and_then(|V| V.as_str()).unwrap_or("").to_string();
1333 let _ = self
1334 .environment
1335 .ApplicationHandle
1336 .emit("sky://statusbar/dispose", json!({ "id": ItemId }));
1337 },
1338 "output.create" => {
1340 use tauri::Emitter;
1341 let Id = Params.get("id").and_then(|V| V.as_str()).unwrap_or("").to_string();
1342 let Name = Params.get("name").and_then(|V| V.as_str()).unwrap_or("").to_string();
1343 let _ = self
1344 .environment
1345 .ApplicationHandle
1346 .emit("sky://output/create", json!({ "id": Id, "name": Name }));
1347 },
1348 "output.append" => {
1349 use tauri::Emitter;
1350 let Channel = Params.get("channel").and_then(|V| V.as_str()).unwrap_or("").to_string();
1351 let Text = Params.get("value").and_then(|V| V.as_str()).unwrap_or("").to_string();
1352 let _ = self
1353 .environment
1354 .ApplicationHandle
1355 .emit("sky://output/append", json!({ "channel": Channel, "text": Text }));
1356 },
1357 "output.appendLine" => {
1358 use tauri::Emitter;
1359 let Channel = Params.get("channel").and_then(|V| V.as_str()).unwrap_or("").to_string();
1360 let Line = Params.get("value").and_then(|V| V.as_str()).unwrap_or("").to_string();
1361 let _ = self.environment.ApplicationHandle.emit(
1362 "sky://output/append",
1363 json!({ "channel": Channel, "text": format!("{}\n", Line) }),
1364 );
1365 },
1366 "output.clear" => {
1367 use tauri::Emitter;
1368 let Channel = Params.get("channel").and_then(|V| V.as_str()).unwrap_or("").to_string();
1369 let _ = self
1370 .environment
1371 .ApplicationHandle
1372 .emit("sky://output/clear", json!({ "channel": Channel }));
1373 },
1374 "output.show" => {
1375 use tauri::Emitter;
1376 let Channel = Params.get("channel").and_then(|V| V.as_str()).unwrap_or("").to_string();
1377 let _ = self
1378 .environment
1379 .ApplicationHandle
1380 .emit("sky://output/show", json!({ "channel": Channel }));
1381 },
1382 "output.dispose" => {
1383 use tauri::Emitter;
1384 let Channel = Params.get("channel").and_then(|V| V.as_str()).unwrap_or("").to_string();
1385 let _ = self
1386 .environment
1387 .ApplicationHandle
1388 .emit("sky://output/dispose", json!({ "channel": Channel }));
1389 },
1390 "set_language_configuration" => {
1392 use tauri::Emitter;
1394 let Language = Params.get("language").and_then(|V| V.as_str()).unwrap_or("").to_string();
1395 let _ = self
1396 .environment
1397 .ApplicationHandle
1398 .emit("sky://language/configure", json!({ "language": Language }));
1399 },
1400 _ => {
1401 dev_log!(
1402 "cocoon",
1403 "[CocoonService] Unknown notification method: '{}'",
1404 notification.method
1405 );
1406 },
1407 }
1408
1409 Ok(Response::new(Empty {}))
1410 }
1411
1412 async fn cancel_operation(&self, request:Request<CancelOperationRequest>) -> Result<Response<Empty>, Status> {
1414 let cancel_request = request.into_inner();
1415 dev_log!(
1416 "cocoon",
1417 "[CocoonService] Cancel operation request: {}",
1418 cancel_request.request_identifier_to_cancel
1419 );
1420
1421 if let Some(token) = self
1423 .ActiveOperations
1424 .read()
1425 .await
1426 .get(&cancel_request.request_identifier_to_cancel)
1427 {
1428 dev_log!(
1429 "cocoon",
1430 "[CocoonService] Triggering cancellation token for operation {}",
1431 cancel_request.request_identifier_to_cancel
1432 );
1433 token.cancel();
1434 } else {
1435 dev_log!(
1436 "cocoon",
1437 "warn: [CocoonService] No active operation found for cancellation: {}",
1438 cancel_request.request_identifier_to_cancel
1439 );
1440 }
1441
1442 Ok(Response::new(Empty {}))
1443 }
1444
1445 async fn initial_handshake(&self, _request:Request<Empty>) -> Result<Response<Empty>, Status> {
1449 dev_log!("cocoon", "[CocoonService] Initial handshake received from Cocoon");
1450 Ok(Response::new(Empty {}))
1451 }
1452
1453 async fn init_extension_host(&self, request:Request<InitExtensionHostRequest>) -> Result<Response<Empty>, Status> {
1455 let req = request.into_inner();
1456 dev_log!(
1457 "cocoon",
1458 "[CocoonService] Initializing extension host with {} workspace folders",
1459 req.workspace_folders.len()
1460 );
1461
1462 for folder in &req.workspace_folders {
1465 dev_log!(
1466 "cocoon",
1467 "[CocoonService] Workspace folder: {} ({})",
1468 folder.name,
1469 folder.uri.as_ref().map(|u| &u.value).unwrap_or(&String::new())
1470 );
1471 }
1472
1473 dev_log!("cocoon", "[CocoonService] Configuration: {} keys", req.configuration.len());
1475
1476 let Folders:Vec<WorkspaceFolderStateDTO> = req
1478 .workspace_folders
1479 .iter()
1480 .enumerate()
1481 .filter_map(|(Index, F)| {
1482 let UriValue = F.uri.as_ref().map(|U| U.value.as_str()).unwrap_or("");
1483 url::Url::parse(UriValue)
1484 .ok()
1485 .and_then(|ParsedUrl| WorkspaceFolderStateDTO::New(ParsedUrl, F.name.clone(), Index).ok())
1486 })
1487 .collect();
1488
1489 if !Folders.is_empty() {
1490 self.environment.ApplicationState.Workspace.SetWorkspaceFolders(Folders);
1491 dev_log!(
1492 "cocoon",
1493 "[CocoonService] Workspace folders stored: {}",
1494 req.workspace_folders.len()
1495 );
1496 }
1497
1498 Ok(Response::new(Empty {}))
1499 }
1500
1501 async fn register_command(&self, request:Request<RegisterCommandRequest>) -> Result<Response<Empty>, Status> {
1505 let req = request.into_inner();
1506 dev_log!(
1507 "cocoon",
1508 "[CocoonService] Registering command '{}' from extension '{}'",
1509 req.command_id,
1510 req.extension_id
1511 );
1512
1513 if let Err(Error) = self
1516 .environment
1517 .RegisterCommand(req.extension_id.clone(), req.command_id.clone())
1518 .await
1519 {
1520 dev_log!(
1521 "cocoon",
1522 "warn: [CocoonService] Failed to register command '{}': {:?}",
1523 req.command_id,
1524 Error
1525 );
1526 } else {
1527 dev_log!(
1528 "cocoon",
1529 "[CocoonService] Command registered: id={}, title={:?}",
1530 req.command_id,
1531 req.title
1532 );
1533 }
1534
1535 Ok(Response::new(Empty {}))
1536 }
1537
1538 async fn execute_contributed_command(
1540 &self,
1541 request:Request<ExecuteCommandRequest>,
1542 ) -> Result<Response<ExecuteCommandResponse>, Status> {
1543 let req = request.into_inner();
1544 dev_log!(
1545 "cocoon",
1546 "[CocoonService] Executing command '{}' with {} arguments",
1547 req.command_id,
1548 req.arguments.len()
1549 );
1550
1551 for (i, arg) in req.arguments.iter().enumerate() {
1554 dev_log!("cocoon", "[CocoonService] Argument {}: {:?}", i, arg);
1555 }
1556
1557 let Arg:serde_json::Value = req
1559 .arguments
1560 .first()
1561 .and_then(|A| A.value.as_ref())
1562 .map(|V| {
1563 match V {
1564 crate::Vine::Generated::argument::Value::StringValue(S) => json!(S),
1565 crate::Vine::Generated::argument::Value::IntValue(I) => json!(I),
1566 crate::Vine::Generated::argument::Value::BoolValue(B) => json!(B),
1567 crate::Vine::Generated::argument::Value::BytesValue(Bytes) => {
1568 serde_json::from_slice(Bytes).unwrap_or(serde_json::Value::Null)
1569 },
1570 }
1571 })
1572 .unwrap_or(serde_json::Value::Null);
1573
1574 match self.environment.ExecuteCommand(req.command_id, Arg).await {
1575 Ok(Value) => {
1576 let Bytes = serde_json::to_vec(&Value).unwrap_or_default();
1577 Ok(Response::new(ExecuteCommandResponse {
1578 result:Some(crate::Vine::Generated::execute_command_response::Result::Value(Bytes)),
1579 }))
1580 },
1581 Err(Error) => {
1582 let Bytes = serde_json::to_vec(&Error.to_string()).unwrap_or_default();
1583 Ok(Response::new(ExecuteCommandResponse {
1584 result:Some(crate::Vine::Generated::execute_command_response::Result::Error(
1585 crate::Vine::Generated::RpcError { code:-32000, message:Error.to_string(), data:Bytes },
1586 )),
1587 }))
1588 },
1589 }
1590 }
1591
1592 async fn unregister_command(&self, request:Request<UnregisterCommandRequest>) -> Result<Response<Empty>, Status> {
1594 let req = request.into_inner();
1595 dev_log!("cocoon", "[CocoonService] Unregistering command '{}'", req.command_id);
1596
1597 if let Err(Error) = self.environment.UnregisterCommand(String::new(), req.command_id.clone()).await {
1599 dev_log!(
1600 "cocoon",
1601 "warn: [CocoonService] Failed to unregister command '{}': {:?}",
1602 req.command_id,
1603 Error
1604 );
1605 } else {
1606 dev_log!("cocoon", "[CocoonService] Command removed: {}", req.command_id);
1607 }
1608
1609 Ok(Response::new(Empty {}))
1610 }
1611
1612 async fn register_hover_provider(
1616 &self,
1617 request:Request<RegisterProviderRequest>,
1618 ) -> Result<Response<Empty>, Status> {
1619 let req = request.into_inner();
1620 dev_log!(
1621 "cocoon",
1622 "[CocoonService] Registering hover provider for '{}' with handle {}",
1623 req.language_selector,
1624 req.handle
1625 );
1626 self.RegisterProvider(req.handle, ProviderType::Hover, &req.language_selector, &req.extension_id);
1627 Ok(Response::new(Empty {}))
1628 }
1629
1630 async fn provide_hover(
1639 &self,
1640 request:Request<ProvideHoverRequest>,
1641 ) -> Result<Response<ProvideHoverResponse>, Status> {
1642 let req = request.into_inner();
1643 dev_log!("cocoon", "[CocoonService] Providing hover for provider {}", req.provider_handle);
1644
1645 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
1646 let document_uri =
1647 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
1648
1649 let position = req.position.as_ref();
1650 let position_dto = PositionDTO {
1651 LineNumber:position.map(|p| p.line).unwrap_or(0),
1652 Column:position.map(|p| p.character).unwrap_or(0),
1653 };
1654
1655 match self.environment.ProvideHover(document_uri, position_dto).await {
1656 Ok(Some(hover)) => {
1657 let markdown = hover
1658 .Contents
1659 .iter()
1660 .map(|c| c.Value.as_str())
1661 .collect::<Vec<_>>()
1662 .join("\n---\n");
1663 let range = hover.Range.map(|r| {
1664 Range {
1665 start:Some(Position { line:r.StartLineNumber, character:r.StartColumn }),
1666 end:Some(Position { line:r.EndLineNumber, character:r.EndColumn }),
1667 }
1668 });
1669 Ok(Response::new(ProvideHoverResponse { markdown, range }))
1670 },
1671 Ok(None) => {
1672 dev_log!("cocoon", "[CocoonService] No hover provider found for request");
1673 Ok(Response::new(ProvideHoverResponse { markdown:String::new(), range:None }))
1674 },
1675 Err(e) => {
1676 dev_log!("cocoon", "warn: [CocoonService] Hover failed: {}", e);
1677 Err(Status::internal(format!("Hover failed: {}", e)))
1678 },
1679 }
1680 }
1681
1682 async fn register_completion_item_provider(
1684 &self,
1685 request:Request<RegisterProviderRequest>,
1686 ) -> Result<Response<Empty>, Status> {
1687 let req = request.into_inner();
1688 dev_log!(
1689 "cocoon",
1690 "[CocoonService] Registering completion provider for '{}' with handle {}",
1691 req.language_selector,
1692 req.handle
1693 );
1694 self.RegisterProvider(req.handle, ProviderType::Completion, &req.language_selector, &req.extension_id);
1695 Ok(Response::new(Empty {}))
1696 }
1697
1698 async fn provide_completion_items(
1700 &self,
1701 request:Request<ProvideCompletionItemsRequest>,
1702 ) -> Result<Response<ProvideCompletionItemsResponse>, Status> {
1703 let req = request.into_inner();
1704 dev_log!(
1705 "cocoon",
1706 "[CocoonService] Providing completions for provider {}",
1707 req.provider_handle
1708 );
1709
1710 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
1711 let document_uri =
1712 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
1713 let position = req.position.as_ref();
1714 let position_dto = PositionDTO {
1715 LineNumber:position.map(|p| p.line).unwrap_or(0),
1716 Column:position.map(|p| p.character).unwrap_or(0),
1717 };
1718 let context_dto = CommonLibrary::LanguageFeature::DTO::CompletionContextDTO::CompletionContextDTO {
1719 TriggerKind:CommonLibrary::LanguageFeature::DTO::CompletionContextDTO::CompletionTriggerKindDTO::Invoke,
1720 TriggerCharacter:if req.trigger_character.is_empty() {
1721 None
1722 } else {
1723 Some(req.trigger_character.clone())
1724 },
1725 };
1726
1727 match self
1728 .environment
1729 .ProvideCompletions(document_uri, position_dto, context_dto, None)
1730 .await
1731 {
1732 Ok(Some(list)) => {
1733 let items = list
1734 .Suggestions
1735 .iter()
1736 .map(|s| {
1737 CompletionItem {
1738 label:s.Label.as_str().map(|l| l.to_string()).unwrap_or_default(),
1739 kind:format!("{}", s.Kind),
1740 detail:s.Detail.clone().unwrap_or_default(),
1741 documentation:Vec::new(),
1742 insert_text:s.InsertText.as_ref().and_then(|v| v.as_str()).unwrap_or("").to_string(),
1743 }
1744 })
1745 .collect();
1746 Ok(Response::new(ProvideCompletionItemsResponse { items }))
1747 },
1748 Ok(None) => Ok(Response::new(ProvideCompletionItemsResponse { items:Vec::new() })),
1749 Err(e) => Err(Status::internal(format!("Completions failed: {}", e))),
1750 }
1751 }
1752
1753 async fn register_definition_provider(
1755 &self,
1756 request:Request<RegisterProviderRequest>,
1757 ) -> Result<Response<Empty>, Status> {
1758 let req = request.into_inner();
1759 dev_log!(
1760 "cocoon",
1761 "[CocoonService] Registering definition provider for '{}' with handle {}",
1762 req.language_selector,
1763 req.handle
1764 );
1765 self.RegisterProvider(req.handle, ProviderType::Definition, &req.language_selector, &req.extension_id);
1766 Ok(Response::new(Empty {}))
1767 }
1768
1769 async fn provide_definition(
1771 &self,
1772 request:Request<ProvideDefinitionRequest>,
1773 ) -> Result<Response<ProvideDefinitionResponse>, Status> {
1774 let req = request.into_inner();
1775 dev_log!(
1776 "cocoon",
1777 "[CocoonService] Providing definition for provider {}",
1778 req.provider_handle
1779 );
1780
1781 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
1782 let document_uri =
1783 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
1784 let position = req.position.as_ref();
1785 let position_dto = PositionDTO {
1786 LineNumber:position.map(|p| p.line).unwrap_or(0),
1787 Column:position.map(|p| p.character).unwrap_or(0),
1788 };
1789
1790 match self.environment.ProvideDefinition(document_uri, position_dto).await {
1791 Ok(Some(locations)) => {
1792 let proto_locations = locations
1793 .iter()
1794 .map(|loc| {
1795 Location {
1796 uri:Some(Uri { value:loc.Uri.to_string() }),
1797 range:Some(Range {
1798 start:Some(Position {
1799 line:loc.Range.StartLineNumber,
1800 character:loc.Range.StartColumn,
1801 }),
1802 end:Some(Position { line:loc.Range.EndLineNumber, character:loc.Range.EndColumn }),
1803 }),
1804 }
1805 })
1806 .collect();
1807 Ok(Response::new(ProvideDefinitionResponse { locations:proto_locations }))
1808 },
1809 Ok(None) => Ok(Response::new(ProvideDefinitionResponse { locations:Vec::new() })),
1810 Err(e) => Err(Status::internal(format!("Definition failed: {}", e))),
1811 }
1812 }
1813
1814 async fn register_reference_provider(
1816 &self,
1817 request:Request<RegisterProviderRequest>,
1818 ) -> Result<Response<Empty>, Status> {
1819 let req = request.into_inner();
1820 dev_log!(
1821 "cocoon",
1822 "[CocoonService] Registering reference provider for '{}' with handle {}",
1823 req.language_selector,
1824 req.handle
1825 );
1826 self.RegisterProvider(req.handle, ProviderType::References, &req.language_selector, &req.extension_id);
1827 Ok(Response::new(Empty {}))
1828 }
1829
1830 async fn provide_references(
1832 &self,
1833 request:Request<ProvideReferencesRequest>,
1834 ) -> Result<Response<ProvideReferencesResponse>, Status> {
1835 let req = request.into_inner();
1836 dev_log!(
1837 "cocoon",
1838 "[CocoonService] Providing references for provider {}",
1839 req.provider_handle
1840 );
1841
1842 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
1843 let document_uri =
1844 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
1845 let position = req.position.as_ref();
1846 let position_dto = PositionDTO {
1847 LineNumber:position.map(|p| p.line).unwrap_or(0),
1848 Column:position.map(|p| p.character).unwrap_or(0),
1849 };
1850 let context_dto = json!({ "includeDeclaration": true });
1851
1852 match self
1853 .environment
1854 .ProvideReferences(document_uri, position_dto, context_dto)
1855 .await
1856 {
1857 Ok(Some(locations)) => {
1858 let proto_locations = locations
1859 .iter()
1860 .map(|loc| {
1861 Location {
1862 uri:Some(Uri { value:loc.Uri.to_string() }),
1863 range:Some(Range {
1864 start:Some(Position {
1865 line:loc.Range.StartLineNumber,
1866 character:loc.Range.StartColumn,
1867 }),
1868 end:Some(Position { line:loc.Range.EndLineNumber, character:loc.Range.EndColumn }),
1869 }),
1870 }
1871 })
1872 .collect();
1873 Ok(Response::new(ProvideReferencesResponse { locations:proto_locations }))
1874 },
1875 Ok(None) => Ok(Response::new(ProvideReferencesResponse { locations:Vec::new() })),
1876 Err(e) => Err(Status::internal(format!("References failed: {}", e))),
1877 }
1878 }
1879
1880 async fn register_code_actions_provider(
1882 &self,
1883 request:Request<RegisterProviderRequest>,
1884 ) -> Result<Response<Empty>, Status> {
1885 let req = request.into_inner();
1886 dev_log!(
1887 "cocoon",
1888 "[CocoonService] Registering code actions provider for '{}' with handle {}",
1889 req.language_selector,
1890 req.handle
1891 );
1892 self.RegisterProvider(req.handle, ProviderType::CodeAction, &req.language_selector, &req.extension_id);
1893 Ok(Response::new(Empty {}))
1894 }
1895
1896 async fn provide_code_actions(
1898 &self,
1899 request:Request<ProvideCodeActionsRequest>,
1900 ) -> Result<Response<ProvideCodeActionsResponse>, Status> {
1901 let req = request.into_inner();
1902 dev_log!(
1903 "cocoon",
1904 "[CocoonService] Providing code actions for provider {}",
1905 req.provider_handle
1906 );
1907
1908 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
1909 let document_uri =
1910 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
1911 let range = req.range.as_ref();
1912 let range_dto = json!({
1913 "StartLineNumber": range.and_then(|r| r.start.as_ref()).map(|p| p.line).unwrap_or(0),
1914 "StartColumn": range.and_then(|r| r.start.as_ref()).map(|p| p.character).unwrap_or(0),
1915 "EndLineNumber": range.and_then(|r| r.end.as_ref()).map(|p| p.line).unwrap_or(0),
1916 "EndColumn": range.and_then(|r| r.end.as_ref()).map(|p| p.character).unwrap_or(0),
1917 });
1918 let context_dto = json!({ "diagnostics": [], "only": null });
1919
1920 match self.environment.ProvideCodeActions(document_uri, range_dto, context_dto).await {
1921 Ok(Some(value)) => {
1922 Ok(Response::new(ProvideCodeActionsResponse { actions:Vec::new() }))
1926 },
1927 Ok(None) => Ok(Response::new(ProvideCodeActionsResponse { actions:Vec::new() })),
1928 Err(e) => Err(Status::internal(format!("Code actions failed: {}", e))),
1929 }
1930 }
1931
1932 async fn show_text_document(
1936 &self,
1937 request:Request<ShowTextDocumentRequest>,
1938 ) -> Result<Response<ShowTextDocumentResponse>, Status> {
1939 use tauri::Emitter;
1940
1941 let req = request.into_inner();
1942 let Uri = req.uri.as_ref().map(|U| U.value.clone()).unwrap_or_default();
1943 dev_log!("cocoon", "[CocoonService] show_text_document: {}", Uri);
1944
1945 let _ = self.environment.ApplicationHandle.emit(
1946 "sky://editor/openDocument",
1947 json!({ "uri": Uri, "viewColumn": req.view_column }),
1948 );
1949
1950 Ok(Response::new(ShowTextDocumentResponse { success:true }))
1951 }
1952
1953 async fn show_information_message(
1956 &self,
1957 request:Request<ShowMessageRequest>,
1958 ) -> Result<Response<ShowMessageResponse>, Status> {
1959 use CommonLibrary::UserInterface::{
1960 DTO::MessageSeverity::MessageSeverity,
1961 UserInterfaceProvider::UserInterfaceProvider,
1962 };
1963
1964 let req = request.into_inner();
1965 dev_log!("cocoon", "[CocoonService] show_information_message: {}", req.message);
1966
1967 let _ = self.environment.ShowMessage(MessageSeverity::Info, req.message, None).await;
1968
1969 Ok(Response::new(ShowMessageResponse { success:true }))
1970 }
1971
1972 async fn show_warning_message(
1974 &self,
1975 request:Request<ShowMessageRequest>,
1976 ) -> Result<Response<ShowMessageResponse>, Status> {
1977 use CommonLibrary::UserInterface::{
1978 DTO::MessageSeverity::MessageSeverity,
1979 UserInterfaceProvider::UserInterfaceProvider,
1980 };
1981
1982 let req = request.into_inner();
1983 dev_log!("cocoon", "warn: [CocoonService] show_warning_message: {}", req.message);
1984
1985 let _ = self.environment.ShowMessage(MessageSeverity::Warning, req.message, None).await;
1986
1987 Ok(Response::new(ShowMessageResponse { success:true }))
1988 }
1989
1990 async fn show_error_message(
1992 &self,
1993 request:Request<ShowMessageRequest>,
1994 ) -> Result<Response<ShowMessageResponse>, Status> {
1995 use CommonLibrary::UserInterface::{
1996 DTO::MessageSeverity::MessageSeverity,
1997 UserInterfaceProvider::UserInterfaceProvider,
1998 };
1999
2000 let req = request.into_inner();
2001 dev_log!("cocoon", "error: [CocoonService] show_error_message: {}", req.message);
2002
2003 let _ = self.environment.ShowMessage(MessageSeverity::Error, req.message, None).await;
2004
2005 Ok(Response::new(ShowMessageResponse { success:true }))
2006 }
2007
2008 async fn create_status_bar_item(
2011 &self,
2012 request:Request<CreateStatusBarItemRequest>,
2013 ) -> Result<Response<CreateStatusBarItemResponse>, Status> {
2014 use tauri::Emitter;
2015
2016 let req = request.into_inner();
2017 dev_log!("cocoon", "[CocoonService] create_status_bar_item: {}", req.id);
2018
2019 let _ = self.environment.ApplicationHandle.emit(
2020 "sky://statusbar/create",
2021 json!({ "id": req.id, "text": req.text, "tooltip": req.tooltip }),
2022 );
2023
2024 Ok(Response::new(CreateStatusBarItemResponse { item_id:req.id.clone() }))
2025 }
2026
2027 async fn set_status_bar_text(&self, request:Request<SetStatusBarTextRequest>) -> Result<Response<Empty>, Status> {
2029 use tauri::Emitter;
2030
2031 let req = request.into_inner();
2032 dev_log!(
2033 "cocoon",
2034 "[CocoonService] set_status_bar_text: id={} text={}",
2035 req.item_id,
2036 req.text
2037 );
2038
2039 let _ = self
2040 .environment
2041 .ApplicationHandle
2042 .emit("sky://statusbar/update", json!({ "id": req.item_id, "text": req.text }));
2043
2044 Ok(Response::new(Empty {}))
2045 }
2046
2047 async fn create_webview_panel(
2049 &self,
2050 request:Request<CreateWebviewPanelRequest>,
2051 ) -> Result<Response<CreateWebviewPanelResponse>, Status> {
2052 use tauri::Emitter;
2053
2054 let req = request.into_inner();
2055 let Handle = SystemTime::now()
2056 .duration_since(UNIX_EPOCH)
2057 .map(|D| D.as_millis() as u32)
2058 .unwrap_or(0);
2059
2060 dev_log!(
2061 "cocoon",
2062 "[CocoonService] create_webview_panel: handle={} view_type={} title={}",
2063 Handle,
2064 req.view_type,
2065 req.title
2066 );
2067
2068 let _ = self.environment.ApplicationHandle.emit(
2069 "sky://webview/create",
2070 json!({
2071 "handle": Handle,
2072 "viewType": req.view_type,
2073 "title": req.title,
2074 "viewColumn": req.view_column,
2075 "preserveFocus": req.preserve_focus,
2076 "iconPath": req.icon_path,
2077 }),
2078 );
2079
2080 Ok(Response::new(CreateWebviewPanelResponse { handle:Handle }))
2081 }
2082
2083 async fn set_webview_html(&self, request:Request<SetWebviewHtmlRequest>) -> Result<Response<Empty>, Status> {
2085 use tauri::Emitter;
2086
2087 let req = request.into_inner();
2088 dev_log!(
2089 "cocoon",
2090 "[CocoonService] set_webview_html: handle={} ({} bytes)",
2091 req.handle,
2092 req.html.len()
2093 );
2094
2095 let _ = self
2096 .environment
2097 .ApplicationHandle
2098 .emit("sky://webview/setHtml", json!({ "handle": req.handle, "html": req.html }));
2099
2100 Ok(Response::new(Empty {}))
2101 }
2102
2103 async fn on_did_receive_message(
2106 &self,
2107 request:Request<OnDidReceiveMessageRequest>,
2108 ) -> Result<Response<Empty>, Status> {
2109 use tauri::Emitter;
2110
2111 let req = request.into_inner();
2112 dev_log!("cocoon", "[CocoonService] on_did_receive_message: handle={}", req.handle);
2113
2114 let MessagePayload = match &req.message {
2115 Some(on_did_receive_message_request::Message::StringMessage(S)) => json!(S),
2116 Some(on_did_receive_message_request::Message::BytesMessage(B)) => json!(B),
2117 None => serde_json::Value::Null,
2118 };
2119
2120 let _ = self.environment.ApplicationHandle.emit(
2121 "sky://webview/message",
2122 json!({ "handle": req.handle, "message": MessagePayload }),
2123 );
2124
2125 Ok(Response::new(Empty {}))
2126 }
2127
2128 async fn read_file(&self, request:Request<ReadFileRequest>) -> Result<Response<ReadFileResponse>, Status> {
2132 let req = request.into_inner();
2133 let Path = Self::UriToPath(req.uri.as_ref())
2134 .ok_or_else(|| Status::invalid_argument("read_file: missing or empty URI"))?;
2135
2136 dev_log!("cocoon", "[CocoonService] Reading file: {:?}", Path);
2137
2138 let Content = tokio::fs::read(&Path).await.map_err(|Error| {
2139 dev_log!("cocoon", "warn: [CocoonService] read_file failed for {:?}: {}", Path, Error);
2140 Status::not_found(format!("read_file: {}: {}", Path.display(), Error))
2141 })?;
2142
2143 Ok(Response::new(ReadFileResponse {
2144 content:Content,
2145 encoding:"utf-8".to_string(),
2146 }))
2147 }
2148
2149 async fn write_file(&self, request:Request<WriteFileRequest>) -> Result<Response<Empty>, Status> {
2151 let req = request.into_inner();
2152 let Path = Self::UriToPath(req.uri.as_ref())
2153 .ok_or_else(|| Status::invalid_argument("write_file: missing or empty URI"))?;
2154
2155 dev_log!(
2156 "cocoon",
2157 "[CocoonService] Writing file: {:?} ({} bytes)",
2158 Path,
2159 req.content.len()
2160 );
2161
2162 if let Some(Parent) = Path.parent() {
2164 if !Parent.as_os_str().is_empty() {
2165 tokio::fs::create_dir_all(Parent)
2166 .await
2167 .map_err(|Error| Status::internal(format!("write_file: create_dir_all {:?}: {}", Parent, Error)))?;
2168 }
2169 }
2170
2171 tokio::fs::write(&Path, &req.content).await.map_err(|Error| {
2172 dev_log!("cocoon", "warn: [CocoonService] write_file failed for {:?}: {}", Path, Error);
2173 Status::internal(format!("write_file: {}: {}", Path.display(), Error))
2174 })?;
2175
2176 Ok(Response::new(Empty {}))
2177 }
2178
2179 async fn stat(&self, request:Request<StatRequest>) -> Result<Response<StatResponse>, Status> {
2181 let req = request.into_inner();
2182 let Path =
2183 Self::UriToPath(req.uri.as_ref()).ok_or_else(|| Status::invalid_argument("stat: missing or empty URI"))?;
2184
2185 dev_log!("cocoon", "[CocoonService] Stat: {:?}", Path);
2186
2187 let Metadata = tokio::fs::metadata(&Path).await.map_err(|Error| {
2188 dev_log!("cocoon", "warn: [CocoonService] stat failed for {:?}: {}", Path, Error);
2189 Status::not_found(format!("stat: {}: {}", Path.display(), Error))
2190 })?;
2191
2192 let Mtime = Metadata
2193 .modified()
2194 .ok()
2195 .and_then(|T| T.duration_since(UNIX_EPOCH).ok())
2196 .map(|D| D.as_millis() as u64)
2197 .unwrap_or(0);
2198
2199 Ok(Response::new(StatResponse {
2200 is_file:Metadata.is_file(),
2201 is_directory:Metadata.is_dir(),
2202 size:Metadata.len(),
2203 mtime:Mtime,
2204 }))
2205 }
2206
2207 async fn readdir(&self, request:Request<ReaddirRequest>) -> Result<Response<ReaddirResponse>, Status> {
2209 let req = request.into_inner();
2210 let Path = Self::UriToPath(req.uri.as_ref())
2211 .ok_or_else(|| Status::invalid_argument("readdir: missing or empty URI"))?;
2212
2213 dev_log!("cocoon", "[CocoonService] Readdir: {:?}", Path);
2214
2215 let mut ReadDir = tokio::fs::read_dir(&Path).await.map_err(|Error| {
2216 dev_log!("cocoon", "warn: [CocoonService] readdir failed for {:?}: {}", Path, Error);
2217 Status::not_found(format!("readdir: {}: {}", Path.display(), Error))
2218 })?;
2219
2220 let mut Entries = Vec::new();
2221 while let Ok(Some(Entry)) = ReadDir.next_entry().await {
2222 if let Some(Name) = Entry.file_name().to_str() {
2223 Entries.push(Name.to_string());
2224 }
2225 }
2226
2227 Ok(Response::new(ReaddirResponse { entries:Entries }))
2228 }
2229
2230 async fn watch_file(&self, request:Request<WatchFileRequest>) -> Result<Response<Empty>, Status> {
2235 let req = request.into_inner();
2236 let Uri = req.uri.as_ref().map(|U| U.value.as_str()).unwrap_or("");
2237 dev_log!(
2238 "cocoon",
2239 "[CocoonService] watch_file registered (polling not yet active): {}",
2240 Uri
2241 );
2242 Ok(Response::new(Empty {}))
2246 }
2247
2248 async fn find_files(&self, request:Request<FindFilesRequest>) -> Result<Response<FindFilesResponse>, Status> {
2253 let req = request.into_inner();
2254 dev_log!("cocoon", "[CocoonService] Finding files with pattern: {}", req.pattern);
2255
2256 use globset::{Glob, GlobSetBuilder};
2257
2258 let GlobMatcher = Glob::new(&req.pattern)
2260 .map_err(|Error| {
2261 Status::invalid_argument(format!("find_files: invalid pattern '{}': {}", req.pattern, Error))
2262 })?
2263 .compile_matcher();
2264
2265 let Roots:Vec<std::path::PathBuf> = {
2267 match self.environment.ApplicationState.Workspace.WorkspaceFolders.lock() {
2268 Ok(Guard) => Guard.iter().map(|F| std::path::PathBuf::from(F.URI.path())).collect(),
2269 Err(_) => Vec::new(),
2270 }
2271 };
2272
2273 let SearchRoots = if Roots.is_empty() {
2274 vec![std::env::current_dir().unwrap_or_default()]
2275 } else {
2276 Roots
2277 };
2278
2279 let mut Uris = Vec::new();
2281
2282 fn WalkAndCollect(
2283 Directory:&std::path::Path,
2284 Root:&std::path::Path,
2285 Matcher:&globset::GlobMatcher,
2286 Results:&mut Vec<String>,
2287 ) {
2288 if let Ok(Entries) = std::fs::read_dir(Directory) {
2289 for Entry in Entries.flatten() {
2290 let EntryPath = Entry.path();
2291 if EntryPath.is_dir() {
2292 WalkAndCollect(&EntryPath, Root, Matcher, Results);
2293 } else if let Ok(Relative) = EntryPath.strip_prefix(Root) {
2294 if Matcher.is_match(Relative) {
2295 Results.push(format!("file://{}", EntryPath.display()));
2296 }
2297 }
2298 }
2299 }
2300 }
2301
2302 for Root in &SearchRoots {
2303 WalkAndCollect(Root, Root, &GlobMatcher, &mut Uris);
2304 }
2305
2306 dev_log!(
2307 "cocoon",
2308 "[CocoonService] find_files: {} results for pattern '{}'",
2309 Uris.len(),
2310 req.pattern
2311 );
2312 Ok(Response::new(FindFilesResponse { uris:Uris }))
2313 }
2314
2315 async fn find_text_in_files(
2320 &self,
2321 request:Request<FindTextInFilesRequest>,
2322 ) -> Result<Response<FindTextInFilesResponse>, Status> {
2323 let req = request.into_inner();
2324 if req.pattern.is_empty() {
2325 return Ok(Response::new(FindTextInFilesResponse::default()));
2326 }
2327 dev_log!("cocoon", "[CocoonService] find_text_in_files: pattern='{}'", req.pattern);
2328
2329 let Roots:Vec<std::path::PathBuf> = {
2330 match self.environment.ApplicationState.Workspace.WorkspaceFolders.lock() {
2331 Ok(Guard) => Guard.iter().map(|F| std::path::PathBuf::from(F.URI.path())).collect(),
2332 Err(_) => Vec::new(),
2333 }
2334 };
2335 let SearchRoots = if Roots.is_empty() {
2336 vec![std::env::current_dir().unwrap_or_default()]
2337 } else {
2338 Roots
2339 };
2340
2341 let Pattern = req.pattern.clone();
2342 let Matches = tokio::task::spawn_blocking(move || {
2343 let mut Results:Vec<TextMatch> = Vec::new();
2344 const MAX_MATCHES:usize = 1000;
2345
2346 fn WalkAndSearch(Dir:&std::path::Path, Pattern:&str, Results:&mut Vec<TextMatch>) {
2347 if Results.len() >= 1000 {
2348 return;
2349 }
2350 if let Ok(Entries) = std::fs::read_dir(Dir) {
2351 for Entry in Entries.flatten() {
2352 if Results.len() >= MAX_MATCHES {
2353 break;
2354 }
2355 let Path = Entry.path();
2356 if Path.is_dir() {
2357 let DirName = Path.file_name().and_then(|N| N.to_str()).unwrap_or("");
2359 if DirName.starts_with('.') || DirName == "node_modules" || DirName == "target" {
2360 continue;
2361 }
2362 WalkAndSearch(&Path, Pattern, Results);
2363 } else if Path.is_file() {
2364 if let Ok(Content) = std::fs::read_to_string(&Path) {
2365 for (LineIdx, Line) in Content.lines().enumerate() {
2366 if Results.len() >= MAX_MATCHES {
2367 break;
2368 }
2369 if let Some(ColIdx) = Line.find(Pattern) {
2370 Results.push(TextMatch {
2371 uri:Some(Uri { value:format!("file://{}", Path.display()) }),
2372 range:Some(Range {
2373 start:Some(Position { line:LineIdx as u32, character:ColIdx as u32 }),
2374 end:Some(Position {
2375 line:LineIdx as u32,
2376 character:(ColIdx + Pattern.len()) as u32,
2377 }),
2378 }),
2379 preview:Line.to_string(),
2380 });
2381 }
2382 }
2383 }
2384 }
2385 }
2386 }
2387 }
2388
2389 for Root in &SearchRoots {
2390 WalkAndSearch(Root, &Pattern, &mut Results);
2391 if Results.len() >= MAX_MATCHES {
2392 break;
2393 }
2394 }
2395 Results
2396 })
2397 .await
2398 .unwrap_or_default();
2399
2400 dev_log!(
2401 "cocoon",
2402 "[CocoonService] find_text_in_files: {} matches for '{}'",
2403 Matches.len(),
2404 req.pattern
2405 );
2406 Ok(Response::new(FindTextInFilesResponse { matches:Matches }))
2407 }
2408
2409 async fn open_document(
2411 &self,
2412 request:Request<OpenDocumentRequest>,
2413 ) -> Result<Response<OpenDocumentResponse>, Status> {
2414 use tauri::Emitter;
2415
2416 let req = request.into_inner();
2417 let Uri = req.uri.as_ref().map(|U| U.value.clone()).unwrap_or_default();
2418 dev_log!("cocoon", "[CocoonService] open_document: {}", Uri);
2419
2420 let _ = self.environment.ApplicationHandle.emit(
2421 "sky://editor/openDocument",
2422 json!({ "uri": Uri, "viewColumn": req.view_column }),
2423 );
2424
2425 Ok(Response::new(OpenDocumentResponse { success:true }))
2426 }
2427
2428 async fn save_all(&self, request:Request<SaveAllRequest>) -> Result<Response<SaveAllResponse>, Status> {
2430 use tauri::Emitter;
2431
2432 let req = request.into_inner();
2433 dev_log!("cocoon", "[CocoonService] save_all: includeUntitled={}", req.include_untitled);
2434
2435 let _ = self
2436 .environment
2437 .ApplicationHandle
2438 .emit("sky://editor/saveAll", json!({ "includeUntitled": req.include_untitled }));
2439
2440 Ok(Response::new(SaveAllResponse { success:true }))
2441 }
2442
2443 async fn apply_edit(&self, request:Request<ApplyEditRequest>) -> Result<Response<ApplyEditResponse>, Status> {
2445 use tauri::Emitter;
2446
2447 let req = request.into_inner();
2448 let Uri = req.uri.as_ref().map(|U| U.value.clone()).unwrap_or_default();
2449 dev_log!("cocoon", "[CocoonService] apply_edit: uri={} edits={}", Uri, req.edits.len());
2450
2451 let EditsJson:Vec<serde_json::Value> = req.edits.iter().map(|E| {
2452 json!({
2453 "range": {
2454 "start": E.range.as_ref().and_then(|R| R.start.as_ref()).map(|P| json!({ "line": P.line, "character": P.character })),
2455 "end": E.range.as_ref().and_then(|R| R.end.as_ref()).map(|P| json!({ "line": P.line, "character": P.character })),
2456 },
2457 "newText": E.new_text,
2458 })
2459 }).collect();
2460
2461 let _ = self
2462 .environment
2463 .ApplicationHandle
2464 .emit("sky://editor/applyEdits", json!({ "uri": Uri, "edits": EditsJson }));
2465
2466 Ok(Response::new(ApplyEditResponse { success:true }))
2467 }
2468
2469 async fn update_configuration(
2472 &self,
2473 request:Request<UpdateConfigurationRequest>,
2474 ) -> Result<Response<Empty>, Status> {
2475 use tauri::Emitter;
2476
2477 let req = request.into_inner();
2478 dev_log!(
2479 "cocoon",
2480 "[CocoonService] update_configuration: {} changed keys",
2481 req.changed_keys.len()
2482 );
2483
2484 let _ = self.environment.ApplicationHandle.emit(
2486 "sky://configuration/changed",
2487 json!({
2488 "changedKeys": req.changed_keys,
2489 }),
2490 );
2491
2492 Ok(Response::new(Empty {}))
2493 }
2494
2495 async fn update_workspace_folders(
2497 &self,
2498 request:Request<UpdateWorkspaceFoldersRequest>,
2499 ) -> Result<Response<Empty>, Status> {
2500 let req = request.into_inner();
2501 dev_log!(
2502 "cocoon",
2503 "[CocoonService] Updating workspace: {} additions, {} removals",
2504 req.additions.len(),
2505 req.removals.len()
2506 );
2507
2508 for addition in &req.additions {
2510 dev_log!(
2511 "cocoon",
2512 "[CocoonService] Adding workspace folder: {} ({})",
2513 addition.name,
2514 addition.uri.as_ref().map(|u| &u.value).unwrap_or(&"?".to_string())
2515 );
2516 }
2517 for removal in &req.removals {
2518 dev_log!(
2519 "cocoon",
2520 "[CocoonService] Removing workspace folder: {}",
2521 removal.uri.as_ref().map(|u| &u.value).unwrap_or(&"?".to_string())
2522 );
2523 }
2524
2525 {
2527 let mut Folders = self.environment.ApplicationState.Workspace.GetWorkspaceFolders();
2528
2529 let RemovalUris:Vec<String> = req
2531 .removals
2532 .iter()
2533 .filter_map(|F| F.uri.as_ref().map(|U| U.value.clone()))
2534 .collect();
2535 Folders.retain(|F| !RemovalUris.contains(&F.URI.to_string()));
2536
2537 let ExistingCount = Folders.len();
2539 for (Idx, Addition) in req.additions.iter().enumerate() {
2540 let UriValue = Addition.uri.as_ref().map(|U| U.value.as_str()).unwrap_or("");
2541 if let Ok(ParsedUrl) = url::Url::parse(UriValue) {
2542 if let Ok(DTO) = WorkspaceFolderStateDTO::New(ParsedUrl, Addition.name.clone(), ExistingCount + Idx)
2543 {
2544 Folders.push(DTO);
2545 }
2546 }
2547 }
2548
2549 self.environment.ApplicationState.Workspace.SetWorkspaceFolders(Folders);
2550 }
2551
2552 Ok(Response::new(Empty {}))
2553 }
2554
2555 async fn open_terminal(&self, request:Request<OpenTerminalRequest>) -> Result<Response<Empty>, Status> {
2560 let req = request.into_inner();
2561 dev_log!("cocoon", "[CocoonService] Opening terminal: {}", req.name);
2562
2563 let Options = json!({
2565 "name": req.name,
2566 "shellPath": if req.shell_path.is_empty() { serde_json::Value::Null } else { json!(req.shell_path) },
2567 "shellArgs": req.shell_args,
2568 "cwd": if req.cwd.is_empty() { serde_json::Value::Null } else { json!(req.cwd) },
2569 });
2570
2571 match self.environment.CreateTerminal(Options).await {
2572 Ok(Info) => {
2573 dev_log!("cocoon", "[CocoonService] Terminal created: {:?}", Info);
2574 Ok(Response::new(Empty {}))
2575 },
2576 Err(Error) => {
2577 dev_log!("cocoon", "error: [CocoonService] open_terminal failed: {}", Error);
2578 Err(Status::internal(format!("open_terminal: {}", Error)))
2579 },
2580 }
2581 }
2582
2583 async fn terminal_input(&self, request:Request<TerminalInputRequest>) -> Result<Response<Empty>, Status> {
2585 let req = request.into_inner();
2586 let TerminalId = req.terminal_id as u64;
2587 dev_log!(
2588 "cocoon",
2589 "[CocoonService] terminal_input: id={} bytes={}",
2590 TerminalId,
2591 req.data.len()
2592 );
2593
2594 let Text = String::from_utf8_lossy(&req.data).into_owned();
2595
2596 match self.environment.SendTextToTerminal(TerminalId, Text).await {
2597 Ok(()) => Ok(Response::new(Empty {})),
2598 Err(Error) => {
2599 dev_log!(
2600 "cocoon",
2601 "warn: [CocoonService] terminal_input failed id={}: {}",
2602 TerminalId,
2603 Error
2604 );
2605 Err(Status::not_found(format!("terminal_input: {}", Error)))
2606 },
2607 }
2608 }
2609
2610 async fn close_terminal(&self, request:Request<CloseTerminalRequest>) -> Result<Response<Empty>, Status> {
2612 let req = request.into_inner();
2613 let TerminalId = req.terminal_id as u64;
2614 dev_log!("cocoon", "[CocoonService] close_terminal: id={}", TerminalId);
2615
2616 match self.environment.DisposeTerminal(TerminalId).await {
2617 Ok(()) => Ok(Response::new(Empty {})),
2618 Err(Error) => {
2619 dev_log!(
2620 "cocoon",
2621 "warn: [CocoonService] close_terminal failed id={}: {}",
2622 TerminalId,
2623 Error
2624 );
2625 Err(Status::internal(format!("close_terminal: {}", Error)))
2626 },
2627 }
2628 }
2629
2630 async fn accept_terminal_opened(
2632 &self,
2633 request:Request<TerminalOpenedNotification>,
2634 ) -> Result<Response<Empty>, Status> {
2635 let req = request.into_inner();
2636 dev_log!(
2637 "cocoon",
2638 "[CocoonService] Terminal opened notification: {} (ID: {})",
2639 req.name,
2640 req.terminal_id
2641 );
2642
2643 use tauri::Emitter;
2645 let _ = self
2646 .environment
2647 .ApplicationHandle
2648 .emit("sky://terminal/opened", json!({ "id": req.terminal_id, "name": req.name }));
2649
2650 Ok(Response::new(Empty {}))
2651 }
2652
2653 async fn accept_terminal_closed(
2655 &self,
2656 request:Request<TerminalClosedNotification>,
2657 ) -> Result<Response<Empty>, Status> {
2658 use tauri::Emitter;
2659
2660 let req = request.into_inner();
2661 dev_log!("cocoon", "[CocoonService] Terminal closed: {}", req.terminal_id);
2662
2663 let _ = self
2664 .environment
2665 .ApplicationHandle
2666 .emit("sky://terminal/closed", json!({ "id": req.terminal_id }));
2667
2668 Ok(Response::new(Empty {}))
2669 }
2670
2671 async fn accept_terminal_process_id(
2673 &self,
2674 request:Request<TerminalProcessIdNotification>,
2675 ) -> Result<Response<Empty>, Status> {
2676 use tauri::Emitter;
2677
2678 let req = request.into_inner();
2679 dev_log!(
2680 "cocoon",
2681 "[CocoonService] Terminal PID: {} for terminal {}",
2682 req.process_id,
2683 req.terminal_id
2684 );
2685
2686 let _ = self.environment.ApplicationHandle.emit(
2687 "sky://terminal/processId",
2688 json!({ "id": req.terminal_id, "pid": req.process_id }),
2689 );
2690
2691 Ok(Response::new(Empty {}))
2692 }
2693
2694 async fn accept_terminal_process_data(
2696 &self,
2697 request:Request<TerminalDataNotification>,
2698 ) -> Result<Response<Empty>, Status> {
2699 use tauri::Emitter;
2700
2701 let req = request.into_inner();
2702 dev_log!(
2703 "cocoon",
2704 "[CocoonService] Terminal data for {}: {} bytes",
2705 req.terminal_id,
2706 req.data.len()
2707 );
2708
2709 let DataString = String::from_utf8_lossy(&req.data).to_string();
2710 let _ = self
2711 .environment
2712 .ApplicationHandle
2713 .emit("sky://terminal/data", json!({ "id": req.terminal_id, "data": DataString }));
2714
2715 Ok(Response::new(Empty {}))
2716 }
2717
2718 async fn register_tree_view_provider(
2722 &self,
2723 request:Request<RegisterTreeViewProviderRequest>,
2724 ) -> Result<Response<Empty>, Status> {
2725 let req = request.into_inner();
2726 dev_log!("cocoon", "[CocoonService] Registering tree view provider: {}", req.view_id);
2727
2728 use tauri::Emitter;
2730
2731 let Handle = req
2732 .view_id
2733 .as_bytes()
2734 .iter()
2735 .fold(0u32, |Acc, B| Acc.wrapping_mul(31).wrapping_add(*B as u32));
2736 let dto = ProviderRegistrationDTO {
2737 Handle,
2738 ProviderType:ProviderType::TreeView,
2739 Selector:json!([{ "viewId": req.view_id }]),
2740 SideCarIdentifier:"cocoon-main".to_string(),
2741 ExtensionIdentifier:json!(req.extension_id),
2742 Options:Some(json!({ "viewId": req.view_id })),
2743 };
2744 self.environment
2745 .ApplicationState
2746 .Extension
2747 .ProviderRegistration
2748 .RegisterProvider(Handle, dto);
2749
2750 let _ = self.environment.ApplicationHandle.emit(
2751 "sky://treeView/register",
2752 json!({ "viewId": req.view_id, "extensionId": req.extension_id }),
2753 );
2754
2755 Ok(Response::new(Empty {}))
2756 }
2757
2758 async fn get_tree_children(
2760 &self,
2761 request:Request<GetTreeChildrenRequest>,
2762 ) -> Result<Response<GetTreeChildrenResponse>, Status> {
2763 let req = request.into_inner();
2764 dev_log!("cocoon", "[CocoonService] get_tree_children: view={}", req.view_id);
2765
2766 Ok(Response::new(GetTreeChildrenResponse { items:Vec::new() }))
2771 }
2772
2773 async fn register_scm_provider(
2777 &self,
2778 request:Request<RegisterScmProviderRequest>,
2779 ) -> Result<Response<Empty>, Status> {
2780 let req = request.into_inner();
2781 dev_log!("cocoon", "[CocoonService] Registering SCM provider: {}", req.scm_id);
2782
2783 use tauri::Emitter;
2785
2786 let Handle = req
2787 .scm_id
2788 .as_bytes()
2789 .iter()
2790 .fold(0u32, |Acc, B| Acc.wrapping_mul(31).wrapping_add(*B as u32));
2791 let dto = ProviderRegistrationDTO {
2792 Handle,
2793 ProviderType:ProviderType::SourceControl,
2794 Selector:json!([{ "scmId": req.scm_id }]),
2795 SideCarIdentifier:"cocoon-main".to_string(),
2796 ExtensionIdentifier:json!(req.extension_id),
2797 Options:Some(json!({ "scmId": req.scm_id })),
2798 };
2799 self.environment
2800 .ApplicationState
2801 .Extension
2802 .ProviderRegistration
2803 .RegisterProvider(Handle, dto);
2804
2805 let _ = self.environment.ApplicationHandle.emit(
2806 "sky://scm/register",
2807 json!({ "scmId": req.scm_id, "extensionId": req.extension_id }),
2808 );
2809
2810 Ok(Response::new(Empty {}))
2811 }
2812
2813 async fn update_scm_group(&self, request:Request<UpdateScmGroupRequest>) -> Result<Response<Empty>, Status> {
2815 use tauri::Emitter;
2816
2817 let req = request.into_inner();
2818 dev_log!(
2819 "cocoon",
2820 "[CocoonService] update_scm_group: provider={} group={}",
2821 req.provider_id,
2822 req.group_id
2823 );
2824
2825 let ResourceStates:Vec<serde_json::Value> = req
2826 .resource_states
2827 .iter()
2828 .map(|Rs| {
2829 json!({
2830 "uri": Rs.uri.as_ref().map(|U| U.value.as_str()).unwrap_or(""),
2831 "decorations": Rs.decorations,
2832 })
2833 })
2834 .collect();
2835
2836 let _ = self.environment.ApplicationHandle.emit(
2837 "sky://scm/updateGroup",
2838 json!({
2839 "providerId": req.provider_id,
2840 "groupId": req.group_id,
2841 "resourceStates": ResourceStates,
2842 }),
2843 );
2844
2845 Ok(Response::new(Empty {}))
2846 }
2847
2848 async fn git_exec(&self, request:Request<GitExecRequest>) -> Result<Response<GitExecResponse>, Status> {
2854 let req = request.into_inner();
2855 dev_log!("cocoon", "[CocoonService] git_exec: {}", req.args.join(" "));
2856
2857 let WorkingDir = if req.repository_path.is_empty() {
2858 std::env::current_dir().unwrap_or_default()
2859 } else {
2860 std::path::PathBuf::from(&req.repository_path)
2861 };
2862
2863 let Output = tokio::process::Command::new("git")
2864 .args(&req.args)
2865 .current_dir(&WorkingDir)
2866 .output()
2867 .await
2868 .map_err(|Error| {
2869 dev_log!("cocoon", "error: [CocoonService] git_exec failed to spawn: {}", Error);
2870 Status::internal(format!("git_exec: failed to spawn git: {}", Error))
2871 })?;
2872
2873 let ExitCode = Output.status.code().unwrap_or(-1);
2874 dev_log!(
2875 "cocoon",
2876 "[CocoonService] git_exec exit={} stdout={} bytes stderr={} bytes",
2877 ExitCode,
2878 Output.stdout.len(),
2879 Output.stderr.len()
2880 );
2881
2882 let StdoutStr = String::from_utf8_lossy(&Output.stdout);
2885 let StderrStr = String::from_utf8_lossy(&Output.stderr);
2886 let mut OutputLines:Vec<String> = StdoutStr.lines().map(|L| L.to_string()).collect();
2887 for Line in StderrStr.lines() {
2888 OutputLines.push(format!("stderr: {}", Line));
2889 }
2890
2891 Ok(Response::new(GitExecResponse { output:OutputLines, exit_code:ExitCode }))
2892 }
2893
2894 async fn register_debug_adapter(
2898 &self,
2899 request:Request<RegisterDebugAdapterRequest>,
2900 ) -> Result<Response<Empty>, Status> {
2901 let req = request.into_inner();
2902 dev_log!("cocoon", "[CocoonService] Registering debug adapter: {}", req.debug_type);
2903
2904 use tauri::Emitter;
2906
2907 let Handle = req
2908 .debug_type
2909 .as_bytes()
2910 .iter()
2911 .fold(0u32, |Acc, B| Acc.wrapping_mul(31).wrapping_add(*B as u32));
2912 let dto = ProviderRegistrationDTO {
2913 Handle,
2914 ProviderType:ProviderType::DebugAdapter,
2915 Selector:json!([{ "debugType": req.debug_type }]),
2916 SideCarIdentifier:"cocoon-main".to_string(),
2917 ExtensionIdentifier:json!(req.extension_id),
2918 Options:Some(json!({ "debugType": req.debug_type })),
2919 };
2920 self.environment
2921 .ApplicationState
2922 .Extension
2923 .ProviderRegistration
2924 .RegisterProvider(Handle, dto);
2925
2926 let _ = self.environment.ApplicationHandle.emit(
2927 "sky://debug/register",
2928 json!({ "debugType": req.debug_type, "extensionId": req.extension_id }),
2929 );
2930
2931 Ok(Response::new(Empty {}))
2932 }
2933
2934 async fn start_debugging(
2936 &self,
2937 request:Request<StartDebuggingRequest>,
2938 ) -> Result<Response<StartDebuggingResponse>, Status> {
2939 use tauri::Emitter;
2940
2941 let req = request.into_inner();
2942 dev_log!("cocoon", "[CocoonService] start_debugging: type={}", req.debug_type);
2943
2944 let SessionId = format!(
2945 "debug-{}",
2946 SystemTime::now().duration_since(UNIX_EPOCH).map(|D| D.as_millis()).unwrap_or(0)
2947 );
2948
2949 let _ = self.environment.ApplicationHandle.emit(
2950 "sky://debug/start",
2951 json!({
2952 "sessionId": SessionId,
2953 "debugType": req.debug_type,
2954 "configuration": req.configuration.as_ref().map(|C| json!({
2955 "name": C.name,
2956 "type": C.r#type,
2957 "request": C.request,
2958 })),
2959 }),
2960 );
2961
2962 Ok(Response::new(StartDebuggingResponse { success:true }))
2963 }
2964
2965 async fn participate_in_save(
2969 &self,
2970 request:Request<ParticipateInSaveRequest>,
2971 ) -> Result<Response<ParticipateInSaveResponse>, Status> {
2972 let req = request.into_inner();
2973 dev_log!("cocoon", "[CocoonService] Participating in save for: {:?}", req.uri);
2974
2975 dev_log!("cocoon", "[CocoonService] Save reason: {:?}, uri: {:?}", req.reason, req.uri);
2981
2982 Ok(Response::new(ParticipateInSaveResponse { edits:Vec::new() }))
2983 }
2984
2985 async fn get_secret(&self, request:Request<GetSecretRequest>) -> Result<Response<GetSecretResponse>, Status> {
2989 let req = request.into_inner();
2990 dev_log!("cocoon", "[CocoonService] get_secret: key={}", req.key);
2991
2992 match self.environment.GetSecret(String::new(), req.key.clone()).await {
2995 Ok(Some(Value)) => Ok(Response::new(GetSecretResponse { value:Value })),
2996 Ok(None) => Ok(Response::new(GetSecretResponse { value:String::new() })),
2997 Err(Error) => {
2998 dev_log!("cocoon", "warn: [CocoonService] get_secret failed key={}: {}", req.key, Error);
2999 Err(Status::internal(format!("get_secret: {}", Error)))
3000 },
3001 }
3002 }
3003
3004 async fn store_secret(&self, request:Request<StoreSecretRequest>) -> Result<Response<Empty>, Status> {
3006 let req = request.into_inner();
3007 dev_log!("cocoon", "[CocoonService] store_secret: key={}", req.key);
3008
3009 match self.environment.StoreSecret(String::new(), req.key.clone(), req.value).await {
3010 Ok(()) => Ok(Response::new(Empty {})),
3011 Err(Error) => {
3012 dev_log!("cocoon", "warn: [CocoonService] store_secret failed key={}: {}", req.key, Error);
3013 Err(Status::internal(format!("store_secret: {}", Error)))
3014 },
3015 }
3016 }
3017
3018 async fn delete_secret(&self, request:Request<DeleteSecretRequest>) -> Result<Response<Empty>, Status> {
3020 let req = request.into_inner();
3021 dev_log!("cocoon", "[CocoonService] delete_secret: key={}", req.key);
3022
3023 match self.environment.DeleteSecret(String::new(), req.key.clone()).await {
3024 Ok(()) => Ok(Response::new(Empty {})),
3025 Err(Error) => {
3026 dev_log!(
3027 "cocoon",
3028 "warn: [CocoonService] delete_secret failed key={}: {}",
3029 req.key,
3030 Error
3031 );
3032 Err(Status::internal(format!("delete_secret: {}", Error)))
3033 },
3034 }
3035 }
3036
3037 async fn register_document_highlight_provider(
3041 &self,
3042 request:Request<RegisterProviderRequest>,
3043 ) -> Result<Response<Empty>, Status> {
3044 let req = request.into_inner();
3045 dev_log!("cocoon", "[CocoonService] Registering Document Highlight Provider");
3046 self.RegisterProvider(
3047 req.handle,
3048 ProviderType::DocumentHighlight,
3049 &req.language_selector,
3050 &req.extension_id,
3051 );
3052 Ok(Response::new(Empty {}))
3053 }
3054
3055 async fn provide_document_highlights(
3057 &self,
3058 request:Request<ProvideDocumentHighlightsRequest>,
3059 ) -> Result<Response<ProvideDocumentHighlightsResponse>, Status> {
3060 let req = request.into_inner();
3061 dev_log!("cocoon", "[CocoonService] Providing document highlights");
3062
3063 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3064 let document_uri =
3065 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3066 let position = req.position.as_ref();
3067 let position_dto = PositionDTO {
3068 LineNumber:position.map(|p| p.line).unwrap_or(0),
3069 Column:position.map(|p| p.character).unwrap_or(0),
3070 };
3071
3072 match self.environment.ProvideDocumentHighlights(document_uri, position_dto).await {
3073 Ok(_result) => Ok(Response::new(ProvideDocumentHighlightsResponse::default())),
3074 Err(e) => Err(Status::internal(format!("Document highlights failed: {}", e))),
3075 }
3076 }
3077
3078 async fn register_document_symbol_provider(
3080 &self,
3081 request:Request<RegisterProviderRequest>,
3082 ) -> Result<Response<Empty>, Status> {
3083 let req = request.into_inner();
3084 dev_log!("cocoon", "[CocoonService] Registering Document Symbol Provider");
3085 self.RegisterProvider(
3086 req.handle,
3087 ProviderType::DocumentSymbol,
3088 &req.language_selector,
3089 &req.extension_id,
3090 );
3091 Ok(Response::new(Empty {}))
3092 }
3093
3094 async fn provide_document_symbols(
3096 &self,
3097 request:Request<ProvideDocumentSymbolsRequest>,
3098 ) -> Result<Response<ProvideDocumentSymbolsResponse>, Status> {
3099 let req = request.into_inner();
3100 dev_log!("cocoon", "[CocoonService] Providing document symbols");
3101
3102 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3103 let document_uri =
3104 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3105
3106 match self.environment.ProvideDocumentSymbols(document_uri).await {
3107 Ok(_result) => Ok(Response::new(ProvideDocumentSymbolsResponse::default())),
3108 Err(e) => Err(Status::internal(format!("Document symbols failed: {}", e))),
3109 }
3110 }
3111
3112 async fn register_workspace_symbol_provider(
3114 &self,
3115 request:Request<RegisterProviderRequest>,
3116 ) -> Result<Response<Empty>, Status> {
3117 let req = request.into_inner();
3118 dev_log!("cocoon", "[CocoonService] Registering Workspace Symbol Provider");
3119 self.RegisterProvider(
3120 req.handle,
3121 ProviderType::WorkspaceSymbol,
3122 &req.language_selector,
3123 &req.extension_id,
3124 );
3125 Ok(Response::new(Empty {}))
3126 }
3127
3128 async fn provide_workspace_symbols(
3130 &self,
3131 request:Request<ProvideWorkspaceSymbolsRequest>,
3132 ) -> Result<Response<ProvideWorkspaceSymbolsResponse>, Status> {
3133 let req = request.into_inner();
3134 dev_log!("cocoon", "[CocoonService] Providing workspace symbols for query: {}", req.query);
3135
3136 match self.environment.ProvideWorkspaceSymbols(req.query).await {
3137 Ok(_result) => Ok(Response::new(ProvideWorkspaceSymbolsResponse::default())),
3138 Err(e) => Err(Status::internal(format!("Workspace symbols failed: {}", e))),
3139 }
3140 }
3141
3142 async fn register_rename_provider(
3144 &self,
3145 request:Request<RegisterProviderRequest>,
3146 ) -> Result<Response<Empty>, Status> {
3147 let req = request.into_inner();
3148 dev_log!("cocoon", "[CocoonService] Registering Rename Provider");
3149 self.RegisterProvider(req.handle, ProviderType::Rename, &req.language_selector, &req.extension_id);
3150 Ok(Response::new(Empty {}))
3151 }
3152
3153 async fn provide_rename_edits(
3155 &self,
3156 request:Request<ProvideRenameEditsRequest>,
3157 ) -> Result<Response<ProvideRenameEditsResponse>, Status> {
3158 let req = request.into_inner();
3159 dev_log!("cocoon", "[CocoonService] Providing rename edits: new_name={}", req.new_name);
3160
3161 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3162 let document_uri =
3163 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3164 let position = req.position.as_ref();
3165 let position_dto = PositionDTO {
3166 LineNumber:position.map(|p| p.line).unwrap_or(0),
3167 Column:position.map(|p| p.character).unwrap_or(0),
3168 };
3169
3170 match self
3171 .environment
3172 .ProvideRenameEdits(document_uri, position_dto, req.new_name)
3173 .await
3174 {
3175 Ok(_result) => Ok(Response::new(ProvideRenameEditsResponse::default())),
3176 Err(e) => Err(Status::internal(format!("Rename edits failed: {}", e))),
3177 }
3178 }
3179
3180 async fn register_document_formatting_provider(
3182 &self,
3183 request:Request<RegisterProviderRequest>,
3184 ) -> Result<Response<Empty>, Status> {
3185 let req = request.into_inner();
3186 dev_log!("cocoon", "[CocoonService] Registering Document Formatting Provider");
3187 self.RegisterProvider(
3188 req.handle,
3189 ProviderType::DocumentFormatting,
3190 &req.language_selector,
3191 &req.extension_id,
3192 );
3193 Ok(Response::new(Empty {}))
3194 }
3195
3196 async fn provide_document_formatting(
3198 &self,
3199 request:Request<ProvideDocumentFormattingRequest>,
3200 ) -> Result<Response<ProvideDocumentFormattingResponse>, Status> {
3201 let req = request.into_inner();
3202 dev_log!("cocoon", "[CocoonService] Providing document formatting");
3203
3204 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3205 let document_uri =
3206 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3207 let options_dto = json!({ "tabSize": 4, "insertSpaces": true });
3208
3209 match self.environment.ProvideDocumentFormattingEdits(document_uri, options_dto).await {
3210 Ok(_result) => Ok(Response::new(ProvideDocumentFormattingResponse::default())),
3211 Err(e) => Err(Status::internal(format!("Document formatting failed: {}", e))),
3212 }
3213 }
3214
3215 async fn register_document_range_formatting_provider(
3217 &self,
3218 request:Request<RegisterProviderRequest>,
3219 ) -> Result<Response<Empty>, Status> {
3220 let req = request.into_inner();
3221 dev_log!("cocoon", "[CocoonService] Registering Document Range Formatting Provider");
3222 self.RegisterProvider(
3223 req.handle,
3224 ProviderType::DocumentRangeFormatting,
3225 &req.language_selector,
3226 &req.extension_id,
3227 );
3228 Ok(Response::new(Empty {}))
3229 }
3230
3231 async fn provide_document_range_formatting(
3233 &self,
3234 request:Request<ProvideDocumentRangeFormattingRequest>,
3235 ) -> Result<Response<ProvideDocumentRangeFormattingResponse>, Status> {
3236 let req = request.into_inner();
3237 dev_log!("cocoon", "[CocoonService] Providing document range formatting");
3238
3239 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3240 let document_uri =
3241 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3242 let range = req.range.as_ref();
3243 let range_dto = json!({
3244 "StartLineNumber": range.and_then(|r| r.start.as_ref()).map(|p| p.line).unwrap_or(0),
3245 "StartColumn": range.and_then(|r| r.start.as_ref()).map(|p| p.character).unwrap_or(0),
3246 "EndLineNumber": range.and_then(|r| r.end.as_ref()).map(|p| p.line).unwrap_or(0),
3247 "EndColumn": range.and_then(|r| r.end.as_ref()).map(|p| p.character).unwrap_or(0),
3248 });
3249 let options_dto = json!({ "tabSize": 4, "insertSpaces": true });
3250
3251 match self
3252 .environment
3253 .ProvideDocumentRangeFormattingEdits(document_uri, range_dto, options_dto)
3254 .await
3255 {
3256 Ok(_result) => Ok(Response::new(ProvideDocumentRangeFormattingResponse::default())),
3257 Err(e) => Err(Status::internal(format!("Document range formatting failed: {}", e))),
3258 }
3259 }
3260
3261 async fn register_on_type_formatting_provider(
3263 &self,
3264 request:Request<RegisterOnTypeFormattingProviderRequest>,
3265 ) -> Result<Response<Empty>, Status> {
3266 let req = request.into_inner();
3267 dev_log!("cocoon", "[CocoonService] Registering On Type Formatting Provider");
3268 self.RegisterProvider(
3269 req.handle,
3270 ProviderType::OnTypeFormatting,
3271 &req.language_selector,
3272 &req.extension_id,
3273 );
3274 Ok(Response::new(Empty {}))
3275 }
3276
3277 async fn provide_on_type_formatting(
3279 &self,
3280 request:Request<ProvideOnTypeFormattingRequest>,
3281 ) -> Result<Response<ProvideOnTypeFormattingResponse>, Status> {
3282 let req = request.into_inner();
3283 dev_log!("cocoon", "[CocoonService] Providing on-type formatting");
3284
3285 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3286 let document_uri =
3287 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3288 let position = req.position.as_ref();
3289 let position_dto = PositionDTO {
3290 LineNumber:position.map(|p| p.line).unwrap_or(0),
3291 Column:position.map(|p| p.character).unwrap_or(0),
3292 };
3293 let options_dto = json!({ "tabSize": 4, "insertSpaces": true });
3294
3295 match self
3296 .environment
3297 .ProvideOnTypeFormattingEdits(document_uri, position_dto, req.character, options_dto)
3298 .await
3299 {
3300 Ok(_result) => Ok(Response::new(ProvideOnTypeFormattingResponse::default())),
3301 Err(e) => Err(Status::internal(format!("On-type formatting failed: {}", e))),
3302 }
3303 }
3304
3305 async fn register_signature_help_provider(
3307 &self,
3308 request:Request<RegisterSignatureHelpProviderRequest>,
3309 ) -> Result<Response<Empty>, Status> {
3310 let req = request.into_inner();
3311 dev_log!("cocoon", "[CocoonService] Registering Signature Help Provider");
3312 self.RegisterProvider(
3313 req.handle,
3314 ProviderType::SignatureHelp,
3315 &req.language_selector,
3316 &req.extension_id,
3317 );
3318 Ok(Response::new(Empty {}))
3319 }
3320
3321 async fn provide_signature_help(
3323 &self,
3324 request:Request<ProvideSignatureHelpRequest>,
3325 ) -> Result<Response<ProvideSignatureHelpResponse>, Status> {
3326 let req = request.into_inner();
3327 dev_log!("cocoon", "[CocoonService] Providing signature help");
3328
3329 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3330 let document_uri =
3331 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3332 let position = req.position.as_ref();
3333 let position_dto = PositionDTO {
3334 LineNumber:position.map(|p| p.line).unwrap_or(0),
3335 Column:position.map(|p| p.character).unwrap_or(0),
3336 };
3337 let context_dto = json!({ "triggerKind": 1, "isRetrigger": false });
3338
3339 match self
3340 .environment
3341 .ProvideSignatureHelp(document_uri, position_dto, context_dto)
3342 .await
3343 {
3344 Ok(_result) => Ok(Response::new(ProvideSignatureHelpResponse::default())),
3345 Err(e) => Err(Status::internal(format!("Signature help failed: {}", e))),
3346 }
3347 }
3348
3349 async fn register_code_lens_provider(
3351 &self,
3352 request:Request<RegisterProviderRequest>,
3353 ) -> Result<Response<Empty>, Status> {
3354 let req = request.into_inner();
3355 dev_log!("cocoon", "[CocoonService] Registering Code Lens Provider");
3356 self.RegisterProvider(req.handle, ProviderType::CodeLens, &req.language_selector, &req.extension_id);
3357 Ok(Response::new(Empty {}))
3358 }
3359
3360 async fn provide_code_lenses(
3362 &self,
3363 request:Request<ProvideCodeLensesRequest>,
3364 ) -> Result<Response<ProvideCodeLensesResponse>, Status> {
3365 let req = request.into_inner();
3366 dev_log!("cocoon", "[CocoonService] Providing code lenses");
3367
3368 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3369 let document_uri =
3370 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3371
3372 match self.environment.ProvideCodeLenses(document_uri).await {
3373 Ok(_result) => Ok(Response::new(ProvideCodeLensesResponse::default())),
3374 Err(e) => Err(Status::internal(format!("Code lenses failed: {}", e))),
3375 }
3376 }
3377
3378 async fn register_folding_range_provider(
3380 &self,
3381 request:Request<RegisterProviderRequest>,
3382 ) -> Result<Response<Empty>, Status> {
3383 let req = request.into_inner();
3384 dev_log!("cocoon", "[CocoonService] Registering Folding Range Provider");
3385 self.RegisterProvider(
3386 req.handle,
3387 ProviderType::FoldingRange,
3388 &req.language_selector,
3389 &req.extension_id,
3390 );
3391 Ok(Response::new(Empty {}))
3392 }
3393
3394 async fn provide_folding_ranges(
3396 &self,
3397 request:Request<ProvideFoldingRangesRequest>,
3398 ) -> Result<Response<ProvideFoldingRangesResponse>, Status> {
3399 let req = request.into_inner();
3400 dev_log!("cocoon", "[CocoonService] Providing folding ranges");
3401
3402 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3403 let document_uri =
3404 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3405
3406 match self.environment.ProvideFoldingRanges(document_uri).await {
3407 Ok(_result) => Ok(Response::new(ProvideFoldingRangesResponse::default())),
3408 Err(e) => Err(Status::internal(format!("Folding ranges failed: {}", e))),
3409 }
3410 }
3411
3412 async fn register_selection_range_provider(
3414 &self,
3415 request:Request<RegisterProviderRequest>,
3416 ) -> Result<Response<Empty>, Status> {
3417 let req = request.into_inner();
3418 dev_log!("cocoon", "[CocoonService] Registering Selection Range Provider");
3419 self.RegisterProvider(
3420 req.handle,
3421 ProviderType::SelectionRange,
3422 &req.language_selector,
3423 &req.extension_id,
3424 );
3425 Ok(Response::new(Empty {}))
3426 }
3427
3428 async fn provide_selection_ranges(
3430 &self,
3431 request:Request<ProvideSelectionRangesRequest>,
3432 ) -> Result<Response<ProvideSelectionRangesResponse>, Status> {
3433 let req = request.into_inner();
3434 dev_log!("cocoon", "[CocoonService] Providing selection ranges");
3435
3436 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3437 let document_uri =
3438 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3439 let PositionDTOs:Vec<PositionDTO> = req
3440 .positions
3441 .iter()
3442 .map(|P| PositionDTO { LineNumber:P.line, Column:P.character })
3443 .collect();
3444
3445 match self.environment.ProvideSelectionRanges(document_uri, PositionDTOs).await {
3446 Ok(_result) => Ok(Response::new(ProvideSelectionRangesResponse::default())),
3447 Err(e) => Err(Status::internal(format!("Selection ranges failed: {}", e))),
3448 }
3449 }
3450
3451 async fn register_semantic_tokens_provider(
3453 &self,
3454 request:Request<RegisterSemanticTokensProviderRequest>,
3455 ) -> Result<Response<Empty>, Status> {
3456 let req = request.into_inner();
3457 dev_log!("cocoon", "[CocoonService] Registering Semantic Tokens Provider");
3458 self.RegisterProvider(
3459 req.handle,
3460 ProviderType::SemanticTokens,
3461 &req.language_selector,
3462 &req.extension_id,
3463 );
3464 Ok(Response::new(Empty {}))
3465 }
3466
3467 async fn provide_semantic_tokens_full(
3469 &self,
3470 request:Request<ProvideSemanticTokensRequest>,
3471 ) -> Result<Response<ProvideSemanticTokensResponse>, Status> {
3472 let req = request.into_inner();
3473 dev_log!("cocoon", "[CocoonService] Providing semantic tokens");
3474
3475 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3476 let document_uri =
3477 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3478
3479 match self.environment.ProvideSemanticTokensFull(document_uri).await {
3480 Ok(_result) => Ok(Response::new(ProvideSemanticTokensResponse::default())),
3481 Err(e) => Err(Status::internal(format!("Semantic tokens failed: {}", e))),
3482 }
3483 }
3484
3485 async fn register_inlay_hints_provider(
3487 &self,
3488 request:Request<RegisterProviderRequest>,
3489 ) -> Result<Response<Empty>, Status> {
3490 let req = request.into_inner();
3491 dev_log!("cocoon", "[CocoonService] Registering Inlay Hints Provider");
3492 self.RegisterProvider(req.handle, ProviderType::InlayHint, &req.language_selector, &req.extension_id);
3493 Ok(Response::new(Empty {}))
3494 }
3495
3496 async fn provide_inlay_hints(
3498 &self,
3499 request:Request<ProvideInlayHintsRequest>,
3500 ) -> Result<Response<ProvideInlayHintsResponse>, Status> {
3501 let req = request.into_inner();
3502 dev_log!("cocoon", "[CocoonService] Providing inlay hints");
3503
3504 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3505 let document_uri =
3506 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3507 let range = req.range.as_ref();
3508 let range_dto = json!({
3509 "StartLineNumber": range.and_then(|r| r.start.as_ref()).map(|p| p.line).unwrap_or(0),
3510 "StartColumn": range.and_then(|r| r.start.as_ref()).map(|p| p.character).unwrap_or(0),
3511 "EndLineNumber": range.and_then(|r| r.end.as_ref()).map(|p| p.line).unwrap_or(0),
3512 "EndColumn": range.and_then(|r| r.end.as_ref()).map(|p| p.character).unwrap_or(0),
3513 });
3514
3515 match self.environment.ProvideInlayHints(document_uri, range_dto).await {
3516 Ok(_result) => Ok(Response::new(ProvideInlayHintsResponse::default())),
3517 Err(e) => Err(Status::internal(format!("Inlay hints failed: {}", e))),
3518 }
3519 }
3520
3521 async fn register_type_hierarchy_provider(
3523 &self,
3524 request:Request<RegisterProviderRequest>,
3525 ) -> Result<Response<Empty>, Status> {
3526 let req = request.into_inner();
3527 dev_log!("cocoon", "[CocoonService] Registering Type Hierarchy Provider");
3528 self.RegisterProvider(
3529 req.handle,
3530 ProviderType::TypeHierarchy,
3531 &req.language_selector,
3532 &req.extension_id,
3533 );
3534 Ok(Response::new(Empty {}))
3535 }
3536
3537 async fn provide_type_hierarchy_supertypes(
3539 &self,
3540 request:Request<ProvideTypeHierarchyRequest>,
3541 ) -> Result<Response<ProvideTypeHierarchyResponse>, Status> {
3542 let req = request.into_inner();
3543 dev_log!("cocoon", "[CocoonService] Providing type hierarchy supertypes");
3544
3545 let item_dto = json!({
3546 "name": req.item.as_ref().map(|i| i.name.as_str()).unwrap_or(""),
3547 "uri": req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or(""),
3548 });
3549 match self.environment.ProvideTypeHierarchySupertypes(item_dto).await {
3550 Ok(_result) => Ok(Response::new(ProvideTypeHierarchyResponse::default())),
3551 Err(e) => Err(Status::internal(format!("Type hierarchy supertypes failed: {}", e))),
3552 }
3553 }
3554
3555 async fn provide_type_hierarchy_subtypes(
3557 &self,
3558 request:Request<ProvideTypeHierarchyRequest>,
3559 ) -> Result<Response<ProvideTypeHierarchyResponse>, Status> {
3560 let req = request.into_inner();
3561 dev_log!("cocoon", "[CocoonService] Providing type hierarchy subtypes");
3562
3563 let item_dto = json!({
3564 "name": req.item.as_ref().map(|i| i.name.as_str()).unwrap_or(""),
3565 "uri": req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or(""),
3566 });
3567 match self.environment.ProvideTypeHierarchySubtypes(item_dto).await {
3568 Ok(_result) => Ok(Response::new(ProvideTypeHierarchyResponse::default())),
3569 Err(e) => Err(Status::internal(format!("Type hierarchy subtypes failed: {}", e))),
3570 }
3571 }
3572
3573 async fn register_call_hierarchy_provider(
3575 &self,
3576 request:Request<RegisterProviderRequest>,
3577 ) -> Result<Response<Empty>, Status> {
3578 let req = request.into_inner();
3579 dev_log!("cocoon", "[CocoonService] Registering Call Hierarchy Provider");
3580 self.RegisterProvider(
3581 req.handle,
3582 ProviderType::CallHierarchy,
3583 &req.language_selector,
3584 &req.extension_id,
3585 );
3586 Ok(Response::new(Empty {}))
3587 }
3588
3589 async fn provide_call_hierarchy_incoming_calls(
3591 &self,
3592 request:Request<ProvideCallHierarchyRequest>,
3593 ) -> Result<Response<ProvideCallHierarchyResponse>, Status> {
3594 let req = request.into_inner();
3595 dev_log!("cocoon", "[CocoonService] Providing call hierarchy incoming");
3596
3597 let item_dto = json!({
3598 "name": req.item.as_ref().map(|i| i.name.as_str()).unwrap_or(""),
3599 "uri": req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or(""),
3600 });
3601 match self.environment.ProvideCallHierarchyIncomingCalls(item_dto).await {
3602 Ok(_result) => Ok(Response::new(ProvideCallHierarchyResponse::default())),
3603 Err(e) => Err(Status::internal(format!("Call hierarchy incoming failed: {}", e))),
3604 }
3605 }
3606
3607 async fn provide_call_hierarchy_outgoing_calls(
3609 &self,
3610 request:Request<ProvideCallHierarchyRequest>,
3611 ) -> Result<Response<ProvideCallHierarchyResponse>, Status> {
3612 let req = request.into_inner();
3613 dev_log!("cocoon", "[CocoonService] Providing call hierarchy outgoing");
3614
3615 let item_dto = json!({
3616 "name": req.item.as_ref().map(|i| i.name.as_str()).unwrap_or(""),
3617 "uri": req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or(""),
3618 });
3619 match self.environment.ProvideCallHierarchyOutgoingCalls(item_dto).await {
3620 Ok(_result) => Ok(Response::new(ProvideCallHierarchyResponse::default())),
3621 Err(e) => Err(Status::internal(format!("Call hierarchy outgoing failed: {}", e))),
3622 }
3623 }
3624
3625 async fn register_linked_editing_range_provider(
3627 &self,
3628 request:Request<RegisterProviderRequest>,
3629 ) -> Result<Response<Empty>, Status> {
3630 let req = request.into_inner();
3631 dev_log!("cocoon", "[CocoonService] Registering Linked Editing Range Provider");
3632 self.RegisterProvider(
3633 req.handle,
3634 ProviderType::LinkedEditingRange,
3635 &req.language_selector,
3636 &req.extension_id,
3637 );
3638 Ok(Response::new(Empty {}))
3639 }
3640
3641 async fn provide_linked_editing_ranges(
3643 &self,
3644 request:Request<ProvideLinkedEditingRangesRequest>,
3645 ) -> Result<Response<ProvideLinkedEditingRangesResponse>, Status> {
3646 let req = request.into_inner();
3647 dev_log!("cocoon", "[CocoonService] Providing linked editing ranges");
3648
3649 let uri_string = req.uri.as_ref().map(|u| u.value.as_str()).unwrap_or("");
3650 let document_uri =
3651 Url::parse(uri_string).map_err(|e| Status::invalid_argument(format!("Invalid URI: {}", e)))?;
3652 let position = req.position.as_ref();
3653 let position_dto = PositionDTO {
3654 LineNumber:position.map(|p| p.line).unwrap_or(0),
3655 Column:position.map(|p| p.character).unwrap_or(0),
3656 };
3657
3658 match self.environment.ProvideLinkedEditingRanges(document_uri, position_dto).await {
3659 Ok(_result) => Ok(Response::new(ProvideLinkedEditingRangesResponse::default())),
3660 Err(e) => Err(Status::internal(format!("Linked editing ranges failed: {}", e))),
3661 }
3662 }
3663
3664 async fn show_quick_pick(
3666 &self,
3667 request:Request<ShowQuickPickRequest>,
3668 ) -> Result<Response<ShowQuickPickResponse>, Status> {
3669 let req = request.into_inner();
3670 dev_log!("cocoon", "[CocoonService] show_quick_pick: {} items", req.items.len());
3671
3672 let Items:Vec<QuickPickItemDTO> = req
3673 .items
3674 .iter()
3675 .map(|Item| {
3676 QuickPickItemDTO {
3677 Label:Item.label.clone(),
3678 Description:if Item.description.is_empty() { None } else { Some(Item.description.clone()) },
3679 Detail:None,
3680 Picked:Some(Item.picked),
3681 AlwaysShow:None,
3682 }
3683 })
3684 .collect();
3685
3686 let Options = Some(QuickPickOptionsDTO {
3687 Title:if req.title.is_empty() { None } else { Some(req.title) },
3688 PlaceHolder:if req.placeholder.is_empty() { None } else { Some(req.placeholder) },
3689 CanPickMany:Some(req.can_pick_many),
3690 IgnoreFocusOut:None,
3691 });
3692
3693 match self.environment.ShowQuickPick(Items, Options).await {
3694 Ok(Some(Selected)) => {
3695 let SelectedIndices:Vec<u32> = Selected
3697 .iter()
3698 .filter_map(|Label| req.items.iter().position(|Item| &Item.label == Label).map(|Idx| Idx as u32))
3699 .collect();
3700 Ok(Response::new(ShowQuickPickResponse { selected_indices:SelectedIndices }))
3701 },
3702 Ok(None) => Ok(Response::new(ShowQuickPickResponse::default())),
3703 Err(Error) => {
3704 dev_log!("cocoon", "warn: [CocoonService] show_quick_pick failed: {}", Error);
3705 Ok(Response::new(ShowQuickPickResponse::default()))
3706 },
3707 }
3708 }
3709
3710 async fn show_input_box(
3712 &self,
3713 request:Request<ShowInputBoxRequest>,
3714 ) -> Result<Response<ShowInputBoxResponse>, Status> {
3715 let req = request.into_inner();
3716 dev_log!("cocoon", "[CocoonService] show_input_box");
3717
3718 let Options = Some(InputBoxOptionsDTO {
3719 Title:if req.title.is_empty() { None } else { Some(req.title) },
3720 PlaceHolder:if req.placeholder.is_empty() { None } else { Some(req.placeholder) },
3721 Value:if req.value.is_empty() { None } else { Some(req.value) },
3722 Prompt:if req.prompt.is_empty() { None } else { Some(req.prompt) },
3723 IsPassword:if req.password { Some(true) } else { None },
3724 IgnoreFocusOut:None,
3725 });
3726
3727 match self.environment.ShowInputBox(Options).await {
3728 Ok(Some(Value)) => Ok(Response::new(ShowInputBoxResponse { value:Value, cancelled:false })),
3729 Ok(None) => Ok(Response::new(ShowInputBoxResponse { value:String::new(), cancelled:true })),
3730 Err(Error) => {
3731 dev_log!("cocoon", "warn: [CocoonService] show_input_box failed: {}", Error);
3732 Ok(Response::new(ShowInputBoxResponse { value:String::new(), cancelled:true }))
3733 },
3734 }
3735 }
3736
3737 async fn show_progress(
3739 &self,
3740 request:Request<ShowProgressRequest>,
3741 ) -> Result<Response<ShowProgressResponse>, Status> {
3742 use tauri::Emitter;
3743
3744 let req = request.into_inner();
3745 dev_log!("cocoon", "[CocoonService] show_progress: title={}", req.title);
3746
3747 let Handle = SystemTime::now()
3748 .duration_since(UNIX_EPOCH)
3749 .map(|D| D.as_millis() as u32)
3750 .unwrap_or(0);
3751
3752 let _ = self.environment.ApplicationHandle.emit(
3753 "sky://progress/start",
3754 json!({
3755 "handle": Handle,
3756 "title": req.title,
3757 "cancellable": req.cancellable,
3758 "location": req.location,
3759 }),
3760 );
3761
3762 Ok(Response::new(ShowProgressResponse { handle:Handle }))
3763 }
3764
3765 async fn report_progress(&self, request:Request<ReportProgressRequest>) -> Result<Response<Empty>, Status> {
3767 use tauri::Emitter;
3768
3769 let req = request.into_inner();
3770 dev_log!("cocoon", "[CocoonService] report_progress: handle={}", req.handle);
3771
3772 let _ = self.environment.ApplicationHandle.emit(
3773 "sky://progress/update",
3774 json!({
3775 "handle": req.handle,
3776 "message": req.message,
3777 "increment": req.increment,
3778 }),
3779 );
3780
3781 Ok(Response::new(Empty {}))
3782 }
3783
3784 async fn post_webview_message(
3786 &self,
3787 request:Request<PostWebviewMessageRequest>,
3788 ) -> Result<Response<Empty>, Status> {
3789 use tauri::Emitter;
3790
3791 let req = request.into_inner();
3792 dev_log!("cocoon", "[CocoonService] post_webview_message: handle={}", req.handle);
3793
3794 let MessagePayload = match &req.message {
3795 Some(post_webview_message_request::Message::StringMessage(S)) => json!(S),
3796 Some(post_webview_message_request::Message::BytesMessage(B)) => json!(B),
3797 None => serde_json::Value::Null,
3798 };
3799
3800 let _ = self.environment.ApplicationHandle.emit(
3801 "sky://webview/postMessage",
3802 json!({
3803 "handle": req.handle,
3804 "message": MessagePayload,
3805 }),
3806 );
3807
3808 Ok(Response::new(Empty {}))
3809 }
3810
3811 async fn dispose_webview_panel(
3813 &self,
3814 request:Request<DisposeWebviewPanelRequest>,
3815 ) -> Result<Response<Empty>, Status> {
3816 use tauri::Emitter;
3817
3818 let req = request.into_inner();
3819 dev_log!("cocoon", "[CocoonService] dispose_webview_panel: handle={}", req.handle);
3820
3821 let _ = self
3822 .environment
3823 .ApplicationHandle
3824 .emit("sky://webview/dispose", json!({ "handle": req.handle }));
3825
3826 Ok(Response::new(Empty {}))
3827 }
3828
3829 async fn open_external(&self, request:Request<OpenExternalRequest>) -> Result<Response<Empty>, Status> {
3831 use tauri::Emitter;
3832
3833 let req = request.into_inner();
3834 dev_log!("cocoon", "[CocoonService] open_external: {}", req.uri);
3835
3836 let _ = self
3837 .environment
3838 .ApplicationHandle
3839 .emit("sky://native/openExternal", json!({ "url": req.uri }));
3840
3841 Ok(Response::new(Empty {}))
3842 }
3843
3844 async fn delete_file(&self, request:Request<DeleteFileRequest>) -> Result<Response<Empty>, Status> {
3846 let req = request.into_inner();
3847 let Path =
3848 Self::UriToPath(req.uri.as_ref()).ok_or_else(|| Status::invalid_argument("delete_file: missing URI"))?;
3849
3850 dev_log!("cocoon", "[CocoonService] delete_file: {:?}", Path);
3851
3852 if Path.is_dir() {
3853 tokio::fs::remove_dir_all(&Path).await
3854 } else {
3855 tokio::fs::remove_file(&Path).await
3856 }
3857 .map_err(|Error| Status::internal(format!("delete_file: {}: {}", Path.display(), Error)))?;
3858
3859 Ok(Response::new(Empty {}))
3860 }
3861
3862 async fn rename_file(&self, request:Request<RenameFileRequest>) -> Result<Response<Empty>, Status> {
3864 let req = request.into_inner();
3865 let OldPath = Self::UriToPath(req.source.as_ref())
3866 .ok_or_else(|| Status::invalid_argument("rename_file: missing source URI"))?;
3867 let NewPath = Self::UriToPath(req.target.as_ref())
3868 .ok_or_else(|| Status::invalid_argument("rename_file: missing target URI"))?;
3869
3870 dev_log!("cocoon", "[CocoonService] rename_file: {:?} → {:?}", OldPath, NewPath);
3871
3872 if let Some(Parent) = NewPath.parent() {
3873 if !Parent.as_os_str().is_empty() {
3874 tokio::fs::create_dir_all(Parent)
3875 .await
3876 .map_err(|Error| Status::internal(format!("rename_file: create_dir_all failed: {}", Error)))?;
3877 }
3878 }
3879
3880 tokio::fs::rename(&OldPath, &NewPath)
3881 .await
3882 .map_err(|Error| Status::internal(format!("rename_file: {}: {}", OldPath.display(), Error)))?;
3883
3884 Ok(Response::new(Empty {}))
3885 }
3886
3887 async fn copy_file(&self, request:Request<CopyFileRequest>) -> Result<Response<Empty>, Status> {
3889 let req = request.into_inner();
3890 let SrcPath = Self::UriToPath(req.source.as_ref())
3891 .ok_or_else(|| Status::invalid_argument("copy_file: missing source URI"))?;
3892 let DstPath = Self::UriToPath(req.target.as_ref())
3893 .ok_or_else(|| Status::invalid_argument("copy_file: missing target URI"))?;
3894
3895 dev_log!("cocoon", "[CocoonService] copy_file: {:?} → {:?}", SrcPath, DstPath);
3896
3897 if let Some(Parent) = DstPath.parent() {
3898 if !Parent.as_os_str().is_empty() {
3899 tokio::fs::create_dir_all(Parent)
3900 .await
3901 .map_err(|Error| Status::internal(format!("copy_file: create_dir_all failed: {}", Error)))?;
3902 }
3903 }
3904
3905 tokio::fs::copy(&SrcPath, &DstPath)
3906 .await
3907 .map_err(|Error| Status::internal(format!("copy_file: {}: {}", SrcPath.display(), Error)))?;
3908
3909 Ok(Response::new(Empty {}))
3910 }
3911
3912 async fn create_directory(&self, request:Request<CreateDirectoryRequest>) -> Result<Response<Empty>, Status> {
3914 let req = request.into_inner();
3915 let Path = Self::UriToPath(req.uri.as_ref())
3916 .ok_or_else(|| Status::invalid_argument("create_directory: missing URI"))?;
3917
3918 dev_log!("cocoon", "[CocoonService] create_directory: {:?}", Path);
3919
3920 tokio::fs::create_dir_all(&Path)
3921 .await
3922 .map_err(|Error| Status::internal(format!("create_directory: {}: {}", Path.display(), Error)))?;
3923
3924 Ok(Response::new(Empty {}))
3925 }
3926
3927 async fn create_output_channel(
3929 &self,
3930 request:Request<CreateOutputChannelRequest>,
3931 ) -> Result<Response<CreateOutputChannelResponse>, Status> {
3932 use tauri::Emitter;
3933
3934 let req = request.into_inner();
3935 dev_log!("cocoon", "[CocoonService] create_output_channel: '{}'", req.name);
3936
3937 let _ = self
3938 .environment
3939 .ApplicationHandle
3940 .emit("sky://output/create", json!({ "channel": req.name }));
3941
3942 Ok(Response::new(CreateOutputChannelResponse { channel_id:req.name.clone() }))
3943 }
3944
3945 async fn append_output(&self, request:Request<AppendOutputRequest>) -> Result<Response<Empty>, Status> {
3947 use tauri::Emitter;
3948
3949 let req = request.into_inner();
3950 let _ = self
3951 .environment
3952 .ApplicationHandle
3953 .emit("sky://output/append", json!({ "channel": req.channel_id, "text": req.value }));
3954 Ok(Response::new(Empty {}))
3955 }
3956
3957 async fn clear_output(&self, request:Request<ClearOutputRequest>) -> Result<Response<Empty>, Status> {
3959 use tauri::Emitter;
3960
3961 let req = request.into_inner();
3962 let _ = self
3963 .environment
3964 .ApplicationHandle
3965 .emit("sky://output/clear", json!({ "channel": req.channel_id }));
3966 Ok(Response::new(Empty {}))
3967 }
3968
3969 async fn show_output(&self, request:Request<ShowOutputRequest>) -> Result<Response<Empty>, Status> {
3971 use tauri::Emitter;
3972
3973 let req = request.into_inner();
3974 let _ = self
3975 .environment
3976 .ApplicationHandle
3977 .emit("sky://output/show", json!({ "channel": req.channel_id }));
3978 Ok(Response::new(Empty {}))
3979 }
3980
3981 async fn dispose_output(&self, request:Request<DisposeOutputRequest>) -> Result<Response<Empty>, Status> {
3984 use tauri::Emitter;
3985
3986 let req = request.into_inner();
3987 let _ = self
3988 .environment
3989 .ApplicationHandle
3990 .emit("sky://output/dispose", json!({ "channel": req.channel_id }));
3991 Ok(Response::new(Empty {}))
3992 }
3993
3994 async fn register_task_provider(
3996 &self,
3997 request:Request<RegisterTaskProviderRequest>,
3998 ) -> Result<Response<Empty>, Status> {
3999 let req = request.into_inner();
4000 dev_log!("cocoon", "[CocoonService] Registering Task Provider: type={}", req.r#type);
4001
4002 let Handle = req
4004 .r#type
4005 .as_bytes()
4006 .iter()
4007 .fold(0u32, |Acc, B| Acc.wrapping_mul(31).wrapping_add(*B as u32));
4008 let dto = ProviderRegistrationDTO {
4009 Handle,
4010 ProviderType:ProviderType::Task,
4011 Selector:json!([{ "language": "*" }]),
4012 SideCarIdentifier:"cocoon-main".to_string(),
4013 ExtensionIdentifier:json!(req.extension_id),
4014 Options:None,
4015 };
4016 self.environment
4017 .ApplicationState
4018 .Extension
4019 .ProviderRegistration
4020 .RegisterProvider(Handle, dto);
4021
4022 Ok(Response::new(Empty {}))
4023 }
4024
4025 async fn execute_task(&self, request:Request<ExecuteTaskRequest>) -> Result<Response<ExecuteTaskResponse>, Status> {
4027 use tauri::Emitter;
4028
4029 let req = request.into_inner();
4030 dev_log!(
4031 "cocoon",
4032 "[CocoonService] execute_task: name={} source={}",
4033 req.name,
4034 req.source
4035 );
4036
4037 let _ = self
4038 .environment
4039 .ApplicationHandle
4040 .emit("sky://task/execute", json!({ "name": req.name, "source": req.source }));
4041
4042 Ok(Response::new(ExecuteTaskResponse { task_id:0, success:true }))
4043 }
4044
4045 async fn terminate_task(&self, request:Request<TerminateTaskRequest>) -> Result<Response<Empty>, Status> {
4047 use tauri::Emitter;
4048
4049 let req = request.into_inner();
4050 dev_log!("cocoon", "[CocoonService] terminate_task: id={}", req.task_id);
4051
4052 let _ = self
4053 .environment
4054 .ApplicationHandle
4055 .emit("sky://task/terminate", json!({ "id": req.task_id }));
4056
4057 Ok(Response::new(Empty {}))
4058 }
4059
4060 async fn get_authentication_session(
4062 &self,
4063 request:Request<GetAuthenticationSessionRequest>,
4064 ) -> Result<Response<GetAuthenticationSessionResponse>, Status> {
4065 let req = request.into_inner();
4066 dev_log!(
4067 "cocoon",
4068 "[CocoonService] get_authentication_session: provider={}",
4069 req.provider_id
4070 );
4071
4072 Ok(Response::new(GetAuthenticationSessionResponse::default()))
4076 }
4077
4078 async fn register_authentication_provider(
4080 &self,
4081 request:Request<RegisterAuthenticationProviderRequest>,
4082 ) -> Result<Response<Empty>, Status> {
4083 let req = request.into_inner();
4084 dev_log!("cocoon", "[CocoonService] Registering Authentication Provider: id={}", req.id);
4085
4086 let Handle = req
4087 .id
4088 .as_bytes()
4089 .iter()
4090 .fold(0u32, |Acc, B| Acc.wrapping_mul(31).wrapping_add(*B as u32));
4091 let dto = ProviderRegistrationDTO {
4092 Handle,
4093 ProviderType:ProviderType::Authentication,
4094 Selector:json!([{ "provider": req.id }]),
4095 SideCarIdentifier:"cocoon-main".to_string(),
4096 ExtensionIdentifier:json!(req.extension_id),
4097 Options:Some(json!({ "label": req.label, "supportsMultipleAccounts": req.supports_multiple_accounts })),
4098 };
4099 self.environment
4100 .ApplicationState
4101 .Extension
4102 .ProviderRegistration
4103 .RegisterProvider(Handle, dto);
4104
4105 Ok(Response::new(Empty {}))
4106 }
4107
4108 async fn stop_debugging(&self, request:Request<StopDebuggingRequest>) -> Result<Response<Empty>, Status> {
4110 use tauri::Emitter;
4111
4112 let req = request.into_inner();
4113 dev_log!("cocoon", "[CocoonService] stop_debugging: session={}", req.session_id);
4114
4115 let _ = self
4116 .environment
4117 .ApplicationHandle
4118 .emit("sky://debug/stop", json!({ "sessionId": req.session_id }));
4119
4120 Ok(Response::new(Empty {}))
4121 }
4122
4123 async fn get_extension(
4125 &self,
4126 request:Request<GetExtensionRequest>,
4127 ) -> Result<Response<GetExtensionResponse>, Status> {
4128 use CommonLibrary::ExtensionManagement::ExtensionManagementService::ExtensionManagementService;
4129
4130 let req = request.into_inner();
4131 dev_log!("cocoon", "[CocoonService] get_extension: {}", req.extension_id);
4132
4133 let ExtensionOption = self.environment.GetExtension(req.extension_id.clone()).await.ok().flatten();
4134
4135 let InfoOption = ExtensionOption.map(|Value| {
4136 ExtensionInfo {
4137 id:req.extension_id,
4138 display_name:Value.get("Name").and_then(|V| V.as_str()).unwrap_or("").to_string(),
4139 version:Value.get("Version").and_then(|V| V.as_str()).unwrap_or("").to_string(),
4140 is_active:true, extension_path:Value
4142 .get("ExtensionLocation")
4143 .and_then(|V| V.as_str())
4144 .unwrap_or("")
4145 .to_string(),
4146 }
4147 });
4148
4149 Ok(Response::new(GetExtensionResponse { extension:InfoOption }))
4150 }
4151
4152 async fn get_all_extensions(&self, request:Request<Empty>) -> Result<Response<GetAllExtensionsResponse>, Status> {
4154 use CommonLibrary::ExtensionManagement::ExtensionManagementService::ExtensionManagementService;
4155
4156 let _req = request.into_inner();
4157
4158 let Extensions = self.environment.GetExtensions().await.unwrap_or_default();
4159
4160 let ExtensionInfoList = Extensions
4161 .iter()
4162 .map(|Value| {
4163 ExtensionInfo {
4164 id:Value.get("Identifier").and_then(|V| V.as_str()).unwrap_or("").to_string(),
4165 display_name:Value.get("Name").and_then(|V| V.as_str()).unwrap_or("").to_string(),
4166 version:Value.get("Version").and_then(|V| V.as_str()).unwrap_or("").to_string(),
4167 is_active:true,
4168 extension_path:Value
4169 .get("ExtensionLocation")
4170 .and_then(|V| V.as_str())
4171 .unwrap_or("")
4172 .to_string(),
4173 }
4174 })
4175 .collect();
4176
4177 Ok(Response::new(GetAllExtensionsResponse { extensions:ExtensionInfoList }))
4178 }
4179
4180 async fn resize_terminal(&self, request:Request<ResizeTerminalRequest>) -> Result<Response<Empty>, Status> {
4186 use tauri::Emitter;
4187
4188 let req = request.into_inner();
4189 dev_log!(
4190 "cocoon",
4191 "[CocoonService] resize_terminal: id={} cols={} rows={}",
4192 req.terminal_id,
4193 req.cols,
4194 req.rows
4195 );
4196
4197 let _ = self.environment.ApplicationHandle.emit(
4199 "sky://terminal/resize",
4200 json!({ "id": req.terminal_id, "cols": req.cols, "rows": req.rows }),
4201 );
4202
4203 Ok(Response::new(Empty {}))
4207 }
4208
4209 async fn get_configuration(
4212 &self,
4213 request:Request<GetConfigurationRequest>,
4214 ) -> Result<Response<GetConfigurationResponse>, Status> {
4215 use CommonLibrary::Configuration::{
4216 ConfigurationProvider::ConfigurationProvider,
4217 DTO::ConfigurationOverridesDTO::ConfigurationOverridesDTO,
4218 };
4219
4220 let req = request.into_inner();
4221 let Key = if req.section.is_empty() {
4222 if req.key.is_empty() { None } else { Some(req.key.clone()) }
4223 } else if req.key.is_empty() {
4224 Some(req.section.clone())
4225 } else {
4226 Some(format!("{}.{}", req.section, req.key))
4227 };
4228
4229 dev_log!("cocoon", "[CocoonService] get_configuration: key={:?}", Key);
4230
4231 match self
4232 .environment
4233 .GetConfigurationValue(Key, ConfigurationOverridesDTO::default())
4234 .await
4235 {
4236 Ok(Value) => {
4237 let Bytes = serde_json::to_vec(&Value).unwrap_or_default();
4238 Ok(Response::new(GetConfigurationResponse { value:Bytes }))
4239 },
4240 Err(Error) => {
4241 dev_log!("cocoon", "warn: [CocoonService] get_configuration failed: {}", Error);
4242 Ok(Response::new(GetConfigurationResponse::default()))
4243 },
4244 }
4245 }
4246}