Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/vs/platform/workspace/common/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,11 @@ export interface IWorkspace {
*/
readonly configuration?: URI | null;

/**
* Optional display name for the workspace.
*/
readonly name?: string;

}

export function isWorkspace(thing: unknown): thing is IWorkspace {
Expand Down Expand Up @@ -350,6 +355,7 @@ export class Workspace implements IWorkspace {
private _transient: boolean,
private _configuration: URI | null,
private ignorePathCasing: (key: URI) => boolean,
private _workspaceName?: string,
) {
this.foldersMap = TernarySearchTree.forUris<WorkspaceFolder>(this.ignorePathCasing, () => true);
this.folders = folders;
Expand All @@ -359,6 +365,7 @@ export class Workspace implements IWorkspace {
this._id = workspace.id;
this._configuration = workspace.configuration;
this._transient = workspace.transient;
this._workspaceName = workspace.name;
this.ignorePathCasing = workspace.ignorePathCasing;
this.folders = workspace.folders;
}
Expand All @@ -379,6 +386,10 @@ export class Workspace implements IWorkspace {
this._configuration = configuration;
}

get name(): string | undefined {
return this._workspaceName;
}

getFolder(resource: URI): IWorkspaceFolder | null {
if (!resource) {
return null;
Expand All @@ -395,7 +406,7 @@ export class Workspace implements IWorkspace {
}

toJSON(): IWorkspace {
return { id: this.id, folders: this.folders, transient: this.transient, configuration: this.configuration };
return { id: this.id, folders: this.folders, transient: this.transient, configuration: this.configuration, name: this.name };
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Emitter, Event } from '../../../../base/common/event.js';
import { Disposable, DisposableMap } from '../../../../base/common/lifecycle.js';
import { ResourceMap } from '../../../../base/common/map.js';
import { URI } from '../../../../base/common/uri.js';
import { Queue } from '../../../../base/common/async.js';
import { Promises, Queue } from '../../../../base/common/async.js';
import { VSBuffer } from '../../../../base/common/buffer.js';
import { JSONPath, ParseError, parse } from '../../../../base/common/json.js';
import { applyEdits, setProperty } from '../../../../base/common/jsonEdit.js';
Expand All @@ -26,7 +26,7 @@ import { IPolicyService, NullPolicyService } from '../../../../platform/policy/c
import { Registry } from '../../../../platform/registry/common/platform.js';
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
import { IWorkspaceContextService, IWorkspaceFoldersChangeEvent, IWorkspaceFolder, WorkbenchState, Workspace } from '../../../../platform/workspace/common/workspace.js';
import { FolderConfiguration, UserConfiguration } from '../../../../workbench/services/configuration/browser/configuration.js';
import { FolderConfiguration, UserConfiguration, WorkspaceConfiguration } from '../../../../workbench/services/configuration/browser/configuration.js';
import { APPLICATION_SCOPES, APPLY_ALL_PROFILES_SETTING, FOLDER_CONFIG_FOLDER_NAME, FOLDER_SETTINGS_PATH, IWorkbenchConfigurationService, RestrictedSettings } from '../../../../workbench/services/configuration/common/configuration.js';
import { Configuration } from '../../../../workbench/services/configuration/common/configurationModels.js';
import { IUserDataProfileService } from '../../../../workbench/services/userDataProfile/common/userDataProfile.js';
Expand All @@ -53,6 +53,7 @@ export class ConfigurationService extends Disposable implements IWorkbenchConfig
private readonly defaultConfiguration: DefaultConfiguration;
private readonly policyConfiguration: IPolicyConfiguration;
private readonly userConfiguration: UserConfiguration;
private readonly workspaceConfiguration: WorkspaceConfiguration;
private readonly cachedFolderConfigs = this._register(new DisposableMap<URI, FolderConfiguration>(new ResourceMap()));
private readonly agentsWindowReadOnlyKeys = new Set<string>();

Expand Down Expand Up @@ -82,6 +83,7 @@ export class ConfigurationService extends Disposable implements IWorkbenchConfig
this.policyConfiguration = policyService instanceof NullPolicyService ? new NullPolicyConfiguration() : this._register(new PolicyConfiguration(this.defaultConfiguration, policyService, logService));
this.initAgentsWindowReadOnlyKeys();
this.userConfiguration = this._register(new UserConfiguration(userDataProfileService.currentProfile.settingsResource, userDataProfileService.currentProfile.tasksResource, userDataProfileService.currentProfile.mcpResource, { exclude: [...this.agentsWindowReadOnlyKeys] }, fileService, uriIdentityService, logService));
this.workspaceConfiguration = this._register(new WorkspaceConfiguration({ needsCaching: () => false, read: async () => '', write: async () => { }, remove: async () => { } }, fileService, uriIdentityService, logService));
this.configurationEditing = new ConfigurationEditing(fileService, this);

this._configuration = new Configuration(
Expand All @@ -101,24 +103,28 @@ export class ConfigurationService extends Disposable implements IWorkbenchConfig
this._register(this.defaultConfiguration.onDidChangeConfiguration(({ defaults, properties }) => this.onDefaultConfigurationChanged(defaults, properties)));
this._register(this.policyConfiguration.onDidChangeConfiguration(configurationModel => this.onPolicyConfigurationChanged(configurationModel)));
this._register(this.userConfiguration.onDidChangeConfiguration(userConfiguration => this.onUserConfigurationChanged(userConfiguration)));
this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged()));
this._register(this.workspaceService.onWillChangeWorkspaceFolders(e => e.join(this.loadFolderConfigurations(e.changes.added))));
this._register(this.workspaceService.onDidChangeWorkspaceFolders(e => this.onWorkspaceFoldersChanged(e)));
}

async initialize(): Promise<void> {
const workspace = this.workspaceService.getWorkspace() as Workspace;
const workspaceIdentifier = { id: workspace.id, configPath: workspace.configuration! };
const [defaultModel, policyModel, userModel] = await Promise.all([
this.defaultConfiguration.initialize(),
this.policyConfiguration.initialize(),
this.userConfiguration.initialize()
this.userConfiguration.initialize(),
this.workspaceConfiguration.initialize(workspaceIdentifier, true),
Comment thread
sandy081 marked this conversation as resolved.
]);
const workspace = this.workspaceService.getWorkspace() as Workspace;
this.workspaceConfiguration.reparseWorkspaceSettings({ exclude: [...this.agentsWindowReadOnlyKeys] });
this._configuration = new Configuration(
defaultModel,
policyModel,
ConfigurationModel.createEmptyModel(this.logService),
userModel,
ConfigurationModel.createEmptyModel(this.logService),
ConfigurationModel.createEmptyModel(this.logService),
this.workspaceConfiguration.getConfiguration(),
new ResourceMap(),
ConfigurationModel.createEmptyModel(this.logService),
new ResourceMap<ConfigurationModel>(),
Expand Down Expand Up @@ -152,6 +158,7 @@ export class ConfigurationService extends Disposable implements IWorkbenchConfig
const overrides: IConfigurationUpdateOverrides | undefined = isConfigurationUpdateOverrides(arg3) ? arg3
: isConfigurationOverrides(arg3) ? { resource: arg3.resource, overrideIdentifiers: arg3.overrideIdentifier ? [arg3.overrideIdentifier] : undefined } : undefined;
const target: ConfigurationTarget | undefined = (overrides ? arg4 : arg3) as ConfigurationTarget | undefined;
const targets: ConfigurationTarget[] = target ? [target] : [];

if (overrides?.overrideIdentifiers) {
overrides.overrideIdentifiers = distinct(overrides.overrideIdentifiers);
Expand All @@ -167,9 +174,13 @@ export class ConfigurationService extends Disposable implements IWorkbenchConfig
throw new Error(`Unable to write ${key} because it is read-only in the Agents window.`);
}

// Remove the setting, if the value is same as default value
if (equals(value, inspect.defaultValue)) {
value = undefined;
if (!targets.length) {
targets.push(...this.deriveConfigurationTargets(key, value, inspect));

// Remove the setting, if the value is same as default value and is updated only in user target
if (equals(value, inspect.defaultValue) && targets.length === 1 && targets[0] === ConfigurationTarget.USER) {
value = undefined;
}
}

if (overrides?.overrideIdentifiers?.length && overrides.overrideIdentifiers.length > 1) {
Expand All @@ -180,22 +191,67 @@ export class ConfigurationService extends Disposable implements IWorkbenchConfig
}
}

const path = overrides?.overrideIdentifiers?.length ? [keyFromOverrideIdentifiers(overrides.overrideIdentifiers), key] : [key];
await Promises.settled(targets.map(t => this.writeConfigurationValue(key, value, t, overrides)));
}

private async writeConfigurationValue(key: string, value: unknown, target: ConfigurationTarget, overrides: IConfigurationUpdateOverrides | undefined): Promise<void> {
let path = overrides?.overrideIdentifiers?.length ? [keyFromOverrideIdentifiers(overrides.overrideIdentifiers), key] : [key];

const settingsResource = this.getSettingsResource(target, overrides?.resource ?? undefined);

// When writing to the workspace configuration file, settings go under the "settings" key
if (this.isWorkspaceConfigurationResource(settingsResource)) {
path = ['settings', ...path];
}

await this.configurationEditing.write(settingsResource, path, value);
await this.reloadConfiguration();
}

private deriveConfigurationTargets(_key: string, value: unknown, inspect: IConfigurationValue<unknown>): ConfigurationTarget[] {
if (equals(value, inspect.value)) {
return [];
}

const definedTargets: ConfigurationTarget[] = [];
if (inspect.workspaceFolderValue !== undefined) {
definedTargets.push(ConfigurationTarget.WORKSPACE_FOLDER);
}
if (inspect.workspaceValue !== undefined) {
definedTargets.push(ConfigurationTarget.WORKSPACE);
}
if (inspect.userValue !== undefined) {
definedTargets.push(ConfigurationTarget.USER);
}

if (value === undefined) {
// Remove the setting in all defined targets
return definedTargets;
}

return [definedTargets[0] || ConfigurationTarget.USER];
}

private isWorkspaceConfigurationResource(resource: URI): boolean {
const workspace = this.workspaceService.getWorkspace();
return !!(workspace.configuration && this.uriIdentityService.extUri.isEqual(workspace.configuration, resource));
}

private getSettingsResource(target: ConfigurationTarget | undefined, resource: URI | undefined): URI {
if (target === ConfigurationTarget.WORKSPACE_FOLDER || target === ConfigurationTarget.WORKSPACE) {
if (target === ConfigurationTarget.WORKSPACE_FOLDER) {
if (resource) {
const folder = this.workspaceService.getWorkspaceFolder(resource);
if (folder) {
return this.uriIdentityService.extUri.joinPath(folder.uri, FOLDER_SETTINGS_PATH);
}
}
}
if (target === ConfigurationTarget.WORKSPACE) {
const workspace = this.workspaceService.getWorkspace();
if (workspace.configuration) {
return workspace.configuration;
}
}
return this.settingsResource;
}

Expand All @@ -212,6 +268,11 @@ export class ConfigurationService extends Disposable implements IWorkbenchConfig
const previousData = this._configuration.toData();
const change = this._configuration.compareAndUpdateLocalUserConfiguration(userModel);

// Reload workspace configuration
const workspaceChange = await this.loadWorkspaceConfiguration();
change.keys.push(...workspaceChange.keys);
change.overrides.push(...workspaceChange.overrides);

// Reload folder configurations
for (const folder of this.workspaceService.getWorkspace().folders) {
const folderConfiguration = this.cachedFolderConfigs.get(folder.uri);
Expand Down Expand Up @@ -272,6 +333,7 @@ export class ConfigurationService extends Disposable implements IWorkbenchConfig
const previousData = this._configuration.toData();
const change = this._configuration.compareAndUpdateDefaultConfiguration(defaults, properties);
this._configuration.updateLocalUserConfiguration(this.userConfiguration.reparse({ exclude: [...this.agentsWindowReadOnlyKeys] }));
this._configuration.updateWorkspaceConfiguration(this.workspaceConfiguration.reparseWorkspaceSettings({ exclude: [...this.agentsWindowReadOnlyKeys] }));
for (const folder of this.workspaceService.getWorkspace().folders) {
const folderConfiguration = this.cachedFolderConfigs.get(folder.uri);
if (folderConfiguration) {
Expand All @@ -293,6 +355,18 @@ export class ConfigurationService extends Disposable implements IWorkbenchConfig
this.triggerConfigurationChange(change, previousData, ConfigurationTarget.USER);
}

private async onWorkspaceConfigurationChanged(): Promise<void> {
const previousData = this._configuration.toData();
const change = await this.loadWorkspaceConfiguration();
this.triggerConfigurationChange(change, previousData, ConfigurationTarget.WORKSPACE);
}

private async loadWorkspaceConfiguration(): Promise<IConfigurationChange> {
await this.workspaceConfiguration.reload();
this.workspaceConfiguration.reparseWorkspaceSettings({ exclude: [...this.agentsWindowReadOnlyKeys] });
return this._configuration.compareAndUpdateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration());
}

private onWorkspaceFoldersChanged(e: IWorkspaceFoldersChangeEvent): void {
// Remove configurations for removed folders
const previousData = this._configuration.toData();
Expand Down
Loading
Loading