[Extension Reloading]: Update custom commands, add enable/disable command (#12547)

This commit is contained in:
Jacob MacDonald
2025-11-05 11:36:07 -08:00
committed by GitHub
parent ca6cfaaf4e
commit fa93b56243
24 changed files with 664 additions and 187 deletions

View File

@@ -616,15 +616,21 @@ export class ExtensionManager extends ExtensionLoader {
throw new Error(`Extension with name ${name} does not exist.`);
}
const scopePath =
scope === SettingScope.Workspace ? this.workspaceDir : os.homedir();
this.extensionEnablementManager.disable(name, true, scopePath);
extension.isActive = false;
await this.maybeStopExtension(extension);
if (scope !== SettingScope.Session) {
const scopePath =
scope === SettingScope.Workspace ? this.workspaceDir : os.homedir();
this.extensionEnablementManager.disable(name, true, scopePath);
}
logExtensionDisable(
this.telemetryConfig,
new ExtensionDisableEvent(hashValue(name), extension.id, scope),
);
if (!this.config || this.config.getEnableExtensionReloading()) {
// Only toggle the isActive state if we are actually going to disable it
// in the current session, or we haven't been initialized yet.
extension.isActive = false;
}
await this.maybeStopExtension(extension);
}
/**
@@ -644,14 +650,21 @@ export class ExtensionManager extends ExtensionLoader {
if (!extension) {
throw new Error(`Extension with name ${name} does not exist.`);
}
const scopePath =
scope === SettingScope.Workspace ? this.workspaceDir : os.homedir();
this.extensionEnablementManager.enable(name, true, scopePath);
if (scope !== SettingScope.Session) {
const scopePath =
scope === SettingScope.Workspace ? this.workspaceDir : os.homedir();
this.extensionEnablementManager.enable(name, true, scopePath);
}
logExtensionEnable(
this.telemetryConfig,
new ExtensionEnableEvent(hashValue(name), extension.id, scope),
);
extension.isActive = true;
if (!this.config || this.config.getEnableExtensionReloading()) {
// Only toggle the isActive state if we are actually going to disable it
// in the current session, or we haven't been initialized yet.
extension.isActive = true;
}
await this.maybeStartExtension(extension);
}
}

View File

@@ -157,6 +157,38 @@ export enum SettingScope {
Workspace = 'Workspace',
System = 'System',
SystemDefaults = 'SystemDefaults',
// Note that this scope is not supported in the settings dialog at this time,
// it is only supported for extensions.
Session = 'Session',
}
/**
* A type representing the settings scopes that are supported for LoadedSettings.
*/
export type LoadableSettingScope =
| SettingScope.User
| SettingScope.Workspace
| SettingScope.System
| SettingScope.SystemDefaults;
/**
* The actual values of the loadable settings scopes.
*/
const _loadableSettingScopes = [
SettingScope.User,
SettingScope.Workspace,
SettingScope.System,
SettingScope.SystemDefaults,
];
/**
* A type guard function that checks if `scope` is a loadable settings scope,
* and allows promotion to the `LoadableSettingsScope` type based on the result.
*/
export function isLoadableSettingScope(
scope: SettingScope,
): scope is LoadableSettingScope {
return _loadableSettingScopes.includes(scope);
}
export interface CheckpointingSettings {
@@ -398,14 +430,14 @@ export class LoadedSettings {
user: SettingsFile,
workspace: SettingsFile,
isTrusted: boolean,
migratedInMemorScopes: Set<SettingScope>,
migratedInMemoryScopes: Set<SettingScope>,
) {
this.system = system;
this.systemDefaults = systemDefaults;
this.user = user;
this.workspace = workspace;
this.isTrusted = isTrusted;
this.migratedInMemorScopes = migratedInMemorScopes;
this.migratedInMemoryScopes = migratedInMemoryScopes;
this._merged = this.computeMergedSettings();
}
@@ -414,7 +446,7 @@ export class LoadedSettings {
readonly user: SettingsFile;
readonly workspace: SettingsFile;
readonly isTrusted: boolean;
readonly migratedInMemorScopes: Set<SettingScope>;
readonly migratedInMemoryScopes: Set<SettingScope>;
private _merged: Settings;
@@ -432,7 +464,7 @@ export class LoadedSettings {
);
}
forScope(scope: SettingScope): SettingsFile {
forScope(scope: LoadableSettingScope): SettingsFile {
switch (scope) {
case SettingScope.User:
return this.user;
@@ -447,7 +479,7 @@ export class LoadedSettings {
}
}
setValue(scope: SettingScope, key: string, value: unknown): void {
setValue(scope: LoadableSettingScope, key: string, value: unknown): void {
const settingsFile = this.forScope(scope);
setNestedProperty(settingsFile.settings, key, value);
setNestedProperty(settingsFile.originalSettings, key, value);
@@ -563,7 +595,7 @@ export function loadSettings(
const settingsErrors: SettingsError[] = [];
const systemSettingsPath = getSystemSettingsPath();
const systemDefaultsPath = getSystemDefaultsPath();
const migratedInMemorScopes = new Set<SettingScope>();
const migratedInMemoryScopes = new Set<SettingScope>();
// Resolve paths to their canonical representation to handle symlinks
const resolvedWorkspaceDir = path.resolve(workspaceDir);
@@ -625,7 +657,7 @@ export function loadSettings(
);
}
} else {
migratedInMemorScopes.add(scope);
migratedInMemoryScopes.add(scope);
}
settingsObject = migratedSettings;
}
@@ -703,7 +735,7 @@ export function loadSettings(
isTrusted,
);
// loadEnviroment depends on settings so we have to create a temp version of
// loadEnvironment depends on settings so we have to create a temp version of
// the settings to avoid a cycle
loadEnvironment(tempMergedSettings);
@@ -744,7 +776,7 @@ export function loadSettings(
rawJson: workspaceResult.rawJson,
},
isTrusted,
migratedInMemorScopes,
migratedInMemoryScopes,
);
}
@@ -752,7 +784,7 @@ export function migrateDeprecatedSettings(
loadedSettings: LoadedSettings,
extensionManager: ExtensionManager,
): void {
const processScope = (scope: SettingScope) => {
const processScope = (scope: LoadableSettingScope) => {
const settings = loadedSettings.forScope(scope).settings;
if (settings.extensions?.disabled) {
debugLogger.log(