Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support reading a object type setting with simple value types #234517

Merged
merged 4 commits into from
Nov 27, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,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
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,10 @@
opacity: 0.9;
}

.settings-editor > .settings-body .settings-tree-container .setting-item-contents .complex-object-edit-in-settings-button-container {
margin-top: 9px;
}

.settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-markdown a,
.settings-editor > .settings-body .settings-tree-container .setting-item-contents .edit-in-settings-button,
.settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-markdown a > code {
Expand Down
50 changes: 50 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,46 @@ 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(DOM.append(common.controlElement, $('.complex-object-edit-in-settings-button-container')), $('a.complex-object.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 {
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 +2173,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 +2463,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
92 changes: 70 additions & 22 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)) {
if (objectSettingSupportsRemoveDefaultValue(key) && type.length === 2) {
if (type.includes('null') && (type.includes('string') || type.includes('boolean') || type.includes('integer') || type.includes('number'))) {
return 'simple';
}
}

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

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 (isSimpleType(type)) {
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
Loading