Skip to content

Commit 5eb660d

Browse files
fix(Mountain): Distinguish user vs built-in extension paths during scan
Add `IsUserExtensionScanPath()` helper to detect whether a scan directory represents the user-writable `~/.land/extensions` root (or `LAND_USER_EXTENSION_DIRECTORY` override) versus bundled paths like `Resources/extensions` and VS Code's submodule. Previously every extension was marked `IsBuiltin = true` regardless of source, causing user-installed VSIXes to appear under "Built-in" in the Extensions sidebar and leaving `@installed` (which filters for User-scope) empty. Now the scanner checks the canonical scan path upfront and sets `Description.IsBuiltin = !IsUserPath` accordingly. This complements the sidebar handler fix in the previous commit by ensuring the underlying scan data is correctly classified at the source.
1 parent 48be854 commit 5eb660d

1 file changed

Lines changed: 58 additions & 2 deletions

File tree

Source/ExtensionManagement/Scanner.rs

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,49 @@ fn IsDeniedDirectory(Name:&str) -> bool { EXTENSION_SCAN_DENY_LIST.iter().any(|D
159159

160160
fn IsTestOnlyExtension(Name:&str) -> bool { TEST_ONLY_EXTENSIONS.iter().any(|TestOnly| *TestOnly == Name) }
161161

162+
/// Return `true` if the given scan path represents a user-writable extension
163+
/// directory (i.e. where `extensions:install` drops VSIX payloads), not a
164+
/// bundled "built-in" path that ships with the app.
165+
///
166+
/// VS Code's sidebar categorises installed extensions by `IsBuiltin`:
167+
/// `true` appears under **Built-in**, `false` under **Installed**
168+
/// (accessible via `@installed`). Previously this classifier was
169+
/// hardcoded to `true` for every scan path, so user-installed VSIXes
170+
/// showed up under Built-in and `@installed` was empty.
171+
///
172+
/// The canonical user extension root on macOS/Linux is `~/.land/extensions`
173+
/// (VS Code's equivalent is `~/.vscode/extensions`). We also honour a
174+
/// `LAND_USER_EXTENSION_DIRECTORY` override in case callers remap it.
175+
///
176+
/// Everything else - the Mountain build's own `Resources/extensions`,
177+
/// Sky's `Static/Application/extensions`, the VS Code submodule's
178+
/// `Dependency/…/extensions` - is treated as built-in.
179+
fn IsUserExtensionScanPath(DirectoryPath:&std::path::Path) -> bool {
180+
let Normalised = match DirectoryPath.canonicalize() {
181+
Ok(Canonical) => Canonical,
182+
Err(_) => DirectoryPath.to_path_buf(),
183+
};
184+
185+
// `${LAND_USER_EXTENSION_DIRECTORY}` explicit override takes priority.
186+
if let Ok(Override) = std::env::var("LAND_USER_EXTENSION_DIRECTORY") {
187+
if !Override.is_empty() && Normalised == std::path::PathBuf::from(&Override) {
188+
return true;
189+
}
190+
}
191+
192+
// `${HOME}/.land/extensions` is the default user-scope root - used by
193+
// `VsixInstaller::InstallVsix` for local VSIX drops and by the scan
194+
// path list in `ScanPathConfigure`.
195+
if let Ok(Home) = std::env::var("HOME") {
196+
let UserRoot = std::path::PathBuf::from(Home).join(".land/extensions");
197+
if Normalised == UserRoot {
198+
return true;
199+
}
200+
}
201+
202+
false
203+
}
204+
162205
/// Scans a single directory for valid extensions.
163206
///
164207
/// This function iterates through a given directory, looking for subdirectories
@@ -169,6 +212,10 @@ pub async fn ScanDirectoryForExtensions(
169212

170213
DirectoryPath:PathBuf,
171214
) -> Result<Vec<ExtensionDescriptionStateDTO>, CommonError> {
215+
// Decide up-front whether this scan path contributes built-ins or user
216+
// extensions. Built-ins are ones shipped inside the Mountain/Sky/VS Code
217+
// bundle; the `~/.land/extensions` root is user-space.
218+
let IsUserPath = IsUserExtensionScanPath(&DirectoryPath);
172219
let RunTime = ApplicationHandle.state::<Arc<ApplicationRunTime>>().inner().clone();
173220

174221
let mut FoundExtensions = Vec::new();
@@ -309,8 +356,17 @@ pub async fn ScanDirectoryForExtensions(
309356
Description.Identifier = serde_json::json!({ "value": Id });
310357
}
311358

312-
// Mark as built-in extension
313-
Description.IsBuiltin = true;
359+
// Classify the extension by the scan path it came from.
360+
// Built-in extensions ship in the Mountain/Sky/VS Code
361+
// bundle; user extensions live under
362+
// `~/.land/extensions` (written by
363+
// `VsixInstaller::InstallVsix`). Hardcoding `true`
364+
// here (the previous behaviour) made every VSIX
365+
// install appear under **Built-in** in the
366+
// Extensions sidebar and left `@installed` empty
367+
// because the default query filters for User-scope
368+
// extensions only.
369+
Description.IsBuiltin = !IsUserPath;
314370

315371
FoundExtensions.push(Description);
316372
},

0 commit comments

Comments
 (0)