#255797 prepare for auto refetching (#257055)

This commit is contained in:
Sandeep Somavarapu 2025-07-21 12:26:25 +02:00 committed by GitHub
parent b37c013d08
commit 040ac30020
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 125 additions and 34 deletions

View File

@ -115,31 +115,46 @@ const editorConfiguration: IConfigurationNode = {
type: 'boolean',
default: false,
markdownDescription: nls.localize('editor.experimental.treeSitterTelemetry', "Controls whether tree sitter parsing should be turned on and telemetry collected. Setting `editor.experimental.preferTreeSitter` for specific languages will take precedence."),
tags: ['experimental', 'onExP']
tags: ['experimental'],
experiment: {
autoRefetch: false
}
},
'editor.experimental.preferTreeSitter.css': {
type: 'boolean',
default: false,
markdownDescription: nls.localize('editor.experimental.preferTreeSitter.css', "Controls whether tree sitter parsing should be turned on for css. This will take precedence over `editor.experimental.treeSitterTelemetry` for css."),
tags: ['experimental', 'onExP']
tags: ['experimental'],
experiment: {
autoRefetch: false
}
},
'editor.experimental.preferTreeSitter.typescript': {
type: 'boolean',
default: false,
markdownDescription: nls.localize('editor.experimental.preferTreeSitter.typescript', "Controls whether tree sitter parsing should be turned on for typescript. This will take precedence over `editor.experimental.treeSitterTelemetry` for typescript."),
tags: ['experimental', 'onExP']
tags: ['experimental'],
experiment: {
autoRefetch: false
}
},
'editor.experimental.preferTreeSitter.ini': {
type: 'boolean',
default: false,
markdownDescription: nls.localize('editor.experimental.preferTreeSitter.ini', "Controls whether tree sitter parsing should be turned on for ini. This will take precedence over `editor.experimental.treeSitterTelemetry` for ini."),
tags: ['experimental', 'onExP']
tags: ['experimental'],
experiment: {
autoRefetch: false
}
},
'editor.experimental.preferTreeSitter.regex': {
type: 'boolean',
default: false,
markdownDescription: nls.localize('editor.experimental.preferTreeSitter.regex', "Controls whether tree sitter parsing should be turned on for regex. This will take precedence over `editor.experimental.treeSitterTelemetry` for regex."),
tags: ['experimental', 'onExP']
tags: ['experimental'],
experiment: {
autoRefetch: false
}
},
'editor.language.brackets': {
type: ['array', 'null'],

View File

@ -4468,14 +4468,20 @@ class InlineEditorSuggest extends BaseEditorOption<EditorOption.inlineSuggest, I
'editor.inlineSuggest.experimental.suppressInlineSuggestions': {
type: 'string',
default: defaults.experimental.suppressInlineSuggestions,
tags: ['experimental', 'onExp'],
description: nls.localize('inlineSuggest.suppressInlineSuggestions', "Suppresses inline completions for specified extension IDs -- comma separated.")
tags: ['experimental'],
description: nls.localize('inlineSuggest.suppressInlineSuggestions', "Suppresses inline completions for specified extension IDs -- comma separated."),
experiment: {
autoRefetch: false
}
},
'editor.inlineSuggest.experimental.triggerCommandOnProviderChange': {
type: 'boolean',
default: defaults.experimental.triggerCommandOnProviderChange,
tags: ['experimental', 'onExp'],
description: nls.localize('inlineSuggest.triggerCommandOnProviderChange', "Controls whether to trigger a command when the inline suggestion provider changes.")
tags: ['experimental'],
description: nls.localize('inlineSuggest.triggerCommandOnProviderChange', "Controls whether to trigger a command when the inline suggestion provider changes."),
experiment: {
autoRefetch: false
}
},
'editor.inlineSuggest.fontFamily': {
type: 'string',
@ -6328,7 +6334,9 @@ export const EditorOptions = {
10, 0, Constants.MAX_SAFE_SMALL_INTEGER,
{
description: nls.localize('quickSuggestionsDelay', "Controls the delay in milliseconds after which quick suggestions will show up."),
tags: ['onExP']
experiment: {
autoRefetch: false
}
}
)),
readOnly: register(new EditorBooleanOption(

View File

@ -176,7 +176,6 @@ export interface IConfigurationPropertySchema extends IJSONSchema {
* List of tags associated to the property.
* - A tag can be used for filtering
* - Use `experimental` tag for marking the setting as experimental.
* - Use `onExP` tag for marking that the default of the setting can be changed by running experiments.
*/
tags?: string[];
@ -217,6 +216,23 @@ export interface IConfigurationPropertySchema extends IJSONSchema {
* a system-wide policy.
*/
policy?: IPolicy;
/**
* When specified, this setting's default value can always be overwritten by
* an experiment.
*/
experiment?: {
/**
* Whether to automatically refetch the experiment data and
* update the configuration.
*/
autoRefetch: boolean;
/**
* The name of the experiment. By default, this is `config.${settingId}`
*/
name?: string;
};
}
export interface IExtensionInfo {
@ -657,6 +673,11 @@ class ConfigurationRegistry extends Disposable implements IConfigurationRegistry
property.restricted = types.isUndefinedOrNull(property.restricted) ? !!restrictedProperties?.includes(key) : property.restricted;
}
if (property.experiment) {
property.tags = property.tags ?? [];
property.tags.push('onExP');
}
const excluded = properties[key].hasOwnProperty('included') && !properties[key].included;
const policyName = properties[key].policy?.name;
@ -679,6 +700,7 @@ class ConfigurationRegistry extends Disposable implements IConfigurationRegistry
}
}
}
}
const subNodes = configuration.allOf;

View File

@ -271,6 +271,11 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => {
if (extensionConfigurationPolicy?.[key]) {
propertyConfiguration.policy = extensionConfigurationPolicy?.[key];
}
if (propertyConfiguration.tags?.some(tag => tag.toLowerCase() === 'onexp')) {
propertyConfiguration.experiment = {
autoRefetch: false
};
}
seenProperties.add(key);
propertyConfiguration.scope = propertyConfiguration.scope ? parseScope(propertyConfiguration.scope.toString()) : ConfigurationScope.WINDOW;
}

View File

@ -538,7 +538,9 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
'type': 'string',
'enum': ['hidden', 'visibleInWorkspace', 'visible', 'maximizedInWorkspace', 'maximized'],
'default': 'hidden',
'tags': ['onExp'],
'experiment': {
autoRefetch: false
},
'description': localize('secondarySideBarDefaultVisibility', "Controls the default visibility of the secondary side bar in workspaces or empty windows opened for the first time."),
'enumDescriptions': [
localize('workbench.secondarySideBar.defaultVisibility.hidden', "The secondary side bar is hidden by default."),
@ -626,7 +628,10 @@ const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Con
'type': 'boolean',
'default': product.quality !== 'stable',
'description': localize('settings.showAISearchToggle', "Controls whether the AI search results toggle is shown in the search bar in the Settings editor after doing a search and once AI search results are available."),
'tags': ['experimental', 'onExP']
'tags': ['experimental'],
'experiment': {
autoRefetch: false
}
},
'workbench.hover.delay': {
'type': 'number',

View File

@ -260,13 +260,19 @@ configurationRegistry.registerConfiguration({
type: 'string',
enum: ['inline', 'hover', 'input', 'none'],
default: 'inline',
tags: ['experimental', 'onExp'],
tags: ['experimental'],
experiment: {
autoRefetch: false
}
},
'chat.emptyChatState.enabled': {
type: 'boolean',
default: true,
description: nls.localize('chat.emptyChatState', "Shows a modified empty chat state with hints in the input placeholder text."),
tags: ['experimental', 'onExp'],
tags: ['experimental'],
experiment: {
autoRefetch: false
}
},
'chat.checkpoints.enabled': {
type: 'boolean',
@ -321,7 +327,9 @@ configurationRegistry.registerConfiguration({
type: 'boolean',
description: nls.localize('chat.edits2Enabled', "Enable the new Edits mode that is based on tool-calling. When this is enabled, models that don't support tool-calling are unavailable for Edits mode."),
default: true,
tags: ['onExp'],
experiment: {
autoRefetch: false
}
},
[ChatConfiguration.ExtensionToolsEnabled]: {
type: 'boolean',
@ -337,7 +345,9 @@ configurationRegistry.registerConfiguration({
type: 'boolean',
description: nls.localize('chat.agent.enabled.description', "Enable agent mode for {0}. When this is enabled, agent mode can be activated via the dropdown in the view.", 'Copilot Chat'),
default: true,
tags: ['onExp'],
experiment: {
autoRefetch: false
},
policy: {
name: 'ChatAgentMode',
minimumVersion: '1.99',
@ -499,7 +509,10 @@ configurationRegistry.registerConfiguration({
enum: ['default', 'apple'],
description: nls.localize('chat.signInDialogVariant', "Control variations of the sign-in dialog."),
default: 'default',
tags: ['onExp', 'experimental']
tags: ['experimental'],
experiment: {
autoRefetch: false
}
}
}
});

View File

@ -35,7 +35,10 @@ configurationRegistry.registerConfiguration({
markdownDescription: localize('telemetry.editStats.detailed.enabled', "Controls whether to enable telemetry for detailed edit statistics (only sends statistics if general telemetry is enabled)."),
type: 'boolean',
default: false,
tags: ['experimental', 'onExP'],
tags: ['experimental'],
experiment: {
autoRefetch: false
}
},
[EDIT_TELEMETRY_SHOW_STATUS_BAR]: {
markdownDescription: localize('telemetry.editStats.showStatusBar', "Controls whether to show the status bar for edit telemetry."),

View File

@ -63,13 +63,19 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
description: localize('enableV2', "Whether to use the next version of inline chat."),
default: false,
type: 'boolean',
tags: ['preview', 'onExp'],
tags: ['preview'],
experiment: {
autoRefetch: false
}
},
[InlineChatConfigKeys.HideOnRequest]: {
markdownDescription: localize('hideOnRequest', "Whether to hide the inline chat widget after making a request. When enabled, the widget hides after a request has been made and instead the chat overlay shows. When hidden, the widget can always be shown again with the inline chat keybinding or from the chat overlay widget. *Note* that this setting requires `#inlineChat.enableV2#` to be enabled."),
default: false,
type: 'boolean',
tags: ['preview', 'onExp'],
tags: ['preview'],
experiment: {
autoRefetch: false
}
},
}
});

View File

@ -40,8 +40,11 @@ Registry.as<IConfigurationRegistry>(ConfigExt.Configuration).registerConfigurati
'application.experimental.rendererProfiling': {
type: 'boolean',
default: false,
tags: ['experimental', 'onExP'],
markdownDescription: localize('experimental.rendererProfiling', "When enabled, slow renderers are automatically profiled.")
tags: ['experimental'],
markdownDescription: localize('experimental.rendererProfiling', "When enabled, slow renderers are automatically profiled."),
experiment: {
autoRefetch: false
}
}
}
});

View File

@ -8,7 +8,7 @@ import { Event, Emitter } from '../../../../base/common/event.js';
import { ResourceMap } from '../../../../base/common/map.js';
import { equals } from '../../../../base/common/objects.js';
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
import { Queue, Barrier, Promises, Delayer } from '../../../../base/common/async.js';
import { Queue, Barrier, Promises, Delayer, RunOnceScheduler } from '../../../../base/common/async.js';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js';
import { IWorkspaceContextService, Workspace as BaseWorkspace, WorkbenchState, IWorkspaceFolder, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder, isWorkspaceFolder, IWorkspaceFoldersWillChangeEvent, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier, IAnyWorkspaceIdentifier } from '../../../../platform/workspace/common/workspace.js';
import { ConfigurationModel, ConfigurationChangeEvent, mergeChanges } from '../../../../platform/configuration/common/configurationModels.js';
@ -47,6 +47,7 @@ import { IBrowserWorkbenchEnvironmentService } from '../../environment/browser/e
import { workbenchConfigurationNodeBase } from '../../../common/configuration.js';
import { mainWindow } from '../../../../base/browser/window.js';
import { runWhenWindowIdle } from '../../../../base/browser/dom.js';
import { ASSIGNMENT_REFETCH_INTERVAL } from '../../../../platform/assignment/common/assignment.js';
function getLocalUserConfigurationScopes(userDataProfile: IUserDataProfile, hasRemote: boolean): ConfigurationScope[] | undefined {
const isDefaultProfile = userDataProfile.isDefault || userDataProfile.useDefaultFlags?.settings;
@ -1338,7 +1339,9 @@ class ConfigurationDefaultOverridesContribution extends Disposable implements IW
static readonly ID = 'workbench.contrib.configurationDefaultOverridesContribution';
private readonly processedExperimentalSettings = new Set<string>();
private readonly autoRefetchExperimentalSettings = new Set<string>();
private readonly configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
private readonly autoRefetchExperimentalSettingsScheduler: RunOnceScheduler;
constructor(
@IWorkbenchAssignmentService private readonly workbenchAssignmentService: IWorkbenchAssignmentService,
@ -1348,17 +1351,23 @@ class ConfigurationDefaultOverridesContribution extends Disposable implements IW
) {
super();
this.autoRefetchExperimentalSettingsScheduler = new RunOnceScheduler(() => {
this.processExperimentalSettings(this.autoRefetchExperimentalSettings, true);
this.autoRefetchExperimentalSettingsScheduler.schedule();
}, ASSIGNMENT_REFETCH_INTERVAL);
this.updateDefaults();
// When configuration is updated make sure to apply experimental configuration overrides
this._register(this.configurationRegistry.onDidUpdateConfiguration(({ properties }) => this.processExperimentalSettings(properties)));
this._register(this.configurationRegistry.onDidUpdateConfiguration(({ properties }) => this.processExperimentalSettings(properties, false)));
}
private async updateDefaults(): Promise<void> {
this.logService.trace('ConfigurationService#updateDefaults: begin');
try {
// Check for experiments
await this.processExperimentalSettings(Object.keys(this.configurationRegistry.getConfigurationProperties()));
await this.processExperimentalSettings(Object.keys(this.configurationRegistry.getConfigurationProperties()), false);
} finally {
// Invalidate defaults cache after extensions have registered
// and after the experiments have been resolved to prevent
@ -1367,26 +1376,28 @@ class ConfigurationDefaultOverridesContribution extends Disposable implements IW
this.logService.trace('ConfigurationService#updateDefaults: resetting the defaults');
this.configurationService.reloadConfiguration(ConfigurationTarget.DEFAULT);
}
// Schedule auto-refetch of experimental settings
this.autoRefetchExperimentalSettingsScheduler.schedule();
}
private async processExperimentalSettings(properties: Iterable<string>): Promise<void> {
private async processExperimentalSettings(properties: Iterable<string>, autoRefetch: boolean): Promise<void> {
const overrides: IStringDictionary<any> = {};
const allProperties = this.configurationRegistry.getConfigurationProperties();
for (const property of properties) {
const schema = allProperties[property];
const tags = schema?.tags;
// Many experimental settings refer to in-development or unstable settings.
// onExP more clearly indicates that the setting could be
// part of an experiment.
if (!tags || !tags.some(tag => tag.toLowerCase() === 'onexp')) {
if (!schema.experiment) {
continue;
}
if (this.processedExperimentalSettings.has(property)) {
if (!autoRefetch && this.processedExperimentalSettings.has(property)) {
continue;
}
this.processedExperimentalSettings.add(property);
if (schema.experiment.autoRefetch) {
this.autoRefetchExperimentalSettings.add(property);
}
try {
const value = await this.workbenchAssignmentService.getTreatment(`config.${property}`);
const value = await this.workbenchAssignmentService.getTreatment(schema.experiment.name ?? `config.${property}`);
if (!isUndefined(value) && !equals(value, schema.default)) {
overrides[property] = value;
}