diff --git a/examples/api-tests/src/monaco-api.spec.js b/examples/api-tests/src/monaco-api.spec.js index ab74e94655faa..958ec6ed960b7 100644 --- a/examples/api-tests/src/monaco-api.spec.js +++ b/examples/api-tests/src/monaco-api.spec.js @@ -25,12 +25,13 @@ describe('Monaco API', async function () { const { WorkspaceService } = require('@theia/workspace/lib/browser/workspace-service'); const { MonacoEditor } = require('@theia/monaco/lib/browser/monaco-editor'); const { MonacoResolvedKeybinding } = require('@theia/monaco/lib/browser/monaco-resolved-keybinding'); + const { MonacoTextmateService } = require('@theia/monaco/lib/browser/textmate/monaco-textmate-service'); /** @type {import('inversify').Container} */ const container = window['theia'].container; const editorManager = container.get(EditorManager); const workspaceService = container.get(WorkspaceService); - + const textmateService = container.get(MonacoTextmateService); /** @type {MonacoEditor} */ let monacoEditor; @@ -87,4 +88,24 @@ describe('Monaco API', async function () { } }); + it('TokenizationRegistry.getColorMap', async () => { + if (textmateService['monacoThemeRegistry'].getThemeData().base !== 'vs') { + const didChangeColorMap = new Promise(resolve => { + const toDispose = monaco.modes.TokenizationRegistry.onDidChange(() => { + toDispose.dispose(); + resolve(); + }) + }); + textmateService['themeService'].setCurrentTheme('light'); + await didChangeColorMap; + } + + const textMateColorMap = textmateService['grammarRegistry'].getColorMap(); + assert.notEqual(textMateColorMap.indexOf('#795E26'), -1, 'Expected custom toke colors for the ligth theme to be enabled.') + + const monacoColorMap = monaco.modes.TokenizationRegistry.getColorMap(). + splice(0, textMateColorMap.length).map(c => c.toString().toUpperCase()); + assert.deepStrictEqual(monacoColorMap, textMateColorMap, 'Expected textmate colors to have the same index in the monaco color map.'); + }); + }); diff --git a/packages/monaco/src/browser/textmate/monaco-textmate-service.ts b/packages/monaco/src/browser/textmate/monaco-textmate-service.ts index a48e50fa32b41..6a36eb5d0c8e1 100644 --- a/packages/monaco/src/browser/textmate/monaco-textmate-service.ts +++ b/packages/monaco/src/browser/textmate/monaco-textmate-service.ts @@ -74,7 +74,7 @@ export class MonacoTextmateService implements FrontendApplicationContribution { this.grammarRegistry = new Registry({ getOnigLib: () => this.onigasmPromise, - theme: this.monacoThemeRegistry.getTheme(this.currentEditorTheme), + theme: this.monacoThemeRegistry.getThemeData(this.currentEditorTheme), loadGrammar: async (scopeName: string) => { const provider = this.textmateRegistry.getProvider(scopeName); if (provider) { @@ -116,7 +116,7 @@ export class MonacoTextmateService implements FrontendApplicationContribution { this.toDisposeOnUpdateTheme.push(Disposable.create(() => document.body.classList.remove(currentEditorTheme))); // first update registry to run tokenization with the proper theme - const theme = this.monacoThemeRegistry.getTheme(currentEditorTheme); + const theme = this.monacoThemeRegistry.getThemeData(currentEditorTheme); if (theme) { this.grammarRegistry.setTheme(theme); } diff --git a/packages/monaco/src/browser/textmate/monaco-theme-registry.ts b/packages/monaco/src/browser/textmate/monaco-theme-registry.ts index 610ead0383450..32ed0648e0050 100644 --- a/packages/monaco/src/browser/textmate/monaco-theme-registry.ts +++ b/packages/monaco/src/browser/textmate/monaco-theme-registry.ts @@ -17,22 +17,37 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { IRawTheme, Registry } from 'vscode-textmate'; +import { IRawTheme, Registry, IRawThemeSetting } from 'vscode-textmate'; export interface ThemeMix extends IRawTheme, monaco.editor.IStandaloneThemeData { } +export interface MixStandaloneTheme extends monaco.services.IStandaloneTheme { + themeData: ThemeMix +} export class MonacoThemeRegistry { - protected themes = new Map(); + getThemeData(): ThemeMix; + getThemeData(name: string): ThemeMix | undefined; + getThemeData(name?: string): ThemeMix | undefined { + const theme = this.doGetTheme(name); + return theme && theme.themeData; + } + + getTheme(): MixStandaloneTheme; + getTheme(name: string): MixStandaloneTheme | undefined; + getTheme(name?: string): MixStandaloneTheme | undefined { + return this.doGetTheme(name); + } - public getTheme(name: string): IRawTheme | undefined { - return this.themes.get(name); + protected doGetTheme(name: string | undefined): MixStandaloneTheme | undefined { + const standaloneThemeService = monaco.services.StaticServices.standaloneThemeService.get(); + const theme = !name ? standaloneThemeService.getTheme() : standaloneThemeService._knownThemes.get(name); + return theme as MixStandaloneTheme | undefined; } setTheme(name: string, data: ThemeMix): void { // monaco auto refrehes a theme with new data monaco.editor.defineTheme(name, data); - this.themes.set(name, data); } /** @@ -58,8 +73,20 @@ export class MonacoThemeRegistry { result.settings.push(...parentTheme.settings); } } - if (json.tokenColors) { - result.settings.push(...json.tokenColors); + const tokenColors: Array = json.tokenColors; + if (Array.isArray(tokenColors)) { + for (const tokenColor of tokenColors) { + if (tokenColor.scope && tokenColor.settings) { + result.settings.push({ + scope: tokenColor.scope, + settings: { + foreground: this.normalizeColor(tokenColor.settings.foreground), + background: this.normalizeColor(tokenColor.settings.background), + fontStyle: tokenColor.settings.fontStyle + } + }); + } + } } if (json.colors) { Object.assign(result.colors, json.colors); @@ -95,22 +122,25 @@ export class MonacoThemeRegistry { } for (const scope of tokenColor.scope) { - - // Converting numbers into a format that monaco understands - const settings = Object.keys(tokenColor.settings).reduce((previous: { [key: string]: string }, current) => { - let value: string = tokenColor.settings[current]; - if (typeof value === typeof '') { - value = value.replace(/^\#/, '').slice(0, 6); - } - previous[current] = value; - return previous; - }, {}); - acceptor({ - ...settings, token: scope + ...tokenColor.settings, token: scope }); } } + + protected normalizeColor(color: string | undefined): string | undefined { + if (!color) { + return undefined; + } + color = color.replace(/^\#/, '').slice(0, 6); + if (color.length < 6) { + // ignoring not normalized colors to avoid breaking token color indexes between monaco and vscode-textmate + console.error(`Color '${color}' is NOT normalized, it must have 6 positions.`); + return undefined; + } + return '#' + color; + } + } export namespace MonacoThemeRegistry { diff --git a/packages/monaco/src/typings/monaco/index.d.ts b/packages/monaco/src/typings/monaco/index.d.ts index a75214c879044..9ce3271b97823 100644 --- a/packages/monaco/src/typings/monaco/index.d.ts +++ b/packages/monaco/src/typings/monaco/index.d.ts @@ -486,10 +486,12 @@ declare module monaco.services { } export interface IStandaloneThemeService extends monaco.theme.IThemeService { + readonly _knownThemes: Map; getTheme(): IStandaloneTheme; } export interface IStandaloneTheme { + themeData: monaco.editor.IStandaloneThemeData tokenTheme: TokenTheme; getColor(color: string): Color | undefined; } @@ -858,6 +860,8 @@ declare module monaco.modes { } export interface TokenizationRegistry { get(language: string): ITokenizationSupport | null; + getColorMap(): monaco.color.Color[] | null; + readonly onDidChange: monaco.IEvent; } export const TokenizationRegistry: TokenizationRegistry;