// ***************************************************************************** // Copyright (C) 2024 TypeFox 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-only WITH Classpath-exception-2.0 // ***************************************************************************** import { nls } from '@theia/core'; import { injectable } from '@theia/core/shared/inversify'; export interface PreferenceLayout { id: string; label: string; children?: PreferenceLayout[]; settings?: string[]; } export const COMMONLY_USED_SECTION_PREFIX = 'commonly-used'; export const COMMONLY_USED_LAYOUT = { id: COMMONLY_USED_SECTION_PREFIX, label: nls.localizeByDefault('Commonly Used'), settings: [ 'files.autoSave', 'editor.fontSize', 'editor.fontFamily', 'editor.tabSize', 'editor.renderWhitespace', 'editor.cursorStyle', 'editor.multiCursorModifier', 'editor.insertSpaces', 'editor.wordWrap', 'files.exclude', 'files.associations' ] }; export const DEFAULT_LAYOUT: PreferenceLayout[] = [ { id: 'editor', label: nls.localizeByDefault('Text Editor'), settings: ['editor.*'], children: [ { id: 'editor.cursor', label: nls.localizeByDefault('Cursor'), settings: ['editor.cursor*'] }, { id: 'editor.find', label: nls.localizeByDefault('Find'), settings: ['editor.find.*'] }, { id: 'editor.font', label: nls.localizeByDefault('Font'), settings: ['editor.font*'] }, { id: 'editor.format', label: nls.localizeByDefault('Formatting'), settings: ['editor.format*'] }, { id: 'editor.diffEditor', label: nls.localizeByDefault('Diff Editor'), settings: ['diffEditor.*'] }, { id: 'editor.multiDiffEditor', label: nls.localizeByDefault('Multi-File Diff Editor'), settings: ['multiDiffEditor.*'] }, { id: 'editor.minimap', label: nls.localizeByDefault('Minimap'), settings: ['editor.minimap.*'] }, { id: 'editor.suggestions', label: nls.localizeByDefault('Suggestions'), settings: ['editor.*suggest*'] }, { id: 'editor.files', label: nls.localizeByDefault('Files'), settings: ['files.*'] } ] }, { id: 'workbench', label: nls.localizeByDefault('Workbench'), settings: ['workbench.*', 'workspace.*'], children: [ { id: 'workbench.appearance', label: nls.localizeByDefault('Appearance'), settings: [ 'workbench.activityBar.*', 'workbench.*color*', 'workbench.fontAliasing', 'workbench.iconTheme', 'workbench.sidebar.location', 'workbench.*.visible', 'workbench.tips.enabled', 'workbench.tree.*', 'workbench.view.*' ] }, { id: 'workbench.breadcrumbs', label: nls.localizeByDefault('Breadcrumbs'), settings: ['breadcrumbs.*'] }, { id: 'workbench.editor', label: nls.localizeByDefault('Editor Management'), settings: ['workbench.editor.*'] }, { id: 'workbench.settings', label: nls.localizeByDefault('Settings Editor'), settings: ['workbench.settings.*'] }, { id: 'workbench.zenmode', label: nls.localizeByDefault('Zen Mode'), settings: ['zenmode.*'] }, { id: 'workbench.screencastmode', label: nls.localizeByDefault('Screencast Mode'), settings: ['screencastMode.*'] } ] }, { id: 'window', label: nls.localizeByDefault('Window'), settings: ['window.*'], children: [ { id: 'window.newWindow', label: nls.localizeByDefault('New Window'), settings: ['window.*newwindow*'] } ] }, { id: 'features', label: nls.localizeByDefault('Features'), children: [ { id: 'features.accessibilitySignals', label: nls.localizeByDefault('Accessibility Signals'), settings: ['accessibility.signal*'] }, { id: 'features.accessibility', label: nls.localizeByDefault('Accessibility'), settings: ['accessibility.*'] }, { id: 'features.explorer', label: nls.localizeByDefault('Explorer'), settings: ['explorer.*', 'outline.*'] }, { id: 'features.search', label: nls.localizeByDefault('Search'), settings: ['search.*'] }, { id: 'features.debug', label: nls.localizeByDefault('Debug'), settings: ['debug.*', 'launch'] }, { id: 'features.testing', label: nls.localizeByDefault('Testing'), settings: ['testing.*'] }, { id: 'features.scm', label: nls.localizeByDefault('Source Control'), settings: ['scm.*'] }, { id: 'features.extensions', label: nls.localizeByDefault('Extensions'), settings: ['extensions.*'] }, { id: 'features.terminal', label: nls.localizeByDefault('Terminal'), settings: ['terminal.*'] }, { id: 'features.task', label: nls.localizeByDefault('Task'), settings: ['task.*'] }, { id: 'features.problems', label: nls.localizeByDefault('Problems'), settings: ['problems.*'] }, { id: 'features.output', label: nls.localizeByDefault('Output'), settings: ['output.*'] }, { id: 'features.comments', label: nls.localizeByDefault('Comments'), settings: ['comments.*'] }, { id: 'features.remote', label: nls.localizeByDefault('Remote'), settings: ['remote.*'] }, { id: 'features.timeline', label: nls.localizeByDefault('Timeline'), settings: ['timeline.*'] }, { id: 'features.toolbar', label: nls.localize('theia/preferences/toolbar', 'Toolbar'), settings: ['toolbar.*'] }, { id: 'features.notebook', label: nls.localizeByDefault('Notebook'), settings: ['notebook.*', 'interactiveWindow.*'] }, { id: 'features.mergeEditor', label: nls.localizeByDefault('Merge Editor'), settings: ['mergeEditor.*'] }, { id: 'features.chat', label: nls.localizeByDefault('Chat'), settings: ['chat.*', 'inlineChat.*'] } ] }, { id: 'application', label: nls.localizeByDefault('Application'), children: [ { id: 'application.http', label: nls.localizeByDefault('HTTP'), settings: ['http.*'] }, { id: 'application.keyboard', label: nls.localizeByDefault('Keyboard'), settings: ['keyboard.*'] }, { id: 'application.update', label: nls.localizeByDefault('Update'), settings: ['update.*'] }, { id: 'application.telemetry', label: nls.localizeByDefault('Telemetry'), settings: ['telemetry.*'] }, { id: 'application.settingsSync', label: nls.localizeByDefault('Settings Sync'), settings: ['settingsSync.*'] }, { id: 'application.experimental', label: nls.localizeByDefault('Experimental'), settings: ['application.experimental.*'] }, { id: 'application.other', label: nls.localizeByDefault('Other'), settings: ['application.*'] } ] }, { id: 'security', label: nls.localizeByDefault('Security'), settings: ['security.*'], children: [ { id: 'security.workspace', label: nls.localizeByDefault('Workspace'), settings: ['security.workspace.*'] } ] }, { id: 'ai-features', label: nls.localize('theia/preferences/ai-features', 'AI Features'), children: [ { id: 'ai-features.aiEnablement', label: nls.localize('theia/preferences/ai-features/ai-enable', 'AI Enablement'), settings: ['ai-features.AiEnable.*'] }, { id: 'ai-features.anthropic', label: 'Anthropic', settings: ['ai-features.anthropic.*'] }, { id: 'ai-features.chat', label: nls.localizeByDefault('Chat'), settings: ['ai-features.chat.*'] }, { id: 'ai-features.copilot', label: 'GitHub Copilot', settings: ['ai-features.copilot.*'] }, { id: 'ai-features.codeCompletion', label: nls.localize('theia/preferences/ai-features/code-completion', 'Code Completion'), settings: ['ai-features.codeCompletion.*'] }, { id: 'ai-features.huggingFace', label: 'Hugging Face', settings: ['ai-features.huggingFace.*'] }, { id: 'ai-features.mcp', label: nls.localizeByDefault('MCP'), settings: ['ai-features.mcp.*'] }, { id: 'ai-features.modelSettings', label: nls.localize('theia/preferences/ai-features/model-settings', 'Model Settings'), settings: ['ai-features.modelSettings.*', 'ai-features.languageModelAliases'] }, { id: 'ai-features.ollama', label: 'Ollama', settings: ['ai-features.ollama'] }, { id: 'ai-features.llamafile', label: 'Llamafile', settings: ['ai-features.llamafile.*'] }, { id: 'ai-features.openAiCustom', label: nls.localize('theia/preferences/ai-features/open-ai-custom', '{0} Custom Models', 'Open AI'), settings: ['ai-features.openAiCustom.*'] }, { id: 'ai-features.openAiOfficial', label: nls.localize('theia/preferences/ai-features/open-ai-official', '{0} Official Models', 'Open AI'), settings: ['ai-features.openAiOfficial.*'] }, { id: 'ai-features.promptTemplates', label: nls.localize('theia/preferences/ai-features/promptTemplates', 'Prompt Templates'), settings: ['ai-features.promptTemplates.*'] }, { id: 'ai-features.SCANOSS', label: 'SCANOSS', settings: ['ai-features.SCANOSS.*'] }, { id: 'ai-features.workspaceFunctions', label: nls.localize('theia/preferences/ai-features/workspace-functions', 'Workspace Functions'), settings: ['ai-features.workspaceFunctions.*'] } ] }, { id: 'extensions', label: nls.localizeByDefault('Extensions'), children: [ { id: 'extensions.hosted-plugin', label: nls.localize('theia/preferences/hostedPlugin', 'Hosted Plugin'), settings: ['hosted-plugin.*'] } ] } ]; @injectable() export class PreferenceLayoutProvider { getLayout(): PreferenceLayout[] { return DEFAULT_LAYOUT; } getCommonlyUsedLayout(): PreferenceLayout { return COMMONLY_USED_LAYOUT; } hasCategory(id: string): boolean { return [...this.getLayout(), this.getCommonlyUsedLayout()].some(e => e.id === id); } getLayoutForPreference(preferenceId: string): PreferenceLayout | undefined { const layout = this.getLayout(); for (const section of layout) { const item = this.findItemInSection(section, preferenceId); if (item) { return item; } } return undefined; } protected findItemInSection(section: PreferenceLayout, preferenceId: string): PreferenceLayout | undefined { // First check whether any of its children match the preferenceId. if (section.children) { for (const child of section.children) { const item = this.findItemInSection(child, preferenceId); if (item) { return item; } } } // Then check whether the section itself matches the preferenceId. if (section.settings) { for (const setting of section.settings) { if (this.matchesSetting(preferenceId, setting)) { return section; } } } return undefined; } protected matchesSetting(preferenceId: string, setting: string): boolean { if (setting.includes('*')) { return this.createRegExp(setting).test(preferenceId); } return preferenceId === setting; } protected createRegExp(setting: string): RegExp { return new RegExp(`^${setting.replace(/\./g, '\\.').replace(/\*/g, '.*')}$`); } }