From b8be5037e09216e3ffdf0999508d877ddb8f1ec4 Mon Sep 17 00:00:00 2001 From: Alvaro Sanchez-Leon Date: Wed, 14 Jun 2023 16:32:49 -0400 Subject: [PATCH] Support icon contributions from plugins - in progress --- .../src/browser/icon-theme-contribution.ts | 2 + .../tab-bar-toolbar-registry.ts | 1 + .../src/browser/monaco-frontend-module.ts | 9 ++ .../src/browser/monaco-icon-registry-types.ts | 127 ++++++++++++++++++ .../src/browser/monaco-icon-registry.ts | 59 ++++++++ .../src/browser/monaco-icon-style-sheet.ts | 28 ++++ .../plugin-ext/src/common/plugin-protocol.ts | 31 ++++- .../src/hosted/node/scanners/scanner-theia.ts | 71 +++++++++- .../browser/plugin-contribution-handler.ts | 29 +++- .../browser/plugin-ext-frontend-module.ts | 4 + .../src/main/browser/plugin-icon-service.ts | 97 +++++++++++++ .../webview-frontend-security-warnings.ts | 1 + 12 files changed, 456 insertions(+), 3 deletions(-) create mode 100644 packages/monaco/src/browser/monaco-icon-registry-types.ts create mode 100644 packages/monaco/src/browser/monaco-icon-registry.ts create mode 100644 packages/monaco/src/browser/monaco-icon-style-sheet.ts create mode 100644 packages/plugin-ext/src/main/browser/plugin-icon-service.ts diff --git a/packages/core/src/browser/icon-theme-contribution.ts b/packages/core/src/browser/icon-theme-contribution.ts index 3c1dda5465f30..d5c628f1ff51b 100644 --- a/packages/core/src/browser/icon-theme-contribution.ts +++ b/packages/core/src/browser/icon-theme-contribution.ts @@ -36,6 +36,8 @@ export class IconThemeApplicationContribution implements FrontendApplicationCont protected readonly iconThemeContributions: ContributionProvider; async onStart(): Promise { + console.log('**** alvs, IconThemeApplicationContribution onStart'); + for (const contribution of this.iconThemeContributions.getContributions()) { await contribution.registerIconThemes(this.iconThemes); } diff --git a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts index 8ea697a7a3ee5..ba72696fbc992 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +++ b/packages/core/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts @@ -81,6 +81,7 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution { throw new Error(`A toolbar item is already registered with the '${id}' ID.`); } this.items.set(id, item); + console.warn('***** alvs: registering tab item: ', item.id); this.fireOnDidChange(); const toDispose = new DisposableCollection( Disposable.create(() => this.fireOnDidChange()), diff --git a/packages/monaco/src/browser/monaco-frontend-module.ts b/packages/monaco/src/browser/monaco-frontend-module.ts index 3c4b2c049a7e7..ad67d59f9f4df 100644 --- a/packages/monaco/src/browser/monaco-frontend-module.ts +++ b/packages/monaco/src/browser/monaco-frontend-module.ts @@ -68,6 +68,9 @@ import { MimeService } from '@theia/core/lib/browser/mime-service'; import { MonacoEditorServices } from './monaco-editor'; import { MonacoColorRegistry } from './monaco-color-registry'; import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; +import { IconRegistry, IconStyleSheetService } from './monaco-icon-registry-types'; +import { MonacoIconRegistry } from './monaco-icon-registry'; +import { MonacoIconStyleSheetService } from './monaco-icon-style-sheet'; import { MonacoThemingService } from './monaco-theming-service'; import { bindContributionProvider } from '@theia/core'; import { WorkspaceSymbolCommand } from './workspace-symbol-command'; @@ -186,6 +189,12 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ThemeServiceWithDB).toSelf().inSingletonScope(); rebind(ThemeService).toService(ThemeServiceWithDB); + + bind(MonacoIconRegistry).toSelf().inSingletonScope(); + bind(IconRegistry).toService(MonacoIconRegistry); + + bind(MonacoIconStyleSheetService).toSelf().inSingletonScope(); + bind(IconStyleSheetService).toService(MonacoIconStyleSheetService); }); export const MonacoConfigurationService = Symbol('MonacoConfigurationService'); diff --git a/packages/monaco/src/browser/monaco-icon-registry-types.ts b/packages/monaco/src/browser/monaco-icon-registry-types.ts new file mode 100644 index 0000000000000..22ef36c55213b --- /dev/null +++ b/packages/monaco/src/browser/monaco-icon-registry-types.ts @@ -0,0 +1,127 @@ + +// ***************************************************************************** +// Copyright (C) 2023 Ericsson 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 +// ***************************************************************************** +import { Event } from '@theia/core'; +import { IJSONSchema } from '@theia/core/lib/common/json-schema'; +import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; +import { URI } from '@theia/core/shared/vscode-uri'; + +// ------ API types + +export type IconIdentifier = string; + +// icon registry +export const Extensions = { + IconContribution: 'base.contributions.icons' +}; + +export type IconDefaults = ThemeIcon | IconDefinition; + +export interface IconDefinition { + font?: IconFontContribution; // undefined for the default font (codicon) + fontCharacter: string; +} + +export interface IconContribution { + readonly id: string; + description: string | undefined; + deprecationMessage?: string; + readonly defaults: IconDefaults; +} + +export interface IconFontContribution { + readonly id: string; + readonly definition: IconFontDefinition; +} + +export interface IconFontDefinition { + readonly weight?: string; + readonly style?: string; + readonly src: IconFontSource[]; +} + +export interface IconFontSource { + readonly location: URI; + readonly format: string; +} + +export const IconRegistry = Symbol('IconRegistry'); +export interface IconRegistry { + + readonly onDidChange: Event; + + /** + * Register a icon to the registry. + * @param id The icon id + * @param defaults The default values + * @param description The description + */ + registerIcon(id: IconIdentifier, defaults: IconDefaults, description?: string): ThemeIcon; + + /** + * Deregister a icon from the registry. + */ + deregisterIcon(id: IconIdentifier): void; + + /** + * Get all icon contributions + */ + getIcons(): IconContribution[]; + + /** + * Get the icon for the given id + */ + getIcon(id: IconIdentifier): IconContribution | undefined; + + /** + * JSON schema for an object to assign icon values to one of the icon contributions. + */ + getIconSchema(): IJSONSchema; + + /** + * JSON schema to for a reference to a icon contribution. + */ + getIconReferenceSchema(): IJSONSchema; + + /** + * Register a icon font to the registry. + * @param id The icon font id + * @param definition The icon font definition + */ + registerIconFont(id: string, definition: IconFontDefinition): IconFontDefinition; + + /** + * Deregister an icon font to the registry. + */ + deregisterIconFont(id: string): void; + + /** + * Get the icon font for the given id + */ + getIconFont(id: string): IconFontDefinition | undefined; +} + +export const IconsStyleSheet = Symbol('IconsStyleSheet'); +export interface IconsStyleSheet { + getCSS(): string; + readonly onDidChange: Event; +} + +export const IconStyleSheetService = Symbol('IconStyleSheetService'); +export interface IconStyleSheetService { + getIconsStyleSheet(): IconsStyleSheet; +} + diff --git a/packages/monaco/src/browser/monaco-icon-registry.ts b/packages/monaco/src/browser/monaco-icon-registry.ts new file mode 100644 index 0000000000000..ba91f620cd647 --- /dev/null +++ b/packages/monaco/src/browser/monaco-icon-registry.ts @@ -0,0 +1,59 @@ + +// ***************************************************************************** +// Copyright (C) 2023 Ericsson 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 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; +import * as monaco from '@theia/monaco-editor-core/esm/vs/platform/theme/common/iconRegistry'; +import { IconContribution, IconDefaults, IconFontDefinition, IconRegistry } from './monaco-icon-registry-types'; +import { Event } from '@theia/core'; +import { IJSONSchema } from '@theia/core/lib/common/json-schema'; +import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService'; + +@injectable() +export class MonacoIconRegistry implements IconRegistry { + protected readonly iconRegistry = monaco.getIconRegistry(); + + onDidChange: Event = this.iconRegistry.onDidChange; + + registerIcon(id: string, defaults: IconDefaults, description?: string | undefined): ThemeIcon { + return this.iconRegistry.registerIcon(id, defaults, description); + } + deregisterIcon(id: string): void { + return this.iconRegistry.deregisterIcon(id); + } + getIcons(): IconContribution[] { + return this.iconRegistry.getIcons(); + } + getIcon(id: string): IconContribution | undefined { + return this.iconRegistry.getIcon(id); + } + getIconSchema(): IJSONSchema { + return this.iconRegistry.getIconSchema(); + } + getIconReferenceSchema(): IJSONSchema { + return this.iconRegistry.getIconReferenceSchema(); + } + registerIconFont(id: string, definition: IconFontDefinition): IconFontDefinition { + return this.iconRegistry.registerIconFont(id, definition); + } + deregisterIconFont(id: string): void { + return this.iconRegistry.deregisterIconFont(id); + } + getIconFont(id: string): IconFontDefinition | undefined { + return this.iconRegistry.getIconFont(id); + } + +} diff --git a/packages/monaco/src/browser/monaco-icon-style-sheet.ts b/packages/monaco/src/browser/monaco-icon-style-sheet.ts new file mode 100644 index 0000000000000..6d3678a02a422 --- /dev/null +++ b/packages/monaco/src/browser/monaco-icon-style-sheet.ts @@ -0,0 +1,28 @@ + +// ***************************************************************************** +// Copyright (C) 2023 Ericsson 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 +// ***************************************************************************** + +import { injectable } from '@theia/core/shared/inversify'; +import { getIconsStyleSheet } from '@theia/monaco-editor-core/esm/vs/platform/theme/browser/iconsStyleSheet'; +import { IconsStyleSheet } from './monaco-icon-registry-types'; + +@injectable() +export class MonacoIconStyleSheetService { + protected getIconsStyleSheet(): IconsStyleSheet { + return getIconsStyleSheet(undefined); + } + +} diff --git a/packages/plugin-ext/src/common/plugin-protocol.ts b/packages/plugin-ext/src/common/plugin-protocol.ts index 32f27ae6178ba..50b293725f3e0 100644 --- a/packages/plugin-ext/src/common/plugin-protocol.ts +++ b/packages/plugin-ext/src/common/plugin-protocol.ts @@ -17,7 +17,7 @@ import { RpcServer } from '@theia/core/lib/common/messaging/proxy-factory'; import { RPCProtocol } from './rpc-protocol'; import { Disposable } from '@theia/core/lib/common/disposable'; import { LogPart, KeysToAnyValues, KeysToKeysToAnyValue } from './types'; -import { CharacterPair, CommentRule, PluginAPIFactory, Plugin } from './plugin-api-rpc'; +import { CharacterPair, CommentRule, PluginAPIFactory, Plugin, ThemeIcon } from './plugin-api-rpc'; import { ExtPluginApi } from './plugin-ext-api-contribution'; import { IJSONSchema, IJSONSchemaSnippet } from '@theia/core/lib/common/json-schema'; import { RecursivePartial } from '@theia/core/lib/common/types'; @@ -90,6 +90,7 @@ export interface PluginPackageContribution { snippets?: PluginPackageSnippetsContribution[]; themes?: PluginThemeContribution[]; iconThemes?: PluginIconThemeContribution[]; + icons?: PluginIconContribution[]; colors?: PluginColorContribution[]; taskDefinitions?: PluginTaskDefinitionContribution[]; problemMatchers?: PluginProblemMatcherContribution[]; @@ -253,6 +254,13 @@ export interface PluginIconThemeContribution { uiTheme?: PluginUiTheme; } +export interface PluginIconContribution { + [id: string]: { + description: string; + default: { fontPath: string; fontCharacter: string } | string; + }; +} + export interface PlatformSpecificAdapterContribution { program?: string; args?: string[]; @@ -577,6 +585,7 @@ export interface PluginContribution { snippets?: SnippetContribution[]; themes?: ThemeContribution[]; iconThemes?: IconThemeContribution[]; + icons?: IconContribution[]; colors?: ColorDefinition[]; taskDefinitions?: TaskDefinition[]; problemMatchers?: ProblemMatcherContribution[]; @@ -644,6 +653,26 @@ export interface IconThemeContribution { uiTheme?: UiTheme; } +export interface IconDefinition { + fontCharacter: string; + location: string; +} + +export type IconDefaults = ThemeIcon | IconDefinition; + +export interface IconContribution { + id: string; + extensionId: string; + description: string | undefined; + defaults: IconDefaults; +} + +export namespace IconContribution { + export function isIconDefinition(defaults: IconDefaults): defaults is IconDefinition { + return 'fontCharacter' in defaults; + } +} + export interface GrammarsContribution { format: 'json' | 'plist'; language?: string; diff --git a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts index 28306abb2af04..1923ade494e48 100644 --- a/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts +++ b/packages/plugin-ext/src/hosted/node/scanners/scanner-theia.ts @@ -23,6 +23,7 @@ import { buildFrontendModuleName, DebuggerContribution, IconThemeContribution, + IconContribution, IconUrl, Keybinding, LanguageConfiguration, @@ -60,7 +61,8 @@ import { PluginPackageTranslation, Translation, PluginIdentifiers, - TerminalProfile + TerminalProfile, + PluginIconContribution } from '../../../common/plugin-protocol'; import * as fs from 'fs'; import * as path from 'path'; @@ -73,6 +75,7 @@ import { deepClone } from '@theia/core/lib/common/objects'; import { PreferenceSchema, PreferenceSchemaProperties } from '@theia/core/lib/common/preferences/preference-schema'; import { TaskDefinition } from '@theia/task/lib/common/task-protocol'; import { ColorDefinition } from '@theia/core/lib/common/color'; +import { CSSIcon } from '@theia/core/lib/common/markdown-rendering/icon-utilities'; import { PluginUriFactory } from './plugin-uri-factory'; namespace nls { @@ -88,6 +91,12 @@ const INTERNAL_CONSOLE_OPTIONS_SCHEMA = { }; const colorIdPattern = '^\\w+[.\\w+]*$'; +const iconIdPattern = `^${CSSIcon.iconNameSegment}(-${CSSIcon.iconNameSegment})+$`; + +function getFileExtension(filePath: string): string { + const index = filePath.lastIndexOf('.'); + return index === -1 ? '' : filePath.substring(index + 1); +} @injectable() export class TheiaPluginScanner implements PluginScanner { @@ -145,6 +154,7 @@ export class TheiaPluginScanner implements PluginScanner { } getContribution(rawPlugin: PluginPackage): PluginContribution | undefined { + console.warn('**** alvs, scanner-theia.ts#getContribution: ', rawPlugin.name); if (!rawPlugin.contributes && !rawPlugin.activationEvents) { return undefined; } @@ -342,6 +352,12 @@ export class TheiaPluginScanner implements PluginScanner { console.error(`Could not read '${rawPlugin.name}' contribution 'themes'.`, rawPlugin.contributes.themes, err); } + try { + contributions.icons = this.readIcons(rawPlugin); + } catch (err) { + console.error(`Could not read '${rawPlugin.name}' contribution 'icons'.`, rawPlugin.contributes.icons, err); + } + try { contributions.iconThemes = this.readIconThemes(rawPlugin); } catch (err) { @@ -512,6 +528,59 @@ export class TheiaPluginScanner implements PluginScanner { return result; } + protected readIcons(pck: PluginPackage): IconContribution[] | undefined { + if (!pck.contributes || !pck.contributes.icons) { + return undefined; + } + const result: IconContribution[] = []; + const iconEntries = (pck.contributes.icons); + for (const id in iconEntries) { + if (pck.contributes.icons.hasOwnProperty(id)) { + if (!id.match(iconIdPattern)) { + console.error("'configuration.icons' keys represent the icon id and can only contain letter, digits and minuses. " + + 'They need to consist of at least two segments in the form `component-iconname`.', 'extension: ', pck.name, 'icon id: ', id); + return; + } + const iconContribution = iconEntries[id]; + if (typeof iconContribution.description !== 'string' || iconContribution.description['length'] === 0) { + console.error('configuration.icons.description must be defined and can not be empty, ', 'extension: ', pck.name, 'icon id: ', id); + return; + } + + const defaultIcon = iconContribution.default; + if (typeof defaultIcon === 'string') { + result.push({ + id, + extensionId: pck.publisher + '.' + pck.name, + description: iconContribution.description, + defaults: { id: defaultIcon } + }); + } else if (typeof defaultIcon === 'object' && typeof defaultIcon.fontPath === 'string' && typeof defaultIcon.fontCharacter === 'string') { + const format = getFileExtension(defaultIcon.fontPath); + if (['woff', 'woff2', 'ttf'].indexOf(format) === -1) { + console.warn("Expected `contributes.icons.default.fontPath` to have file extension 'woff', woff2' or 'ttf', is '{0}'.", format); + return; + } + + const iconFontLocation = this.pluginUriFactory.createUri(pck, defaultIcon.fontPath).toString(); + result.push({ + id, + extensionId: pck.publisher + '.' + pck.name, + description: iconContribution.description, + defaults: { + fontCharacter: defaultIcon.fontCharacter, + location: iconFontLocation + } + }); + } else { + console.error("'configuration.icons.default' must be either a reference to the id of an other theme icon (string) or a icon definition (object) with ", + 'properties `fontPath` and `fontCharacter`.'); + } + } + } + return result; + } + protected readSnippets(pck: PluginPackage): SnippetContribution[] | undefined { if (!pck.contributes || !pck.contributes.snippets) { return undefined; diff --git a/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts b/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts index fe671d99b6dd3..ddabc67008377 100644 --- a/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts +++ b/packages/plugin-ext/src/main/browser/plugin-contribution-handler.ts @@ -21,7 +21,17 @@ import { TextmateRegistry, getEncodedLanguageId, MonacoTextmateService, GrammarD import { MenusContributionPointHandler } from './menus/menus-contribution-handler'; import { PluginViewRegistry } from './view/plugin-view-registry'; import { PluginCustomEditorRegistry } from './custom-editors/plugin-custom-editor-registry'; -import { PluginContribution, IndentationRules, FoldingRules, ScopeMap, DeployedPlugin, GrammarsContribution, EnterAction, OnEnterRule, RegExpOptions } from '../../common'; +import { + PluginContribution, + IndentationRules, + FoldingRules, + ScopeMap, + DeployedPlugin, + GrammarsContribution, + EnterAction, + OnEnterRule, + RegExpOptions, + IconContribution } from '../../common'; import { DefaultUriLabelProviderContribution, LabelProviderContribution, @@ -39,6 +49,7 @@ import { PluginDebugService } from './debug/plugin-debug-service'; import { DebugSchemaUpdater } from '@theia/debug/lib/browser/debug-schema-updater'; import { MonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service'; import { ColorRegistry } from '@theia/core/lib/browser/color-registry'; +import { PluginIconService } from './plugin-icon-service'; import { PluginIconThemeService } from './plugin-icon-theme-service'; import { ContributionProvider } from '@theia/core/lib/common'; import * as monaco from '@theia/monaco-editor-core'; @@ -108,6 +119,9 @@ export class PluginContributionHandler { @inject(ColorRegistry) protected readonly colors: ColorRegistry; + @inject(PluginIconService) + protected readonly iconService: PluginIconService; + @inject(PluginIconThemeService) protected readonly iconThemeService: PluginIconThemeService; @@ -322,6 +336,19 @@ export class PluginContributionHandler { } } + if (contributions.icons && contributions.icons.length) { + for (const icon of contributions.icons) { + const defaultIcon = icon.defaults; + let key: string; + if (IconContribution.isIconDefinition(defaultIcon)) { + key = defaultIcon.location; + } else { + key = defaultIcon.id; + } + pushContribution(`icons.${key}`, () => this.iconService.register(icon, plugin)); + } + } + const colors = contributions.colors; if (colors) { pushContribution('colors', () => this.colors.register(...colors)); diff --git a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts index f737307a54961..6bf19e5985ef1 100644 --- a/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts +++ b/packages/plugin-ext/src/main/browser/plugin-ext-frontend-module.ts @@ -80,6 +80,7 @@ import { bindTreeViewDecoratorUtilities, TreeViewDecoratorService } from './view import { CodeEditorWidgetUtil } from './menus/vscode-theia-menu-mappings'; import { PluginMenuCommandAdapter } from './menus/plugin-menu-command-adapter'; import './theme-icon-override'; +import { PluginIconService } from './plugin-icon-service'; import { PluginTerminalRegistry } from './plugin-terminal-registry'; import { DnDFileContentStore } from './view/dnd-file-content-store'; import { WebviewContextKeys } from './webview/webview-context-keys'; @@ -248,6 +249,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(WebviewFrontendSecurityWarnings).toSelf().inSingletonScope(); bind(FrontendApplicationContribution).toService(WebviewFrontendSecurityWarnings); + bind(PluginIconService).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(PluginIconService); + bind(PluginAuthenticationServiceImpl).toSelf().inSingletonScope(); rebind(AuthenticationService).toService(PluginAuthenticationServiceImpl); diff --git a/packages/plugin-ext/src/main/browser/plugin-icon-service.ts b/packages/plugin-ext/src/main/browser/plugin-icon-service.ts new file mode 100644 index 0000000000000..b8a4b5092ae9c --- /dev/null +++ b/packages/plugin-ext/src/main/browser/plugin-icon-service.ts @@ -0,0 +1,97 @@ +// ***************************************************************************** +// Copyright (C) 2023 Ericsson 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 +// ***************************************************************************** + +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { Disposable } from '@theia/core/lib/common/disposable'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { URI } from '@theia/core/shared/vscode-uri'; +import { IconRegistry, IconStyleSheetService } from '@theia/monaco/lib/browser/monaco-icon-registry-types'; +import * as path from 'path'; +import { IconContribution, DeployedPlugin } from '../../common/plugin-protocol'; + +@injectable() +export class PluginIconService implements Disposable, FrontendApplicationContribution { + + @inject(IconRegistry) + protected readonly iconRegistry: IconRegistry; + + @inject(IconStyleSheetService) + protected readonly iconStyleSheetService: IconStyleSheetService; + + styleSheet: string = ''; + + styleElement: HTMLStyleElement; + + // @inject(ContributionProvider) + // protected readonly iconContributions: ContributionProvider; + + async onStart(): Promise { + console.log('**** alvs, PluginIconService onStart'); + // NOTE: The registration of the following icons is performed by the icon registry by default + // this.iconRegistry.registerIcon('goto-previous-location', { id: 'arrow-up'}, 'Icon for goto previous editor location.'); + // this.iconRegistry.registerIcon('goto-next-location', { id: 'arrowDown'}, 'Icon for goto next editor location.'); + + } + + register(contribution: IconContribution, plugin: DeployedPlugin): Disposable { + const defaultIcon = contribution.defaults; + if (IconContribution.isIconDefinition(defaultIcon)) { + const location = defaultIcon.location; + const format = getFileExtension(location); + const fontId = getFontId(contribution.extensionId, location); + const definition = this.iconRegistry.registerIconFont(fontId, { src: [{ location: URI.file(location), format }] }); + this.iconRegistry.registerIcon(contribution.id, { + fontCharacter: defaultIcon.fontCharacter, + font: { + id: fontId, + definition + } + }, contribution.description); + } else { + this.iconRegistry.registerIcon(contribution.id, { id: defaultIcon.id}, contribution.description); + } + console.warn('**** alvs, iconContributionr registered: ', contribution.id); + this.updateStyle(); + return Disposable.NULL; + } + + updateStyle(): void { + if (!this.styleElement) { + const styleElement = document.createElement('style'); + styleElement.type = 'text/css'; + styleElement.media = 'screen'; + styleElement.id = 'contributedIconsStyles'; + document.head.appendChild(styleElement); + this.styleElement = styleElement; + } + + this.styleElement.innerText = this.iconStyleSheetService.getIconsStyleSheet().getCSS(); + // const toRemoveStyleElement = Disposable.create(() => styleElement.remove()); + } + + dispose(): void { + // Implement me + } +} + +function getFontId(extensionId: string, fontPath: string): string { + return path.join(extensionId, fontPath); +} + +function getFileExtension(filePath: string): string { + const index = filePath.lastIndexOf('.'); + return index === -1 ? '' : filePath.substring(index + 1); +} diff --git a/packages/plugin-ext/src/main/browser/webview/webview-frontend-security-warnings.ts b/packages/plugin-ext/src/main/browser/webview/webview-frontend-security-warnings.ts index 7435b78eedf0f..3fd3ac8cd911b 100644 --- a/packages/plugin-ext/src/main/browser/webview/webview-frontend-security-warnings.ts +++ b/packages/plugin-ext/src/main/browser/webview/webview-frontend-security-warnings.ts @@ -40,6 +40,7 @@ export class WebviewFrontendSecurityWarnings implements FrontendApplicationContr } protected async checkHostPattern(): Promise { + console.log('**** alvs, WebviewFrontendSecurityWarnings checkHostPattern'); if (FrontendApplicationConfigProvider.get()['warnOnPotentiallyInsecureHostPattern'] === false) { return; }