Skip to content

Commit 731d08f

Browse files
authored
Add experimental settings type (v2) (microsoft#183263)
1 parent 51f1d2d commit 731d08f

File tree

9 files changed

+295
-33
lines changed

9 files changed

+295
-33
lines changed

src/vs/base/common/product.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ export interface IProductConfiguration {
191191
readonly 'editSessions.store'?: Omit<ConfigurationSyncStore, 'insidersUrl' | 'stableUrl'>;
192192
readonly darwinUniversalAssetId?: string;
193193
readonly profileTemplatesUrl?: string;
194+
195+
readonly commonlyUsedSettings?: string[];
194196
}
195197

196198
export interface ITunnelApplicationConfig {
@@ -201,6 +203,11 @@ export interface ITunnelApplicationConfig {
201203

202204
export interface IExtensionRecommendations {
203205
readonly onFileOpen: IFileOpenCondition[];
206+
readonly onSettingsEditorOpen?: ISettingsEditorOpenCondition;
207+
}
208+
209+
export interface ISettingsEditorOpenCondition {
210+
readonly prerelease: boolean | string;
204211
}
205212

206213
export interface IExtensionRecommendationCondition {

src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@
160160
color: var(--vscode-settings-headerForeground);
161161
}
162162

163+
.settings-editor > .settings-body .settings-tree-container .setting-item-extension-toggle .setting-item-extension-toggle-button {
164+
display: block;
165+
width: fit-content;
166+
}
167+
163168
.settings-editor.no-results > .settings-body .settings-toc-container,
164169
.settings-editor.no-results > .settings-body .settings-tree-container {
165170
display: none;
@@ -481,6 +486,7 @@
481486
.settings-editor > .settings-body .settings-tree-container .setting-item.setting-item-number input[type=number] {
482487
/* Hide arrow button that shows in type=number fields */
483488
-moz-appearance: textfield !important;
489+
appearance: textfield !important;
484490
}
485491

486492
.settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-markdown * {
@@ -556,7 +562,7 @@
556562
padding: 0px;
557563
}
558564

559-
.settings-editor > .settings-body .settings-tree-container .setting-item-bool .setting-value-checkbox.codicon:not(.checked)::before {
565+
.settings-editor > .settings-body .settings-tree-container .setting-item-bool .setting-value-checkbox.codicon:not(.checked)::before {
560566
opacity: 0;
561567
}
562568

src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { ITreeElement } from 'vs/base/browser/ui/tree/tree';
1212
import { Action } from 'vs/base/common/actions';
1313
import { Delayer, IntervalTimer, ThrottledDelayer, timeout } from 'vs/base/common/async';
1414
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
15-
import * as collections from 'vs/base/common/collections';
1615
import { fromNow } from 'vs/base/common/date';
1716
import { isCancellationError } from 'vs/base/common/errors';
1817
import { Emitter, Event } from 'vs/base/common/event';
@@ -39,16 +38,16 @@ import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
3938
import { IEditorMemento, IEditorOpenContext, IEditorPane } from 'vs/workbench/common/editor';
4039
import { SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput';
4140
import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets';
42-
import { commonlyUsedData, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout';
41+
import { getCommonlyUsedData, tocData } from 'vs/workbench/contrib/preferences/browser/settingsLayout';
4342
import { AbstractSettingRenderer, HeightChangeParams, ISettingLinkClickEvent, resolveConfiguredUntrustedSettings, createTocTreeForExtensionSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers } from 'vs/workbench/contrib/preferences/browser/settingsTree';
4443
import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels';
4544
import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree';
46-
import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, ENABLE_LANGUAGE_FILTER, EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, ID_SETTING_TAG, IPreferencesSearchService, ISearchProvider, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG, POLICY_SETTING_TAG, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SUGGEST_FILTERS, WORKSPACE_TRUST_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences';
45+
import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, ENABLE_LANGUAGE_FILTER, EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, ID_SETTING_TAG, IPreferencesSearchService, ISearchProvider, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG, POLICY_SETTING_TAG, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SUGGEST_FILTERS, WORKSPACE_TRUST_SETTING_TAG, getExperimentalExtensionToggleData } from 'vs/workbench/contrib/preferences/common/preferences';
4746
import { settingsHeaderBorder, settingsSashBorder, settingsTextInputBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry';
4847
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
49-
import { IOpenSettingsOptions, IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingMatchType, SettingValueType, validateSettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences';
48+
import { IOpenSettingsOptions, IPreferencesService, ISearchResult, ISetting, ISettingsEditorModel, ISettingsEditorOptions, ISettingsGroup, SettingMatchType, SettingValueType, validateSettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences';
5049
import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
51-
import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
50+
import { Settings2EditorModel, nullRange } from 'vs/workbench/services/preferences/common/preferencesModels';
5251
import { IUserDataSyncWorkbenchService } from 'vs/workbench/services/userDataSync/common/userDataSync';
5352
import { preferencesClearInputIcon, preferencesFilterIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons';
5453
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
@@ -59,11 +58,14 @@ import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/spl
5958
import { Color } from 'vs/base/common/color';
6059
import { ILanguageService } from 'vs/editor/common/languages/language';
6160
import { SettingsSearchFilterDropdownMenuActionViewItem } from 'vs/workbench/contrib/preferences/browser/settingsSearchMenu';
62-
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
61+
import { IExtensionGalleryService, IExtensionManagementService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
6362
import { ISettingOverrideClickEvent } from 'vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators';
6463
import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
6564
import { Registry } from 'vs/platform/registry/common/platform';
6665
import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
66+
import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService';
67+
import { IProductService } from 'vs/platform/product/common/productService';
68+
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
6769

6870
export const enum SettingsFocusContext {
6971
Search,
@@ -229,7 +231,11 @@ export class SettingsEditor2 extends EditorPane {
229231
@IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService,
230232
@IExtensionService private readonly extensionService: IExtensionService,
231233
@ILanguageService private readonly languageService: ILanguageService,
232-
@IExtensionManagementService extensionManagementService: IExtensionManagementService
234+
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
235+
@IWorkbenchAssignmentService private readonly workbenchAssignmentService: IWorkbenchAssignmentService,
236+
@IProductService private readonly productService: IProductService,
237+
@IEnvironmentService private readonly environmentService: IEnvironmentService,
238+
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
233239
) {
234240
super(SettingsEditor2.ID, telemetryService, themeService, storageService);
235241
this.delayedFilterLogging = new Delayer<void>(1000);
@@ -1189,14 +1195,49 @@ export class SettingsEditor2 extends EditorPane {
11891195
});
11901196
}
11911197

1198+
private addOrRemoveManageExtensionSetting(setting: ISetting, extension: IGalleryExtension, groups: ISettingsGroup[]): ISettingsGroup | undefined {
1199+
const extensionId = setting.extensionId!;
1200+
const matchingGroups = groups.filter(g => g.extensionInfo?.id.toLowerCase() === extensionId.toLowerCase());
1201+
if (!matchingGroups.length) {
1202+
const newGroup: ISettingsGroup = {
1203+
sections: [{
1204+
settings: [setting],
1205+
}],
1206+
id: extensionId,
1207+
title: setting.extensionGroupTitle!,
1208+
titleRange: nullRange,
1209+
range: nullRange,
1210+
extensionInfo: {
1211+
id: extensionId,
1212+
displayName: extension?.displayName,
1213+
}
1214+
};
1215+
groups.push(newGroup);
1216+
return newGroup;
1217+
} else if (matchingGroups.length >= 2) {
1218+
// Remove the group with the manage extension setting.
1219+
const matchingGroupIndex = matchingGroups.findIndex(group =>
1220+
group.sections.length === 1 && group.sections[0].settings.length === 1 && group.sections[0].settings[0].extensionId);
1221+
if (matchingGroupIndex !== -1) {
1222+
groups.splice(matchingGroupIndex, 1);
1223+
}
1224+
}
1225+
return undefined;
1226+
}
1227+
11921228
private async onConfigUpdate(keys?: ReadonlySet<string>, forceRefresh = false, schemaChange = false): Promise<void> {
11931229
if (keys && this.settingsTreeModel) {
11941230
return this.updateElementsByKey(keys);
11951231
}
11961232

1233+
if (!this.defaultSettingsEditorModel) {
1234+
return;
1235+
}
1236+
11971237
const groups = this.defaultSettingsEditorModel.settingsGroups.slice(1); // Without commonlyUsed
1198-
const dividedGroups = collections.groupBy(groups, g => g.extensionInfo ? 'extension' : 'core');
1199-
const settingsResult = resolveSettingsTree(tocData, dividedGroups.core, this.logService);
1238+
1239+
const coreSettings = groups.filter(g => !g.extensionInfo);
1240+
const settingsResult = resolveSettingsTree(tocData, coreSettings, this.logService);
12001241
const resolvedSettingsRoot = settingsResult.tree;
12011242

12021243
// Warn for settings not included in layout
@@ -1210,10 +1251,61 @@ export class SettingsEditor2 extends EditorPane {
12101251
this.hasWarnedMissingSettings = true;
12111252
}
12121253

1213-
const commonlyUsed = resolveSettingsTree(commonlyUsedData, dividedGroups.core, this.logService);
1254+
const additionalGroups: ISettingsGroup[] = [];
1255+
const toggleData = await getExperimentalExtensionToggleData(this.workbenchAssignmentService, this.environmentService, this.productService);
1256+
if (toggleData && groups.filter(g => g.extensionInfo).length) {
1257+
for (const key in toggleData.settingsEditorRecommendedExtensions) {
1258+
const prerelease = toggleData.settingsEditorRecommendedExtensions[key].onSettingsEditorOpen!.prerelease;
1259+
1260+
const extensionId = (typeof prerelease === 'string' && this.productService.quality !== 'stable') ? prerelease : key;
1261+
const [extension] = await this.extensionGalleryService.getExtensions([{ id: extensionId }], CancellationToken.None);
1262+
if (!extension) {
1263+
continue;
1264+
}
1265+
1266+
let groupTitle: string | undefined;
1267+
const manifest = await this.extensionGalleryService.getManifest(extension, CancellationToken.None);
1268+
const contributesConfiguration = manifest?.contributes?.configuration;
1269+
if (!Array.isArray(contributesConfiguration)) {
1270+
groupTitle = contributesConfiguration?.title;
1271+
} else if (contributesConfiguration.length === 1) {
1272+
groupTitle = contributesConfiguration[0].title;
1273+
}
1274+
1275+
const extensionName = extension?.displayName ?? extension?.name ?? extensionId;
1276+
const settingKey = `${key}.manageExtension`;
1277+
const setting: ISetting = {
1278+
range: nullRange,
1279+
key: settingKey,
1280+
keyRange: nullRange,
1281+
value: null,
1282+
valueRange: nullRange,
1283+
description: [extension?.description || ''],
1284+
descriptionIsMarkdown: false,
1285+
descriptionRanges: [],
1286+
title: localize('manageExtension', "Manage {0}", extensionName),
1287+
scope: ConfigurationScope.WINDOW,
1288+
type: 'null',
1289+
extensionId: extensionId,
1290+
extensionGroupTitle: groupTitle ?? extensionName
1291+
};
1292+
const additionalGroup = this.addOrRemoveManageExtensionSetting(setting, extension, groups);
1293+
if (additionalGroup) {
1294+
additionalGroups.push(additionalGroup);
1295+
}
1296+
}
1297+
}
1298+
1299+
resolvedSettingsRoot.children!.push(await createTocTreeForExtensionSettings(this.extensionService, groups.filter(g => g.extensionInfo)));
1300+
1301+
const commonlyUsedDataToUse = await getCommonlyUsedData(this.workbenchAssignmentService, this.environmentService, this.productService);
1302+
const commonlyUsed = resolveSettingsTree(commonlyUsedDataToUse, groups, this.logService);
12141303
resolvedSettingsRoot.children!.unshift(commonlyUsed.tree);
12151304

1216-
resolvedSettingsRoot.children!.push(await createTocTreeForExtensionSettings(this.extensionService, dividedGroups.extension || []));
1305+
if (toggleData) {
1306+
// Add the additional groups to the model to help with searching.
1307+
this.defaultSettingsEditorModel.setAdditionalGroups(additionalGroups);
1308+
}
12171309

12181310
if (!this.workspaceTrustManagementService.isWorkspaceTrusted() && (this.viewState.settingsTarget instanceof URI || this.viewState.settingsTarget === ConfigurationTarget.WORKSPACE)) {
12191311
const configuredUntrustedWorkspaceSettings = resolveConfiguredUntrustedSettings(groups, this.viewState.settingsTarget, this.viewState.languageFilter, this.configurationService);

src/vs/workbench/contrib/preferences/browser/settingsLayout.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55

66
import { isWindows } from 'vs/base/common/platform';
77
import { localize } from 'vs/nls';
8+
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
9+
import { IProductService } from 'vs/platform/product/common/productService';
10+
import { getExperimentalExtensionToggleData } from 'vs/workbench/contrib/preferences/common/preferences';
11+
import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService';
812
export interface ITOCEntry<T> {
913
id: string;
1014
label: string;
@@ -13,11 +17,29 @@ export interface ITOCEntry<T> {
1317
settings?: Array<T>;
1418
}
1519

16-
export const commonlyUsedData: ITOCEntry<string> = {
17-
id: 'commonlyUsed',
18-
label: localize('commonlyUsed', "Commonly Used"),
19-
settings: ['files.autoSave', 'editor.fontSize', 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace', 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces', 'editor.wordWrap', 'files.exclude', 'files.associations', 'workbench.editor.enablePreview']
20-
};
20+
const defaultCommonlyUsedSettings: string[] = [
21+
'files.autoSave',
22+
'editor.fontSize',
23+
'editor.fontFamily',
24+
'editor.tabSize',
25+
'editor.renderWhitespace',
26+
'editor.cursorStyle',
27+
'editor.multiCursorModifier',
28+
'editor.insertSpaces',
29+
'editor.wordWrap',
30+
'files.exclude',
31+
'files.associations',
32+
'workbench.editor.enablePreview'
33+
];
34+
35+
export async function getCommonlyUsedData(workbenchAssignmentService: IWorkbenchAssignmentService, environmentService: IEnvironmentService, productService: IProductService): Promise<ITOCEntry<string>> {
36+
const toggleData = await getExperimentalExtensionToggleData(workbenchAssignmentService, environmentService, productService);
37+
return {
38+
id: 'commonlyUsed',
39+
label: localize('commonlyUsed', "Commonly Used"),
40+
settings: toggleData ? toggleData.commonlyUsed : defaultCommonlyUsedSettings
41+
};
42+
}
2143

2244
export const tocData: ITOCEntry<string> = {
2345
id: 'root',

0 commit comments

Comments
 (0)