diff --git a/packages/core/src/common/preferences/preference-schema.ts b/packages/core/src/common/preferences/preference-schema.ts index d176dd6313519..0e456f861a3d8 100644 --- a/packages/core/src/common/preferences/preference-schema.ts +++ b/packages/core/src/common/preferences/preference-schema.ts @@ -84,6 +84,7 @@ export interface PreferenceDataProperty extends PreferenceItem { description?: string; markdownDescription?: string; scope?: PreferenceScope; + typeDetails?: any; } export namespace PreferenceDataProperty { export function fromPreferenceSchemaProperty(schemaProps: PreferenceSchemaProperty, defaultScope: PreferenceScope = PreferenceScope.Workspace): PreferenceDataProperty { diff --git a/packages/external-terminal/src/electron-browser/external-terminal-preference.ts b/packages/external-terminal/src/electron-browser/external-terminal-preference.ts index c9b8705b48d11..3080d37d28f75 100644 --- a/packages/external-terminal/src/electron-browser/external-terminal-preference.ts +++ b/packages/external-terminal/src/electron-browser/external-terminal-preference.ts @@ -87,6 +87,7 @@ export async function getExternalTerminalSchema(externalTerminalService: Externa properties: { 'terminal.external.windowsExec': { type: 'string', + typeDetails: { isFilepath: true }, description: nls.localizeByDefault('Customizes which terminal to run on Windows.'), default: `${isWindows ? hostExec : 'C:\\WINDOWS\\System32\\cmd.exe'}` }, diff --git a/packages/preferences/src/browser/style/index.css b/packages/preferences/src/browser/style/index.css index b74c8ad983825..f573cb7b07d34 100644 --- a/packages/preferences/src/browser/style/index.css +++ b/packages/preferences/src/browser/style/index.css @@ -28,6 +28,7 @@ @import url("./preference-context-menu.css"); @import url("./preference-array.css"); +@import url("./preference-file.css"); @import url("./preference-object.css"); @import url("./search-input.css"); diff --git a/packages/preferences/src/browser/style/preference-file.css b/packages/preferences/src/browser/style/preference-file.css new file mode 100644 index 0000000000000..96783311fd4dc --- /dev/null +++ b/packages/preferences/src/browser/style/preference-file.css @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (C) 2022 EclipseSource and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ + +.theia-settings-container .preference-file-container { + position: relative; + display: flex; + align-items: center; +} + +.theia-settings-container .preference-file-input { + flex: 1; + padding-right: 4px; +} + +.theia-settings-container .preference-file-button { + position: absolute; + padding: 2px 9px; + right: 4px; +} diff --git a/packages/preferences/src/browser/util/preference-types.ts b/packages/preferences/src/browser/util/preference-types.ts index 35dfc0e3cad88..7c4bdb2a35078 100644 --- a/packages/preferences/src/browser/util/preference-types.ts +++ b/packages/preferences/src/browser/util/preference-types.ts @@ -21,6 +21,7 @@ import { CompositeTreeNode as BaseCompositeTreeNode, PreferenceInspection, CommonCommands, + JsonType, } from '@theia/core/lib/browser'; import { Command, MenuPath } from '@theia/core'; import { JSONValue } from '@theia/core/shared/@phosphor/coreutils'; @@ -69,6 +70,10 @@ export namespace Preference { export namespace LeafNode { export const is = (node: BaseTreeNode | LeafNode): node is LeafNode => 'preference' in node && !!node.preference.data; + + export const getType = (node: BaseTreeNode | LeafNode): JsonType | undefined => is(node) + ? Array.isArray(node.preference.data.type) ? node.preference.data.type[0] : node.preference.data.type + : undefined; } export const getValueInScope = (preferenceInfo: PreferenceInspection | undefined, scope: number): T | undefined => { diff --git a/packages/preferences/src/browser/views/components/preference-array-input.ts b/packages/preferences/src/browser/views/components/preference-array-input.ts index 901adbbb49d00..4bae85d10106c 100644 --- a/packages/preferences/src/browser/views/components/preference-array-input.ts +++ b/packages/preferences/src/browser/views/components/preference-array-input.ts @@ -15,8 +15,11 @@ // ***************************************************************************** import { codiconArray } from '@theia/core/lib/browser'; -import { injectable } from '@theia/core/shared/inversify'; -import { PreferenceLeafNodeRenderer } from './preference-node-renderer'; +import { injectable, interfaces } from '@theia/core/shared/inversify'; +import { IJSONSchema } from '@theia/core/lib/common/json-schema'; +import { Preference } from '../../util/preference-types'; +import { PreferenceLeafNodeRenderer, PreferenceNodeRenderer } from './preference-node-renderer'; +import { PreferenceLeafNodeRendererContribution } from './preference-node-renderer-creator'; @injectable() export class PreferenceArrayInputRenderer extends PreferenceLeafNodeRenderer { @@ -154,3 +157,18 @@ export class PreferenceArrayInputRenderer extends PreferenceLeafNodeRenderer { @@ -51,3 +53,17 @@ export class PreferenceBooleanInputRenderer extends PreferenceLeafNodeRenderer; +} + +export namespace FileNodeTypeDetails { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + export function is(typeDetails?: any): typeDetails is FileNodeTypeDetails { + return !!typeDetails && !!typeDetails.isFilepath; + } +} + +@injectable() +export class PreferenceSingleFilePathInputRendererContribution extends PreferenceLeafNodeRendererContribution { + static ID = 'preference-single-file-path-input-renderer'; + id = PreferenceSingleFilePathInputRendererContribution.ID; + + canHandleLeafNode(node: Preference.LeafNode): number { + const typeDetails = node.preference.data.typeDetails; + return FileNodeTypeDetails.is(typeDetails) && !typeDetails.selectionProps?.canSelectMany ? 5 : 0; + } + + createLeafNodeRenderer(container: interfaces.Container): PreferenceNodeRenderer { + return container.get(PreferenceSingleFilePathInputRenderer); + } +} + +@injectable() +export class PreferenceSingleFilePathInputRenderer extends PreferenceStringInputRenderer { + @inject(FileDialogService) fileDialogService: FileDialogService; + + get typeDetails(): FileNodeTypeDetails { + return this.preferenceNode.preference.data.typeDetails as FileNodeTypeDetails; + } + + protected createInputWrapper(): HTMLElement { + const inputWrapper = document.createElement('div'); + inputWrapper.classList.add('preference-file-container'); + return inputWrapper; + } + + protected override createInteractable(parent: HTMLElement): void { + const inputWrapper = this.createInputWrapper(); + + super.createInteractable(inputWrapper); + this.interactable.classList.add('preference-file-input'); + + this.createBrowseButton(inputWrapper); + + parent.appendChild(inputWrapper); + } + + protected createBrowseButton(parent: HTMLElement): void { + const button = document.createElement('button'); + button.classList.add('theia-button', 'main', 'preference-file-button'); + button.textContent = nls.localize('theia/core/file/browse', 'Browse'); + const handler = this.browse.bind(this); + button.onclick = handler; + button.onkeydown = handler; + button.tabIndex = 0; + button.setAttribute('aria-label', 'Submit Preference Input'); + parent.appendChild(button); + } + + protected async browse(): Promise { + const selectionProps = this.typeDetails.selectionProps; + const title = selectionProps?.title ?? selectionProps?.canSelectFolders ? WorkspaceCommands.OPEN_FOLDER.dialogLabel : WorkspaceCommands.OPEN_FILE.dialogLabel; + const selection = await this.fileDialogService.showOpenDialog({ title, ...selectionProps }); + if (selection) { + this.setPreferenceImmediately(selection.path.toString()); + } + } + + protected override setPreferenceImmediately(value: string): Promise { + this.interactable.value = value; + return super.setPreferenceImmediately(value); + } +} diff --git a/packages/preferences/src/browser/views/components/preference-json-input.ts b/packages/preferences/src/browser/views/components/preference-json-input.ts index b5b2b4dbf005a..237deff0af7d5 100644 --- a/packages/preferences/src/browser/views/components/preference-json-input.ts +++ b/packages/preferences/src/browser/views/components/preference-json-input.ts @@ -14,11 +14,12 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** -import { PreferenceLeafNodeRenderer } from './preference-node-renderer'; -import { injectable, inject } from '@theia/core/shared/inversify'; +import { PreferenceLeafNodeRenderer, PreferenceNodeRenderer } from './preference-node-renderer'; +import { injectable, inject, interfaces } from '@theia/core/shared/inversify'; import { CommandService, nls } from '@theia/core/lib/common'; -import { PreferencesCommands } from '../../util/preference-types'; +import { Preference, PreferencesCommands } from '../../util/preference-types'; import { JSONValue } from '@theia/core/shared/@phosphor/coreutils'; +import { PreferenceLeafNodeRendererContribution } from './preference-node-renderer-creator'; @injectable() export class PreferenceJSONLinkRenderer extends PreferenceLeafNodeRenderer { @@ -61,3 +62,17 @@ export class PreferenceJSONLinkRenderer extends PreferenceLeafNodeRenderer; +} + +export const PreferenceNodeRendererContribution = Symbol('PreferenceNodeRendererContribution'); +export interface PreferenceNodeRendererContribution { + registerPreferenceNodeRendererCreator(registry: PreferenceNodeRendererCreatorRegistry): void; +} + +export const PreferenceNodeRendererCreator = Symbol('PreferenceNodeRendererCreator'); +export interface PreferenceNodeRendererCreator { + id: string; + canHandle(node: Preference.TreeNode): number; + createRenderer(node: Preference.TreeNode, container: interfaces.Container): PreferenceNodeRenderer; +} + +@injectable() +export class DefaultPreferenceNodeRendererCreatorRegistry implements PreferenceNodeRendererCreatorRegistry { + protected readonly _creators: Map = new Map(); + protected readonly onDidChangeEmitter = new Emitter(); + readonly onDidChange = this.onDidChangeEmitter.event; + + constructor( + @inject(ContributionProvider) @named(PreferenceNodeRendererContribution) + protected readonly contributionProvider: ContributionProvider + ) { + const contributions = this.contributionProvider.getContributions(); + for (const contrib of contributions) { + contrib.registerPreferenceNodeRendererCreator(this); + } + } + + registerPreferenceNodeRendererCreator(creator: PreferenceNodeRendererCreator): Disposable { + if (this._creators.has(creator.id)) { + console.warn(`A preference node renderer creator ${creator.id} is already registered.`); + return Disposable.NULL; + } + this._creators.set(creator.id, creator); + this.fireDidChange(); + return Disposable.create(() => this._creators.delete(creator.id)); + } + + unregisterPreferenceNodeRendererCreator(creator: PreferenceNodeRendererCreator | string): void { + const id = typeof creator === 'string' ? creator : creator.id; + if (this._creators.delete(id)) { + this.fireDidChange(); + } + } + + getPreferenceNodeRendererCreator(node: Preference.TreeNode): PreferenceNodeRendererCreator { + const contributions = this.prioritize(node); + if (contributions.length >= 1) { + return contributions[0]; + } + // we already bind a default creator contribution so if that happens it was deliberate + throw new Error(`There is no contribution for ${node.id}.`); + } + + protected fireDidChange(): void { + this.onDidChangeEmitter.fire(undefined); + } + + protected prioritize(node: Preference.TreeNode): PreferenceNodeRendererCreator[] { + const prioritized = Prioritizeable.prioritizeAllSync(Array.from(this._creators.values()), creator => { + try { + return creator.canHandle(node); + } catch { + return 0; + } + }); + return prioritized.map(p => p.value); + } +} + +@injectable() +export abstract class PreferenceLeafNodeRendererContribution implements PreferenceNodeRendererCreator, PreferenceNodeRendererContribution { + abstract id: string; + + canHandle(node: Preference.TreeNode): number { + return Preference.LeafNode.is(node) ? this.canHandleLeafNode(node) : 0; + } + + registerPreferenceNodeRendererCreator(registry: PreferenceNodeRendererCreatorRegistry): void { + registry.registerPreferenceNodeRendererCreator(this); + } + + abstract canHandleLeafNode(node: Preference.LeafNode): number; + + createRenderer(node: Preference.TreeNode, container: interfaces.Container): PreferenceNodeRenderer { + const child = container.createChild(); + child.bind(Preference.Node).toConstantValue(node); + return this.createLeafNodeRenderer(child); + } + + abstract createLeafNodeRenderer(container: interfaces.Container): PreferenceNodeRenderer; +} diff --git a/packages/preferences/src/browser/views/components/preference-node-renderer.ts b/packages/preferences/src/browser/views/components/preference-node-renderer.ts index 9a55b440830a9..3e72015d2ffb3 100644 --- a/packages/preferences/src/browser/views/components/preference-node-renderer.ts +++ b/packages/preferences/src/browser/views/components/preference-node-renderer.ts @@ -14,7 +14,7 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; +import { injectable, inject, postConstruct, interfaces } from '@theia/core/shared/inversify'; import { PreferenceService, ContextMenuRenderer, PreferenceInspection, PreferenceScope, PreferenceProvider, codicon, OpenerService, open @@ -30,6 +30,7 @@ import { PreferencesSearchbarWidget } from '../preference-searchbar-widget'; import * as markdownit from '@theia/core/shared/markdown-it'; import * as DOMPurify from '@theia/core/shared/dompurify'; import URI from '@theia/core/lib/common/uri'; +import { PreferenceNodeRendererContribution, PreferenceNodeRendererCreator, PreferenceNodeRendererCreatorRegistry } from './preference-node-renderer-creator'; export const PreferenceNodeRendererFactory = Symbol('PreferenceNodeRendererFactory'); export type PreferenceNodeRendererFactory = (node: Preference.TreeNode) => PreferenceNodeRenderer; @@ -151,6 +152,26 @@ export class PreferenceHeaderRenderer extends PreferenceNodeRenderer { } } +@injectable() +export class PreferenceHeaderRendererContribution implements PreferenceNodeRendererCreator, PreferenceNodeRendererContribution { + static ID = 'preference-header-renderer'; + id = PreferenceHeaderRendererContribution.ID; + + registerPreferenceNodeRendererCreator(registry: PreferenceNodeRendererCreatorRegistry): void { + registry.registerPreferenceNodeRendererCreator(this); + } + + canHandle(node: Preference.TreeNode): number { + return !Preference.LeafNode.is(node) ? 1 : 0; + } + + createRenderer(node: Preference.TreeNode, container: interfaces.Container): PreferenceNodeRenderer { + const grandchild = container.createChild(); + grandchild.bind(Preference.Node).toConstantValue(node); + return grandchild.get(PreferenceHeaderRenderer); + } +} + @injectable() export abstract class PreferenceLeafNodeRenderer extends PreferenceNodeRenderer diff --git a/packages/preferences/src/browser/views/components/preference-number-input.ts b/packages/preferences/src/browser/views/components/preference-number-input.ts index d42c6bfba1011..6e6983170046d 100644 --- a/packages/preferences/src/browser/views/components/preference-number-input.ts +++ b/packages/preferences/src/browser/views/components/preference-number-input.ts @@ -14,8 +14,10 @@ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** -import { injectable } from '@theia/core/shared/inversify'; -import { PreferenceLeafNodeRenderer } from './preference-node-renderer'; +import { injectable, interfaces } from '@theia/core/shared/inversify'; +import { Preference } from '../../util/preference-types'; +import { PreferenceLeafNodeRenderer, PreferenceNodeRenderer } from './preference-node-renderer'; +import { PreferenceLeafNodeRendererContribution } from './preference-node-renderer-creator'; interface PreferenceNumberInputValidation { /** @@ -126,3 +128,18 @@ export class PreferenceNumberInputRenderer extends PreferenceLeafNodeRenderer { @@ -69,3 +71,17 @@ export class PreferenceSelectInputRenderer extends PreferenceLeafNodeRenderer { @@ -58,3 +60,17 @@ export class PreferenceStringInputRenderer extends PreferenceLeafNodeRenderer { + this.id = PreferencesEditorWidget.ID; this.title.label = PreferencesEditorWidget.LABEL; this.addClass('settings-main'); @@ -78,6 +81,7 @@ export class PreferencesEditorWidget extends BaseWidget implements StatefulWidge this.createContainers(); await this.preferenceService.ready; this.handleDisplayChange({ source: PreferenceFilterChangeSource.Schema }); + this.rendererRegistry.onDidChange(() => this.handleRegistryChange()); } protected createContainers(): void { @@ -108,6 +112,14 @@ export class PreferencesEditorWidget extends BaseWidget implements StatefulWidge this.resetScroll(currentFirstVisible, e.source === PreferenceFilterChangeSource.Search && !isFiltered); } + protected handleRegistryChange(): void { + for (const [id, renderer, collection] of this.allRenderers()) { + renderer.dispose(); + collection.delete(id); + } + this.handleDisplayChange({ source: PreferenceFilterChangeSource.Schema }); + } + protected handleSchemaChange(isFiltered: boolean): void { for (const [id, renderer, collection] of this.allRenderers()) { if (!this.model.getNode(renderer.nodeId)) { diff --git a/packages/preferences/src/browser/views/preference-widget-bindings.ts b/packages/preferences/src/browser/views/preference-widget-bindings.ts index 4aacef57bfc13..b0ad5d63f2fb5 100644 --- a/packages/preferences/src/browser/views/preference-widget-bindings.ts +++ b/packages/preferences/src/browser/views/preference-widget-bindings.ts @@ -13,24 +13,28 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 // ***************************************************************************** -import { interfaces, Container } from '@theia/core/shared/inversify'; -import { WidgetFactory, createTreeContainer, LabelProviderContribution } from '@theia/core/lib/browser'; -import { PreferenceNodeRendererFactory, PreferenceHeaderRenderer } from './components/preference-node-renderer'; -import { PreferencesWidget } from './preference-widget'; -import { PreferencesTreeWidget } from './preference-tree-widget'; -import { PreferencesEditorWidget } from './preference-editor-widget'; -import { PreferencesSearchbarWidget } from './preference-searchbar-widget'; -import { PreferencesScopeTabBar } from './preference-scope-tabbar-widget'; +import { createTreeContainer, LabelProviderContribution, WidgetFactory } from '@theia/core/lib/browser'; +import { bindContributionProvider } from '@theia/core/lib/common/contribution-provider'; +import { Container, interfaces } from '@theia/core/shared/inversify'; import { PreferenceTreeModel } from '../preference-tree-model'; import { PreferenceTreeLabelProvider } from '../util/preference-tree-label-provider'; import { Preference } from '../util/preference-types'; -import { PreferenceStringInputRenderer } from './components/preference-string-input'; -import { PreferenceBooleanInputRenderer } from './components/preference-boolean-input'; -import { PreferenceJSONLinkRenderer } from './components/preference-json-input'; -import { PreferenceSelectInputRenderer } from './components/preference-select-input'; -import { PreferenceNumberInputRenderer } from './components/preference-number-input'; -import { PreferenceArrayInputRenderer } from './components/preference-array-input'; -import { IJSONSchema } from '@theia/core/lib/common/json-schema'; +import { PreferenceArrayInputRenderer, PreferenceArrayInputRendererContribution } from './components/preference-array-input'; +import { PreferenceBooleanInputRenderer, PreferenceBooleanInputRendererContribution } from './components/preference-boolean-input'; +import { PreferenceSingleFilePathInputRenderer, PreferenceSingleFilePathInputRendererContribution } from './components/preference-file-input'; +import { PreferenceJSONLinkRenderer, PreferenceJSONLinkRendererContribution } from './components/preference-json-input'; +import { PreferenceHeaderRenderer, PreferenceHeaderRendererContribution, PreferenceNodeRendererFactory } from './components/preference-node-renderer'; +import { + DefaultPreferenceNodeRendererCreatorRegistry, PreferenceNodeRendererContribution, PreferenceNodeRendererCreatorRegistry +} from './components/preference-node-renderer-creator'; +import { PreferenceNumberInputRenderer, PreferenceNumberInputRendererContribution } from './components/preference-number-input'; +import { PreferenceSelectInputRenderer, PreferenceSelectInputRendererContribution } from './components/preference-select-input'; +import { PreferenceStringInputRenderer, PreferenceStringInputRendererContribution } from './components/preference-string-input'; +import { PreferencesEditorWidget } from './preference-editor-widget'; +import { PreferencesScopeTabBar } from './preference-scope-tabbar-widget'; +import { PreferencesSearchbarWidget } from './preference-searchbar-widget'; +import { PreferencesTreeWidget } from './preference-tree-widget'; +import { PreferencesWidget } from './preference-widget'; export function bindPreferencesWidgets(bind: interfaces.Bind): void { bind(PreferenceTreeLabelProvider).toSelf().inSingletonScope(); @@ -42,16 +46,38 @@ export function bindPreferencesWidgets(bind: interfaces.Bind): void { id: PreferencesWidget.ID, createWidget: () => container.get(PreferencesWidget) })).inSingletonScope(); + + bindContributionProvider(bind, PreferenceNodeRendererContribution); + bind(PreferenceSelectInputRenderer).toSelf(); + bind(PreferenceNodeRendererContribution).to(PreferenceSelectInputRendererContribution).inSingletonScope(); + bind(PreferenceArrayInputRenderer).toSelf(); + bind(PreferenceNodeRendererContribution).to(PreferenceArrayInputRendererContribution).inSingletonScope(); + bind(PreferenceStringInputRenderer).toSelf(); + bind(PreferenceNodeRendererContribution).to(PreferenceStringInputRendererContribution).inSingletonScope(); + bind(PreferenceBooleanInputRenderer).toSelf(); + bind(PreferenceNodeRendererContribution).to(PreferenceBooleanInputRendererContribution).inSingletonScope(); + bind(PreferenceNumberInputRenderer).toSelf(); + bind(PreferenceNodeRendererContribution).to(PreferenceNumberInputRendererContribution).inSingletonScope(); + bind(PreferenceJSONLinkRenderer).toSelf(); + bind(PreferenceNodeRendererContribution).to(PreferenceJSONLinkRendererContribution).inSingletonScope(); + bind(PreferenceHeaderRenderer).toSelf(); + bind(PreferenceNodeRendererContribution).to(PreferenceHeaderRendererContribution).inSingletonScope(); + + bind(PreferenceSingleFilePathInputRenderer).toSelf(); + bind(PreferenceNodeRendererContribution).to(PreferenceSingleFilePathInputRendererContribution).inSingletonScope(); + + bind(DefaultPreferenceNodeRendererCreatorRegistry).toSelf().inSingletonScope(); + bind(PreferenceNodeRendererCreatorRegistry).toService(DefaultPreferenceNodeRendererCreatorRegistry); } -function createPreferencesWidgetContainer(parent: interfaces.Container): Container { +export function createPreferencesWidgetContainer(parent: interfaces.Container): Container { const child = createTreeContainer(parent, { model: PreferenceTreeModel, widget: PreferencesTreeWidget, @@ -64,30 +90,9 @@ function createPreferencesWidgetContainer(parent: interfaces.Container): Contain child.bind(PreferencesWidget).toSelf(); child.bind(PreferenceNodeRendererFactory).toFactory(({ container }) => (node: Preference.TreeNode) => { - const grandchild = container.createChild(); - grandchild.bind(Preference.Node).toConstantValue(node); - if (Preference.LeafNode.is(node)) { - if (node.preference.data.enum) { - return grandchild.get(PreferenceSelectInputRenderer); - } - const type = Array.isArray(node.preference.data.type) ? node.preference.data.type[0] : node.preference.data.type; - if (type === 'array' && (node.preference.data.items as IJSONSchema)?.type === 'string') { - return grandchild.get(PreferenceArrayInputRenderer); - } - switch (type) { - case 'string': - return grandchild.get(PreferenceStringInputRenderer); - case 'boolean': - return grandchild.get(PreferenceBooleanInputRenderer); - case 'number': - case 'integer': - return grandchild.get(PreferenceNumberInputRenderer); - default: - return grandchild.get(PreferenceJSONLinkRenderer); - } - } else { - return grandchild.get(PreferenceHeaderRenderer); - } + const registry = container.get(PreferenceNodeRendererCreatorRegistry); + const creator = registry.getPreferenceNodeRendererCreator(node); + return creator.createRenderer(node, container); }); return child; diff --git a/packages/terminal/src/browser/terminal-preferences.ts b/packages/terminal/src/browser/terminal-preferences.ts index 4316801e2db8c..e05f1a77bfe5e 100644 --- a/packages/terminal/src/browser/terminal-preferences.ts +++ b/packages/terminal/src/browser/terminal-preferences.ts @@ -111,6 +111,7 @@ export const TerminalConfigSchema: PreferenceSchema = { }, 'terminal.integrated.shell.windows': { type: ['string', 'null'], + typeDetails: { isFilepath: true }, markdownDescription: nls.localize('theia/terminal/shellWindows', 'The path of the shell that the terminal uses on Windows. (default: \'{0}\').', 'C:\\Windows\\System32\\cmd.exe'), default: undefined },