Skip to content

Commit

Permalink
#84756 Support reading a object type setting with simple value types
Browse files Browse the repository at this point in the history
  • Loading branch information
sandy081 committed Nov 24, 2024
1 parent 8eb7fac commit e293c73
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,9 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
},
{
type: 'array',
items: {
type: 'string',
},
description: localize('extensions.allow.version.description', "Allow or disallow specific versions of the extension. To specifcy a platform specific version, use the format `[email protected]`, e.g. `[email protected]`. Supported platforms are `win32-x64`, `win32-arm64`, `linux-x64`, `linux-arm64`, `linux-armhf`, `alpine-x64`, `alpine-arm64`, `darwin-x64`, `darwin-arm64`"),
},
]
Expand Down
51 changes: 51 additions & 0 deletions src/vs/workbench/contrib/preferences/browser/settingsTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,10 @@ interface ISettingComplexItemTemplate extends ISettingItemTemplate<void> {
validationErrorMessageElement: HTMLElement;
}

interface ISettingComplexObjectItemTemplate extends ISettingComplexItemTemplate {
objectSettingWidget: ObjectSettingDropdownWidget;
}

interface ISettingListItemTemplate extends ISettingItemTemplate<string[] | undefined> {
listWidget: ListSettingWidget<IListDataItem>;
validationErrorMessageElement: HTMLElement;
Expand Down Expand Up @@ -725,6 +729,7 @@ const SETTINGS_INCLUDE_TEMPLATE_ID = 'settings.include.template';
const SETTINGS_OBJECT_TEMPLATE_ID = 'settings.object.template';
const SETTINGS_BOOL_OBJECT_TEMPLATE_ID = 'settings.boolObject.template';
const SETTINGS_COMPLEX_TEMPLATE_ID = 'settings.complex.template';
const SETTINGS_COMPLEX_OBJECT_TEMPLATE_ID = 'settings.complexObject.template';
const SETTINGS_NEW_EXTENSIONS_TEMPLATE_ID = 'settings.newExtensions.template';
const SETTINGS_ELEMENT_TEMPLATE_ID = 'settings.group.template';
const SETTINGS_EXTENSION_TOGGLE_TEMPLATE_ID = 'settings.extensionToggle.template';
Expand Down Expand Up @@ -1205,6 +1210,47 @@ export class SettingComplexRenderer extends AbstractSettingRenderer implements I
}
}

class SettingComplexObjectRenderer extends SettingComplexRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingComplexObjectItemTemplate> {

override templateId = SETTINGS_COMPLEX_OBJECT_TEMPLATE_ID;

override renderTemplate(container: HTMLElement): ISettingComplexObjectItemTemplate {
const common = this.renderCommonTemplate(null, container, 'list');

const objectSettingWidget = common.toDispose.add(this._instantiationService.createInstance(ObjectSettingDropdownWidget, common.controlElement));
objectSettingWidget.domNode.classList.add(AbstractSettingRenderer.CONTROL_CLASS);

const openSettingsButton = DOM.append(common.controlElement, $('a.edit-in-settings-button'));
openSettingsButton.classList.add(AbstractSettingRenderer.CONTROL_CLASS);
openSettingsButton.role = 'button';

const validationErrorMessageElement = $('.setting-item-validation-message');
common.containerElement.appendChild(validationErrorMessageElement);

const template: ISettingComplexObjectItemTemplate = {
...common,
button: openSettingsButton,
validationErrorMessageElement,
objectSettingWidget
};

this.addSettingElementFocusHandler(template);

return template;
}

protected override renderValue(dataElement: SettingsTreeSettingElement, template: ISettingComplexObjectItemTemplate, onChange: (value: string) => void): void {
template.objectSettingWidget.domNode.classList.remove('hide');
const items = getObjectDisplayValue(dataElement);
template.objectSettingWidget.setValue(items, {
settingKey: dataElement.setting.key,
showAddButton: false,
isReadOnly: true,
});
super.renderValue(dataElement, template, onChange);
}
}

class SettingArrayRenderer extends AbstractSettingRenderer implements ITreeRenderer<SettingsTreeSettingElement, never, ISettingListItemTemplate> {
templateId = SETTINGS_ARRAY_TEMPLATE_ID;

Expand Down Expand Up @@ -2128,6 +2174,7 @@ export class SettingTreeRenderers extends Disposable {
this._instantiationService.createInstance(SettingNumberRenderer, this.settingActions, actionFactory),
this._instantiationService.createInstance(SettingArrayRenderer, this.settingActions, actionFactory),
this._instantiationService.createInstance(SettingComplexRenderer, this.settingActions, actionFactory),
this._instantiationService.createInstance(SettingComplexObjectRenderer, this.settingActions, actionFactory),
this._instantiationService.createInstance(SettingTextRenderer, this.settingActions, actionFactory),
this._instantiationService.createInstance(SettingMultilineTextRenderer, this.settingActions, actionFactory),
this._instantiationService.createInstance(SettingExcludeRenderer, this.settingActions, actionFactory),
Expand Down Expand Up @@ -2417,6 +2464,10 @@ class SettingsTreeDelegate extends CachedListVirtualDelegate<SettingsTreeGroupCh
return SETTINGS_BOOL_OBJECT_TEMPLATE_ID;
}

if (element.valueType === SettingValueType.ComplexObject) {
return SETTINGS_COMPLEX_OBJECT_TEMPLATE_ID;
}

if (element.valueType === SettingValueType.LanguageTag) {
return SETTINGS_COMPLEX_TEMPLATE_ID;
}
Expand Down
90 changes: 69 additions & 21 deletions src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,16 +253,21 @@ export class SettingsTreeSettingElement extends SettingsTreeElement {
} else {
this.valueType = SettingValueType.Complex;
}
} else if (isObjectSetting(this.setting)) {
if (this.setting.allKeysAreBoolean) {
this.valueType = SettingValueType.BooleanObject;
} else {
const schemaType = getObjectSettingSchemaType(this.setting);
if (schemaType) {
if (this.setting.allKeysAreBoolean) {
this.valueType = SettingValueType.BooleanObject;
} else if (schemaType === 'simple') {
this.valueType = SettingValueType.Object;
} else {
this.valueType = SettingValueType.ComplexObject;
}
} else if (this.setting.isLanguageTagSetting) {
this.valueType = SettingValueType.LanguageTag;
} else {
this.valueType = SettingValueType.Object;
this.valueType = SettingValueType.Complex;
}
} else if (this.setting.isLanguageTagSetting) {
this.valueType = SettingValueType.LanguageTag;
} else {
this.valueType = SettingValueType.Complex;
}
}

Expand Down Expand Up @@ -798,25 +803,63 @@ export function objectSettingSupportsRemoveDefaultValue(key: string): boolean {
return key === 'workbench.editor.customLabels.patterns';
}

function isObjectRenderableSchema({ type }: IJSONSchema, key: string): boolean {
if (type === 'string' || type === 'boolean' || type === 'integer' || type === 'number') {
return true;
function isSimpleType(type: string | undefined): boolean {
return type === 'string' || type === 'boolean' || type === 'integer' || type === 'number';
}

function getObjectRenderableSchemaType(schema: IJSONSchema, key: string): 'simple' | 'complex' | false {
const { type } = schema;

if (Array.isArray(type)) {
for (const t of type) {
if (!isSimpleType(t)) {
return false;
}
}
return 'complex';
}

if (isSimpleType(type)) {
return 'simple';
}

if (objectSettingSupportsRemoveDefaultValue(key) && Array.isArray(type) && type.length === 2) {
return type.includes('null') && (type.includes('string') || type.includes('boolean') || type.includes('integer') || type.includes('number'));
if (type.includes('null') && (type.includes('string') || type.includes('boolean') || type.includes('integer') || type.includes('number'))) {
return 'simple';
}
}

if (type === 'array') {
if (schema.items) {
const itemSchemas = Array.isArray(schema.items) ? schema.items : [schema.items];
for (const { type } of itemSchemas) {
if (Array.isArray(type)) {
for (const t of type) {
if (!isSimpleType(t)) {
return false;
}
}
return 'complex';
}
if (!isSimpleType(type)) {
return false;
}
return 'complex';
}
}
return false;
}

return false;
}

function isObjectSetting({
function getObjectSettingSchemaType({
key,
type,
objectProperties,
objectPatternProperties,
objectAdditionalProperties
}: ISetting): boolean {
}: ISetting): 'simple' | 'complex' | false {
if (type !== 'object') {
return false;
}
Expand Down Expand Up @@ -845,15 +888,20 @@ function isObjectSetting({
schemas.push(objectAdditionalProperties);
}

// Flatten anyof schemas
const flatSchemas = schemas.map((schema): IJSONSchema[] => {
if (Array.isArray(schema.anyOf)) {
return schema.anyOf;
let schemaType: 'simple' | 'complex' | false = 'simple';
for (const schema of schemas) {
for (const subSchema of Array.isArray(schema.anyOf) ? schema.anyOf : [schema]) {
const subSchemaType = getObjectRenderableSchemaType(subSchema, key);
if (subSchemaType === false) {
return false;
}
if (subSchemaType === 'complex') {
schemaType = 'complex';
}
}
return [schema];
}).flat();
}

return flatSchemas.every((schema) => isObjectRenderableSchema(schema, key));
return schemaType;
}

function settingTypeEnumRenderable(_type: string | string[]) {
Expand Down
28 changes: 23 additions & 5 deletions src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ export abstract class AbstractListSettingWidget<TDataItem extends object> extend
return this.model.items;
}

get inReadMode(): boolean {
return this.model.items.every(item => !item.editing);
protected get isReadOnly(): boolean {
return false;
}

constructor(
Expand Down Expand Up @@ -373,6 +373,10 @@ export abstract class AbstractListSettingWidget<TDataItem extends object> extend
return;
}

if (this.isReadOnly) {
return;
}

const item = this.model.items[targetIdx];
if (item) {
this.editSetting(targetIdx);
Expand Down Expand Up @@ -481,6 +485,9 @@ export class ListSettingWidget<TListDataItem extends IListDataItem> extends Abst
}

protected getActionsForItem(item: TListDataItem, idx: number): IAction[] {
if (this.isReadOnly) {
return [];
}
return [
{
class: ThemeIcon.asClassName(settingsEditIcon),
Expand Down Expand Up @@ -520,7 +527,7 @@ export class ListSettingWidget<TListDataItem extends IListDataItem> extends Abst
}

protected addDragAndDrop(rowElement: HTMLElement, item: TListDataItem, idx: number) {
if (this.inReadMode) {
if (this.model.items.every(item => !item.editing)) {
rowElement.draggable = true;
rowElement.classList.add('draggable');
} else {
Expand Down Expand Up @@ -898,8 +905,9 @@ export interface IObjectKeySuggester {
interface IObjectSetValueOptions {
settingKey: string;
showAddButton: boolean;
keySuggester: IObjectKeySuggester;
valueSuggester: IObjectValueSuggester;
isReadOnly?: boolean;
keySuggester?: IObjectKeySuggester;
valueSuggester?: IObjectValueSuggester;
}

interface IObjectRenderEditWidgetOptions {
Expand All @@ -911,6 +919,7 @@ interface IObjectRenderEditWidgetOptions {
}

export class ObjectSettingDropdownWidget extends AbstractListSettingWidget<IObjectDataItem> {
private editable: boolean = true;
private currentSettingKey: string = '';
private showAddButton: boolean = true;
private keySuggester: IObjectKeySuggester = () => undefined;
Expand All @@ -926,6 +935,7 @@ export class ObjectSettingDropdownWidget extends AbstractListSettingWidget<IObje
}

override setValue(listData: IObjectDataItem[], options?: IObjectSetValueOptions): void {
this.editable = !options?.isReadOnly;
this.showAddButton = options?.showAddButton ?? this.showAddButton;
this.keySuggester = options?.keySuggester ?? this.keySuggester;
this.valueSuggester = options?.valueSuggester ?? this.valueSuggester;
Expand All @@ -947,6 +957,10 @@ export class ObjectSettingDropdownWidget extends AbstractListSettingWidget<IObje
return this.showAddButton;
}

protected override get isReadOnly(): boolean {
return !this.editable;
}

protected getEmptyItem(): IObjectDataItem {
return {
key: { type: 'string', data: '' },
Expand All @@ -961,6 +975,10 @@ export class ObjectSettingDropdownWidget extends AbstractListSettingWidget<IObje
}

protected getActionsForItem(item: IObjectDataItem, idx: number): IAction[] {
if (this.isReadOnly) {
return [];
}

const actions: IAction[] = [
{
class: ThemeIcon.asClassName(settingsEditIcon),
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/services/preferences/common/preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ export enum SettingValueType {
Object = 'object',
BooleanObject = 'boolean-object',
LanguageTag = 'language-tag',
ExtensionToggle = 'extension-toggle'
ExtensionToggle = 'extension-toggle',
ComplexObject = 'complex-object',
}

export interface ISettingsGroup {
Expand Down

0 comments on commit e293c73

Please sign in to comment.