Skip to content

Commit 8c25573

Browse files
refactor(Mountain): Revert development naming and implement core file operations
Revert the Mountain binary/lib/config naming from "Mountain" back to the verbose development identifier to maintain continuity with the existing build artifacts and CI configurations. Implement two missing trait implementations: 1. **FileSystemProvider::Copy for directories** (WriteOperations.rs): - Previously returned NotImplemented for directory copies - New `copy_directory_recursive` uses an iterative stack (avoids async recursion stack overflow on deep trees) - Symlinks are followed to match VS Code's IFileService.copy semantics - Prevents copy-to-self which would truncate files or explode recursively - Creates parent directories as needed; uses tokio::fs::copy for fast file copying 2. **WorkspaceEditApplier::ApplyWorkspaceEdit** (WorkspaceProvider.rs): - Two-tier approach: emit `sky://editor/applyEdits` for open documents (preserves undo/dirty state), then apply edits directly to disk for closed files - Converts VS Code's line/character positions to byte offsets with UTF-16 code unit counting (matches VS Code Range semantics) - Edits sorted by descending offset to maintain validity during splicing - Atomic writes via tempfile+rename to prevent torn writes on crash - Handles file:// URIs with percent-encoding decode BREAKING CHANGE: Binary and lib names changed from "Mountain" to "DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_Mountain"
1 parent 2d330db commit 8c25573

6 files changed

Lines changed: 632 additions & 48 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[[bin]]
2-
name = "Mountain"
2+
name = "DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_Mountain"
33
path = "Source/Library.rs"
44

55
[build-dependencies]
@@ -148,7 +148,7 @@ TierOpenExternalLayer4 = []
148148
TierExtensionScanParallel = []
149149

150150
[lib]
151-
name = "Mountain"
151+
name = "DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_Mountain"
152152
path = "Source/Library.rs"
153153
crate-type = ["lib", "staticlib"]
154154

@@ -158,11 +158,11 @@ autobenches = false
158158
autobins = false
159159
autoexamples = false
160160
autotests = false
161-
default-run = "Mountain"
161+
default-run = "DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_Mountain"
162162
description = "Mountain ⛰️"
163163
edition = "2024"
164164
license-file = "LICENSE"
165-
name = "Mountain"
165+
name = "DevelopmentNodeEnvironment_MicrosoftVSCodeDependency_22NodeVersion_Bundle_Clean_Debug_ElectronProfile_Mountain"
166166
publish = false
167167
include = [
168168
"build.rs",

Cargo.toml.Backup

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
[[bin]]
2+
name = "Mountain"
3+
path = "Source/Library.rs"
4+
5+
[build-dependencies]
6+
json5 = { workspace = true }
7+
toml_edit = { workspace = true }
8+
serde = { workspace = true, features = ["derive"] }
9+
serde_json = { workspace = true }
10+
tauri-build = { workspace = true, features = [] }
11+
toml = { workspace = true }
12+
tonic-build = { workspace = true, features = ["transport"] }
13+
tonic-prost-build = { workspace = true }
14+
15+
[dev-dependencies]
16+
tokio-test = { workspace = true }
17+
futures = { workspace = true }
18+
19+
[dependencies]
20+
tauri = { workspace = true, features = [
21+
"compression",
22+
"devtools",
23+
"image-png",
24+
"rustls-tls",
25+
"tray-icon",
26+
"wry",
27+
], default-features = false }
28+
29+
tauri-plugin-dialog = { workspace = true }
30+
tauri-plugin-fs = { workspace = true }
31+
tauri-plugin-localhost = { workspace = true }
32+
tauri-plugin-log = { workspace = true }
33+
34+
async-trait = { workspace = true }
35+
anyhow = { workspace = true } # TODO: Added for error handling in CertificateManager
36+
arboard = { workspace = true }
37+
base64 = { workspace = true }
38+
chrono = { workspace = true, features = ["serde"] }
39+
colored = { workspace = true }
40+
Common = { workspace = true }
41+
dirs = { workspace = true }
42+
Echo = { workspace = true }
43+
Air = { workspace = true, optional = true }
44+
Mist = { workspace = true }
45+
env_logger = { workspace = true }
46+
futures-util = { workspace = true, features = ["sink", "std"] }
47+
globset = { workspace = true }
48+
grep-regex = { workspace = true }
49+
grep-searcher = { workspace = true }
50+
http = { workspace = true }
51+
ignore = { workspace = true }
52+
keyring = { workspace = true }
53+
lazy_static = { workspace = true }
54+
log = { workspace = true }
55+
md5 = { workspace = true }
56+
notify = { workspace = true }
57+
num_cpus = { workspace = true }
58+
once_cell = { workspace = true }
59+
open = { workspace = true }
60+
parking_lot = { workspace = true }
61+
portable-pty = { workspace = true }
62+
portpicker = { workspace = true }
63+
prost = { workspace = true }
64+
rand = { workspace = true }
65+
regex = { workspace = true }
66+
serde = { workspace = true }
67+
serde_json = { workspace = true }
68+
sha2 = { workspace = true }
69+
sysinfo = { workspace = true }
70+
hostname = { workspace = true }
71+
thiserror = { workspace = true }
72+
tokio = { workspace = true, features = ["full"] }
73+
tokio-tungstenite = { workspace = true, features = ["rustls-tls-native-roots"] }
74+
tokio-util = { workspace = true, features = ["full"] }
75+
pem = { workspace = true }
76+
rcgen = { workspace = true }
77+
p256 = { workspace = true }
78+
x509-parser = { workspace = true }
79+
rustls-pki-types = { workspace = true }
80+
rustls = { workspace = true } # TODO: Added for TLS configuration in CertificateManager
81+
toml = { workspace = true }
82+
tonic = { workspace = true }
83+
tonic-prost = { workspace = true }
84+
url = { workspace = true, features = ["serde"] }
85+
uuid = { workspace = true, features = ["v4", "serde"] }
86+
flate2 = { workspace = true }
87+
ring = { workspace = true }
88+
bincode = { workspace = true }
89+
brotli = { workspace = true }
90+
hex = { workspace = true }
91+
opentelemetry = { workspace = true, features = ["trace"] }
92+
posthog-rs = { workspace = true }
93+
zip = { workspace = true }
94+
95+
[features]
96+
default = ["ExtensionHostCocoon", "MistNative", "AirIntegration"]
97+
98+
ExtensionHostCocoon = []
99+
100+
MistNative = []
101+
102+
Debug = []
103+
104+
AirIntegration = ["Air"]
105+
106+
# Feature flags for conditional compilation
107+
grove = []
108+
cocoon = []
109+
terminals = []
110+
debug-protocol = []
111+
scm-support = []
112+
child-processes = []
113+
Telemetry = []
114+
Development = []
115+
Test = []
116+
117+
# ===========================================================================
118+
# Tier-gating (Plan A §Rust feature flags). Every name here is mirrored by
119+
# `build.rs::IsDeclaredTierFeature`. Adding a new tier requires editing
120+
# BOTH places so typos in `.env.Land` fail loud.
121+
# ===========================================================================
122+
123+
# Tier:RemoteProcedureCall:SharedMemory 🟣 Experimental
124+
TierRemoteProcedureCallSharedMemory = []
125+
# Tier:HTTPProxy:Hyper 🟣 Experimental
126+
TierHTTPProxyHyper = []
127+
# Tier:Logger:Ring 🟣 Experimental
128+
TierLoggerRing = []
129+
# Tier:FileSystem:Layer4 🟣 Experimental
130+
TierFileSystemLayer4 = []
131+
# Tier:FindFiles:Layer4 🟣 Experimental
132+
TierFindFilesLayer4 = []
133+
# Tier:Glob:Native 🟣 Experimental - globset-compiled patterns
134+
TierGlobNative = []
135+
# Tier:FileWatcher:Layer4 🟣 Experimental - notify-rs FS events (dep is already on the crate)
136+
TierFileWatcherLayer4 = []
137+
# Tier:SchemeAssets:Hybrid 🟣 Experimental
138+
TierSchemeAssetsHybrid = []
139+
# Tier:Configuration:Eager 🟣 Experimental
140+
TierConfigurationEager = []
141+
# Tier:Diagnostics:Delta 🟣 Experimental
142+
TierDiagnosticsDelta = []
143+
# Tier:Clipboard:Layer4 🟣 Experimental - arboard crate is on the crate unconditionally
144+
TierClipboardLayer4 = []
145+
# Tier:OpenExternal:Layer4 🟣 Experimental - `open` crate is on the crate unconditionally
146+
TierOpenExternalLayer4 = []
147+
# Tier:ExtensionScan:Parallel 🟣 Experimental
148+
TierExtensionScanParallel = []
149+
150+
[lib]
151+
name = "Mountain"
152+
path = "Source/Library.rs"
153+
crate-type = ["lib", "staticlib"]
154+
155+
[package]
156+
authors = ["Source 🖋️ Open 👐🏻 <Source/Open@Editor.Land>"]
157+
autobenches = false
158+
autobins = false
159+
autoexamples = false
160+
autotests = false
161+
default-run = "Mountain"
162+
description = "Mountain ⛰️"
163+
edition = "2024"
164+
license-file = "LICENSE"
165+
name = "Mountain"
166+
publish = false
167+
include = [
168+
"build.rs",
169+
"capabilities/**/*",
170+
"Cargo.toml",
171+
"CHANGELOG.md",
172+
"gen/**/*",
173+
"icons/**/*",
174+
"LICENSE",
175+
"Mountain.key.pub",
176+
"Proto/**/*",
177+
"README.md",
178+
"Source/**/*",
179+
"tauri.conf.json",
180+
"Test/**/*",
181+
]
182+
version = "0.0.1"
183+
184+
[package.metadata.docs.rs]
185+
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
186+
all-features = true
187+
default-target = "x86_64-unknown-linux-gnu"
188+
targets = [
189+
"x86_64-unknown-linux-gnu",
190+
"x86_64-apple-darwin",
191+
"aarch64-apple-darwin",
192+
]

Source/Environment/FileSystemProvider/WriteOperations.rs

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,10 @@ pub(super) async fn copy_impl(
173173
// Call stat_file_impl from the ReadOperations module
174174
let source_metadata = super::ReadOperations::stat_file_impl(env, source).await?;
175175

176-
if (source_metadata.FileType & FileTypeDTO::Directory as u8) != 0 {
177-
return Err(CommonError::NotImplemented { FeatureName:"Recursive directory copy".to_string() });
178-
}
176+
let SourceIsDir = (source_metadata.FileType & FileTypeDTO::Directory as u8) != 0;
179177

180-
// Prevent copying file to itself (which would truncate it)
178+
// Prevent copying file/dir to itself (which would truncate or
179+
// recursively explode).
181180
if fs::canonicalize(source).await.ok().as_ref() == fs::canonicalize(target).await.ok().as_ref() {
182181
return Err(CommonError::InvalidArgument {
183182
ArgumentName:"Target".to_string(),
@@ -189,7 +188,9 @@ pub(super) async fn copy_impl(
189188
return Err(CommonError::FileSystemFileExists(target.clone()));
190189
}
191190

192-
// Create target parent directory if needed
191+
// Create target parent directory if needed (works for both file
192+
// and directory copies; the directory copy below also creates
193+
// the target itself).
193194
if let Some(target_parent) = target.parent() {
194195
if !fs::try_exists(target_parent).await.unwrap_or(false) {
195196
fs::create_dir_all(target_parent).await.map_err(|error| {
@@ -198,12 +199,78 @@ pub(super) async fn copy_impl(
198199
}
199200
}
200201

202+
if SourceIsDir {
203+
// Recursive directory copy. Walks the source tree iteratively
204+
// (avoids deep async recursion blowing the stack on
205+
// pathological depths) and re-creates each entry under the
206+
// target. Symlinks are followed to keep behaviour consistent
207+
// with VS Code's `IFileService.copy` - if you want preserve-
208+
// symlinks semantics, use `clone_native` instead which does a
209+
// COW reflink on supported filesystems.
210+
return copy_directory_recursive(source, target, overwrite).await;
211+
}
212+
201213
fs::copy(source, target)
202214
.await
203215
.map(|_| ())
204216
.map_err(|error| CommonError::FromStandardIOError(error, source.clone(), "Copy"))
205217
}
206218

219+
/// Recursively copy a directory tree from `source` into `target`.
220+
/// Iterative (uses an explicit stack of `(SrcDir, DstDir)`) so it
221+
/// can't blow the Tokio task stack on very deep trees. Files inside
222+
/// re-use `tokio::fs::copy` for fast path; directories are created
223+
/// with `create_dir`. Symlinks are dereferenced.
224+
async fn copy_directory_recursive(
225+
source:&PathBuf,
226+
target:&PathBuf,
227+
overwrite:bool,
228+
) -> Result<(), CommonError> {
229+
// Pre-create the top-level target dir.
230+
if !fs::try_exists(target).await.unwrap_or(false) {
231+
fs::create_dir(target).await.map_err(|error| {
232+
CommonError::FromStandardIOError(error, target.clone(), "Copy.CreateTargetRoot")
233+
})?;
234+
}
235+
236+
let mut Stack:Vec<(PathBuf, PathBuf)> = vec![(source.clone(), target.clone())];
237+
while let Some((SrcDir, DstDir)) = Stack.pop() {
238+
let mut Entries = fs::read_dir(&SrcDir)
239+
.await
240+
.map_err(|error| CommonError::FromStandardIOError(error, SrcDir.clone(), "Copy.ReadDir"))?;
241+
while let Some(Entry) = Entries
242+
.next_entry()
243+
.await
244+
.map_err(|error| CommonError::FromStandardIOError(error, SrcDir.clone(), "Copy.NextEntry"))?
245+
{
246+
let Name = Entry.file_name();
247+
let SrcPath = SrcDir.join(&Name);
248+
let DstPath = DstDir.join(&Name);
249+
let FileType = Entry
250+
.file_type()
251+
.await
252+
.map_err(|error| CommonError::FromStandardIOError(error, SrcPath.clone(), "Copy.FileType"))?;
253+
254+
if FileType.is_dir() {
255+
if !fs::try_exists(&DstPath).await.unwrap_or(false) {
256+
fs::create_dir(&DstPath).await.map_err(|error| {
257+
CommonError::FromStandardIOError(error, DstPath.clone(), "Copy.CreateSubDir")
258+
})?;
259+
}
260+
Stack.push((SrcPath, DstPath));
261+
} else {
262+
if !overwrite && fs::try_exists(&DstPath).await.unwrap_or(false) {
263+
return Err(CommonError::FileSystemFileExists(DstPath));
264+
}
265+
fs::copy(&SrcPath, &DstPath).await.map_err(|error| {
266+
CommonError::FromStandardIOError(error, SrcPath.clone(), "Copy.CopyFile")
267+
})?;
268+
}
269+
}
270+
}
271+
Ok(())
272+
}
273+
207274
/// CreateFile operations implementation for MountainEnvironment
208275
pub(super) async fn create_file_impl(env:&MountainEnvironment, path:&PathBuf) -> Result<(), CommonError> {
209276
// Use WriteFile with an empty Vec, ensuring creation without overwrite.

0 commit comments

Comments
 (0)