Skip to main content

Mountain/Track/Effect/CreateEffectForRequest/
Workspace.rs

1#![allow(non_snake_case, unused_variables, dead_code, unused_imports)]
2
3use std::{future::Future, pin::Pin, sync::Arc};
4
5use serde_json::{Value, json};
6use tauri::Runtime;
7
8use crate::{RunTime::ApplicationRunTime::ApplicationRunTime, Track::Effect::MappedEffectType::MappedEffect, dev_log};
9
10pub fn CreateEffect<R:Runtime>(MethodName:&str, Parameters:Value) -> Option<Result<MappedEffect, String>> {
11	match MethodName {
12		"applyEdit" => {
13			let effect =
14				move |run_time:Arc<ApplicationRunTime>| -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
15					Box::pin(async move {
16						// Atom T1: round-trip via Mountain's request/reply plumbing so the
17						// extension's `await workspace.applyEdit(…)` resolves when Sky has
18						// actually applied the edit (or refused). Previously a synthetic
19						// `true` returned before the edit ran, racing listeners that
20						// expected post-apply state.
21						let Payload = if Parameters.is_array() {
22							Parameters.get(0).cloned().unwrap_or_default()
23						} else {
24							Parameters
25						};
26						match crate::Environment::UserInterfaceProvider::SendUserInterfaceRequest(
27							&run_time.Environment,
28							"sky://workspace/applyEdit",
29							Payload,
30						)
31						.await
32						{
33							Ok(Value) => Ok(Value),
34							Err(Error) => {
35								dev_log!(
36									"ipc",
37									"warn: [applyEdit] Sky did not answer ({:?}); returning synthetic true",
38									Error
39								);
40								Ok(json!(true))
41							},
42						}
43					})
44				};
45			Some(Ok(Box::new(effect)))
46		},
47
48		"showTextDocument" => {
49			let effect =
50				move |run_time:Arc<ApplicationRunTime>| -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
51					Box::pin(async move {
52						// Atom T1: same round-trip as applyEdit. The canonical vscode
53						// return shape is a `TextEditor` - today Sky resolves with a
54						// thin `{ uri, viewColumn }` stub. Extensions that chain
55						// editor ops may still see undefined properties; that's a
56						// Sky-side enrichment task (T2 follow-up).
57						match crate::Environment::UserInterfaceProvider::SendUserInterfaceRequest(
58							&run_time.Environment,
59							"sky://window/showTextDocument",
60							Parameters,
61						)
62						.await
63						{
64							Ok(Value) => Ok(Value),
65							Err(Error) => {
66								dev_log!(
67									"ipc",
68									"warn: [showTextDocument] Sky did not answer ({:?}); returning null",
69									Error
70								);
71								Ok(json!(null))
72							},
73						}
74					})
75				};
76			Some(Ok(Box::new(effect)))
77		},
78
79		// Workspace-trust family. vscode.git's `Model.openRepository` calls
80		// `await workspace.requestResourceTrust({uri, message})` and
81		// `await workspace.isResourceTrusted(uri)` before constructing the
82		// Repository. The Cocoon `WrapWorkspaceNamespace` Proxy fallback
83		// already returns a permissive `true` heuristic so vscode.git
84		// proceeds; routing the same method names through Mountain here
85		// gives the canonical handler a place to live (and makes
86		// `MountainMethods` see them via `GenerateRouteManifest.sh`'s grep,
87		// which switches the Cocoon shim from heuristic-default to
88		// gRPC-routed automatically on the next manifest regeneration). A
89		// future round can replace the unconditional `true` with a real
90		// per-OS trust query (Gatekeeper / SmartScreen / xattrs); single-
91		// window dev runtime stays trust-by-default.
92		"Workspace.RequestResourceTrust" | "Workspace.IsResourceTrusted" => {
93			let effect =
94				move |_run_time:Arc<ApplicationRunTime>| -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
95					Box::pin(async move {
96						Ok(json!({ "trusted": true }))
97					})
98				};
99			Some(Ok(Box::new(effect)))
100		},
101
102		"$updateWorkspaceFolders" => {
103			let effect =
104				move |run_time:Arc<ApplicationRunTime>| -> Pin<Box<dyn Future<Output = Result<Value, String>> + Send>> {
105					Box::pin(async move {
106						let Payload = if Parameters.is_array() {
107							Parameters.get(0).cloned().unwrap_or_default()
108						} else {
109							Parameters
110						};
111						let Additions:Vec<(String, String)> = Payload
112							.get("additions")
113							.and_then(Value::as_array)
114							.map(|Array| {
115								Array
116									.iter()
117									.filter_map(|Entry| {
118										let Uri = Entry
119											.get("uri")
120											.and_then(|U| U.get("value").and_then(Value::as_str).or_else(|| U.as_str()))
121											.map(str::to_string)?;
122										let Name = Entry.get("name").and_then(Value::as_str).unwrap_or("").to_string();
123										Some((Uri, Name))
124									})
125									.collect()
126							})
127							.unwrap_or_default();
128						let Removals:Vec<String> = Payload
129							.get("removals")
130							.and_then(Value::as_array)
131							.map(|Array| {
132								Array
133									.iter()
134									.filter_map(|Entry| {
135										Entry
136											.get("uri")
137											.and_then(|U| U.get("value").and_then(Value::as_str).or_else(|| U.as_str()))
138											.map(str::to_string)
139									})
140									.collect()
141							})
142							.unwrap_or_default();
143
144						let Workspace = &run_time.Environment.ApplicationState.Workspace;
145						let mut Folders = Workspace.GetWorkspaceFolders();
146						Folders.retain(|F| !Removals.contains(&F.URI.to_string()));
147						let Base = Folders.len();
148						for (Index, (UriStr, Name)) in Additions.iter().enumerate() {
149							if let Ok(Url) = url::Url::parse(UriStr) {
150								if let Ok(Dto) =
151									crate::ApplicationState::DTO::WorkspaceFolderStateDTO::WorkspaceFolderStateDTO::New(
152										Url,
153										Name.clone(),
154										Base + Index,
155									) {
156									Folders.push(Dto);
157								}
158							}
159						}
160						crate::ApplicationState::State::WorkspaceState::WorkspaceDelta::UpdateWorkspaceFoldersAndNotify(
161							Workspace, Folders,
162						);
163						Ok(json!(null))
164					})
165				};
166			Some(Ok(Box::new(effect)))
167		},
168
169		_ => None,
170	}
171}