From 955e0c8c3b46d77fa1ebd44d1d1789e2a4e42526 Mon Sep 17 00:00:00 2001 From: Kai Salmen Date: Fri, 6 Oct 2023 23:03:37 +0200 Subject: [PATCH] Ensure vscode editor always comes with themes and textmate enabled. --- package-lock.json | 6 ++-- packages/examples/package.json | 3 -- .../langium/config/wrapperLangiumVscode.ts | 10 ------ .../config/wrapperStatemachineConfig.ts | 10 ------ packages/examples/src/reactPython.tsx | 9 +---- packages/examples/src/wrapperWs.ts | 33 +++++-------------- packages/monaco-editor-wrapper/package.json | 3 ++ .../src/editorAppBase.ts | 1 + .../src/editorAppClassic.ts | 6 +++- .../src/editorAppVscodeApi.ts | 20 ++++++++--- .../src/languageClientWrapper.ts | 9 ++--- packages/monaco-editor-wrapper/src/wrapper.ts | 20 ++++++----- .../test/languageClientWrapper.test.ts | 12 +++---- 13 files changed, 58 insertions(+), 84 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6097af9..520036c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7581,9 +7581,6 @@ "@codingame/monaco-vscode-json-default-extension": "~1.82.3", "@codingame/monaco-vscode-keybindings-service-override": "~1.82.3", "@codingame/monaco-vscode-python-default-extension": "~1.82.3", - "@codingame/monaco-vscode-textmate-service-override": "~1.82.3", - "@codingame/monaco-vscode-theme-defaults-default-extension": "~1.82.3", - "@codingame/monaco-vscode-theme-service-override": "~1.82.3", "@typefox/monaco-editor-react": "~2.2.4", "http-server": "~14.1.1", "langium": "~2.0.2", @@ -7623,6 +7620,9 @@ "license": "MIT", "dependencies": { "@codingame/monaco-vscode-configuration-service-override": "~1.82.3", + "@codingame/monaco-vscode-textmate-service-override": "~1.82.3", + "@codingame/monaco-vscode-theme-defaults-default-extension": "~1.82.3", + "@codingame/monaco-vscode-theme-service-override": "~1.82.3", "monaco-languageclient": "~6.5.1" }, "peerDependencies": { diff --git a/packages/examples/package.json b/packages/examples/package.json index 50f72e8..8ec7ac1 100644 --- a/packages/examples/package.json +++ b/packages/examples/package.json @@ -9,9 +9,6 @@ "@codingame/monaco-vscode-json-default-extension": "~1.82.3", "@codingame/monaco-vscode-keybindings-service-override": "~1.82.3", "@codingame/monaco-vscode-python-default-extension": "~1.82.3", - "@codingame/monaco-vscode-textmate-service-override": "~1.82.3", - "@codingame/monaco-vscode-theme-defaults-default-extension": "~1.82.3", - "@codingame/monaco-vscode-theme-service-override": "~1.82.3", "@typefox/monaco-editor-react": "~2.2.4", "http-server": "~14.1.1", "langium": "~2.0.2", diff --git a/packages/examples/src/langium/config/wrapperLangiumVscode.ts b/packages/examples/src/langium/config/wrapperLangiumVscode.ts index a95792f..cc6fa5f 100644 --- a/packages/examples/src/langium/config/wrapperLangiumVscode.ts +++ b/packages/examples/src/langium/config/wrapperLangiumVscode.ts @@ -1,10 +1,5 @@ -import { Uri } from 'vscode'; -import getConfigurationServiceOverride from '@codingame/monaco-vscode-configuration-service-override'; import getEditorServiceOverride from '@codingame/monaco-vscode-editor-service-override'; import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; -import getThemeServiceOverride from '@codingame/monaco-vscode-theme-service-override'; -import getTextmateServiceOverride from '@codingame/monaco-vscode-textmate-service-override'; -import { whenReady as whenReadyThemes } from '@codingame/monaco-vscode-theme-defaults-default-extension'; import { useOpenEditorStub } from 'monaco-languageclient'; import { UserConfig } from 'monaco-editor-wrapper'; import { getTextContent } from '../../common.js'; @@ -25,9 +20,6 @@ export const setupLangiumClientVscodeApi = async (): Promise => { wrapperConfig: { serviceConfig: { userServices: { - ...getThemeServiceOverride(), - ...getTextmateServiceOverride(), - ...getConfigurationServiceOverride(Uri.file('/workspace')), ...getEditorServiceOverride(useOpenEditorStub), ...getKeybindingsServiceOverride() }, @@ -38,8 +30,6 @@ export const setupLangiumClientVscodeApi = async (): Promise => { languageId: 'langium', code: code, useDiffEditor: false, - // Ensure all required extensions are loaded before setting up the language extension - awaitExtensionReadiness: [whenReadyThemes], extensions: [{ config: { name: 'langium-example', diff --git a/packages/examples/src/langium/config/wrapperStatemachineConfig.ts b/packages/examples/src/langium/config/wrapperStatemachineConfig.ts index a330d7f..6b77498 100644 --- a/packages/examples/src/langium/config/wrapperStatemachineConfig.ts +++ b/packages/examples/src/langium/config/wrapperStatemachineConfig.ts @@ -1,10 +1,5 @@ -import getConfigurationServiceOverride from '@codingame/monaco-vscode-configuration-service-override'; import getEditorServiceOverride from '@codingame/monaco-vscode-editor-service-override'; import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; -import getThemeServiceOverride from '@codingame/monaco-vscode-theme-service-override'; -import getTextmateServiceOverride from '@codingame/monaco-vscode-textmate-service-override'; -import { whenReady as whenReadyThemes } from '@codingame/monaco-vscode-theme-defaults-default-extension'; -import { Uri } from 'vscode'; import { useOpenEditorStub } from 'monaco-languageclient'; import { UserConfig } from 'monaco-editor-wrapper'; import { loadStatemachinWorker } from '../wrapperStatemachine.js'; @@ -25,9 +20,6 @@ export const createLangiumGlobalConfig = async (): Promise => { wrapperConfig: { serviceConfig: { userServices: { - ...getThemeServiceOverride(), - ...getTextmateServiceOverride(), - ...getConfigurationServiceOverride(Uri.file('/workspace')), ...getEditorServiceOverride(useOpenEditorStub), ...getKeybindingsServiceOverride() }, @@ -38,8 +30,6 @@ export const createLangiumGlobalConfig = async (): Promise => { languageId: 'statemachine', code: code, useDiffEditor: false, - // Ensure all required extensions are loaded before setting up the language extension - awaitExtensionReadiness: [whenReadyThemes], extensions: [{ config: { name: 'statemachine-example', diff --git a/packages/examples/src/reactPython.tsx b/packages/examples/src/reactPython.tsx index 3af279f..7f8115e 100644 --- a/packages/examples/src/reactPython.tsx +++ b/packages/examples/src/reactPython.tsx @@ -1,8 +1,4 @@ -import getConfigurationServiceOverride from '@codingame/monaco-vscode-configuration-service-override'; import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; -import getThemeServiceOverride from '@codingame/monaco-vscode-theme-service-override'; -import getTextmateServiceOverride from '@codingame/monaco-vscode-textmate-service-override'; -import { whenReady as whenReadyThemes } from '@codingame/monaco-vscode-theme-defaults-default-extension'; import { whenReady as whenReadyPython } from '@codingame/monaco-vscode-python-default-extension'; import React from 'react'; import ReactDOM from 'react-dom/client'; @@ -56,9 +52,6 @@ const userConfig: UserConfig = { wrapperConfig: { serviceConfig: { userServices: { - ...getThemeServiceOverride(), - ...getTextmateServiceOverride(), - ...getConfigurationServiceOverride(Uri.file('/workspace')), ...getKeybindingsServiceOverride() }, debugLogging: true @@ -67,7 +60,7 @@ const userConfig: UserConfig = { $type: 'vscodeApi', languageId: 'python', codeUri: '/workspace/python.py', - awaitExtensionReadiness: [whenReadyThemes, whenReadyPython], + awaitExtensionReadiness: [whenReadyPython], extensions: [{ config: { name: 'python-client', diff --git a/packages/examples/src/wrapperWs.ts b/packages/examples/src/wrapperWs.ts index 47ffd36..cb18f90 100644 --- a/packages/examples/src/wrapperWs.ts +++ b/packages/examples/src/wrapperWs.ts @@ -1,7 +1,4 @@ import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; -import getThemeServiceOverride from '@codingame/monaco-vscode-theme-service-override'; -import getTextmateServiceOverride from '@codingame/monaco-vscode-textmate-service-override'; -import { whenReady as whenReadyTheme } from '@codingame/monaco-vscode-theme-defaults-default-extension'; import { whenReady as whenReadyJson } from '@codingame/monaco-vscode-json-default-extension'; import { disposeEditor, startEditor, swapEditors } from './common.js'; import { UserConfig } from 'monaco-editor-wrapper'; @@ -19,42 +16,28 @@ const codeOrg = `{ "line_endings": {"value": "unix"} }`; -const monacoEditorConfig = { - glyphMargin: true, - guides: { - bracketPairs: true - }, - lightbulb: { - enabled: true - }, -}; - const userConfig: UserConfig = { wrapperConfig: { serviceConfig: { userServices: { - ...getThemeServiceOverride(), - ...getTextmateServiceOverride(), ...getKeybindingsServiceOverride(), }, debugLogging: true }, editorAppConfig: { - $type: 'classic', + $type: 'vscodeApi', languageId: languageId, code: codeMain, useDiffEditor: false, codeOriginal: codeOrg, - editorOptions: monacoEditorConfig, - diffEditorOptions: monacoEditorConfig, - theme: 'vs-dark', // Ensure all required extensions are loaded before setting up the language extension - awaitExtensionReadiness: [whenReadyTheme, whenReadyJson], - languageExtensionConfig: { - id: 'json', - extensions: ['.json', '.jsonc'], - aliases: ['JSON', 'json'], - mimetypes: ['application/json'] + awaitExtensionReadiness: [whenReadyJson], + userConfiguration: { + json: JSON.stringify({ + 'workbench.colorTheme': 'Default Dark Modern', + 'editor.guides.bracketPairsHorizontal': 'active', + 'editor.lightbulb.enabled': true + }) } } }, diff --git a/packages/monaco-editor-wrapper/package.json b/packages/monaco-editor-wrapper/package.json index d682c90..051742f 100644 --- a/packages/monaco-editor-wrapper/package.json +++ b/packages/monaco-editor-wrapper/package.json @@ -59,6 +59,9 @@ }, "dependencies": { "@codingame/monaco-vscode-configuration-service-override": "~1.82.3", + "@codingame/monaco-vscode-textmate-service-override": "~1.82.3", + "@codingame/monaco-vscode-theme-defaults-default-extension": "~1.82.3", + "@codingame/monaco-vscode-theme-service-override": "~1.82.3", "monaco-languageclient": "~6.5.1" }, "peerDependencies": { diff --git a/packages/monaco-editor-wrapper/src/editorAppBase.ts b/packages/monaco-editor-wrapper/src/editorAppBase.ts index 7e7890a..d4fcab6 100644 --- a/packages/monaco-editor-wrapper/src/editorAppBase.ts +++ b/packages/monaco-editor-wrapper/src/editorAppBase.ts @@ -230,6 +230,7 @@ export abstract class EditorAppBase { abstract getAppType(): string; abstract init(): Promise; + abstract specifyService(): editor.IEditorOverrideServices; abstract createEditors(container: HTMLElement): Promise; abstract getConfig(): EditorAppConfigClassic | EditorAppConfigVscodeApi; abstract disposeApp(): void; diff --git a/packages/monaco-editor-wrapper/src/editorAppClassic.ts b/packages/monaco-editor-wrapper/src/editorAppClassic.ts index 85fb248..0063a09 100644 --- a/packages/monaco-editor-wrapper/src/editorAppClassic.ts +++ b/packages/monaco-editor-wrapper/src/editorAppClassic.ts @@ -1,5 +1,5 @@ -import { EditorAppBase, EditorAppBaseConfig, EditorAppType } from './editorAppBase.js'; import { editor, languages } from 'monaco-editor'; +import { EditorAppBase, EditorAppBaseConfig, EditorAppType } from './editorAppBase.js'; import { UserConfig } from './wrapper.js'; import { Logger } from './logger.js'; /** @@ -76,6 +76,10 @@ export class EditorAppClassic extends EditorAppBase { return this.config; } + override specifyService(): editor.IEditorOverrideServices { + return {}; + } + async createEditors(container: HTMLElement): Promise { if (this.config.useDiffEditor) { await this.createDiffEditor(container, this.diffEditorOptions); diff --git a/packages/monaco-editor-wrapper/src/editorAppVscodeApi.ts b/packages/monaco-editor-wrapper/src/editorAppVscodeApi.ts index 58ebdb7..cb05fd9 100644 --- a/packages/monaco-editor-wrapper/src/editorAppVscodeApi.ts +++ b/packages/monaco-editor-wrapper/src/editorAppVscodeApi.ts @@ -1,9 +1,12 @@ import type * as vscode from 'vscode'; +import { IDisposable, editor } from 'monaco-editor'; +import getThemeServiceOverride from '@codingame/monaco-vscode-theme-service-override'; +import getTextmateServiceOverride from '@codingame/monaco-vscode-textmate-service-override'; +import { whenReady as whenReadyTheme } from '@codingame/monaco-vscode-theme-defaults-default-extension'; import { EditorAppBase, EditorAppBaseConfig, EditorAppType } from './editorAppBase.js'; import { registerExtension, IExtensionManifest, ExtensionHostKind } from 'vscode/extensions'; import { UserConfig } from './wrapper.js'; import { verifyUrlorCreateDataUrl } from './utils.js'; -import { IDisposable } from 'monaco-editor'; import { Logger } from './logger.js'; export type ExtensionConfig = { @@ -60,6 +63,13 @@ export class EditorAppVscodeApi extends EditorAppBase { return this.extensionRegisterResults.get(extensionName); } + override specifyService(): editor.IEditorOverrideServices { + return { + ...getThemeServiceOverride(), + ...getTextmateServiceOverride() + }; + } + async createEditors(container: HTMLElement): Promise { if (this.config.useDiffEditor) { await this.createDiffEditor(container); @@ -68,9 +78,11 @@ export class EditorAppVscodeApi extends EditorAppBase { } } - async init() { - // await all extenson that should be ready beforehand - await this.awaitReadiness(this.config.awaitExtensionReadiness); + override async init() { + // await all extensions that should be ready beforehand + // always await theme extension + const awaitReadiness = (this.config.awaitExtensionReadiness ?? []).concat(whenReadyTheme); + await this.awaitReadiness(awaitReadiness); if (this.config.extensions) { const allPromises: Array> = []; diff --git a/packages/monaco-editor-wrapper/src/languageClientWrapper.ts b/packages/monaco-editor-wrapper/src/languageClientWrapper.ts index 2f9d96c..d9d1246 100644 --- a/packages/monaco-editor-wrapper/src/languageClientWrapper.ts +++ b/packages/monaco-editor-wrapper/src/languageClientWrapper.ts @@ -72,11 +72,12 @@ export class LanguageClientWrapper { private languageClient: MonacoLanguageClient | undefined; private languageClientConfig?: LanguageClientConfig; private worker: Worker | undefined; - private languageId: string | undefined; + private languageId: string; private name; private logger: Logger | undefined; - constructor(languageClientConfig?: LanguageClientConfig, logger?: Logger) { + constructor(languageId: string, languageClientConfig?: LanguageClientConfig, logger?: Logger) { + this.languageId = languageId; if (languageClientConfig) { this.languageClientConfig = languageClientConfig; this.name = this.languageClientConfig.options.name ?? 'unnamed'; @@ -104,10 +105,6 @@ export class LanguageClientWrapper { return this.languageClient !== undefined && this.languageClient?.isRunning(); } - init(languageId: string) { - this.languageId = languageId; - } - async start() { if (this.languageClientConfig) { return this.startLanguageClientConnection(); diff --git a/packages/monaco-editor-wrapper/src/wrapper.ts b/packages/monaco-editor-wrapper/src/wrapper.ts index 079cb35..ae1e02c 100644 --- a/packages/monaco-editor-wrapper/src/wrapper.ts +++ b/packages/monaco-editor-wrapper/src/wrapper.ts @@ -1,5 +1,4 @@ -import { editor } from 'monaco-editor'; -import { Uri } from 'vscode'; +import { editor, Uri } from 'monaco-editor'; import getConfigurationServiceOverride from '@codingame/monaco-vscode-configuration-service-override'; import { initServices, wasVscodeApiInitialized, InitializeServiceConfig, MonacoLanguageClient, mergeServices } from 'monaco-languageclient'; import { EditorAppVscodeApi, EditorAppConfigVscodeApi } from './editorAppVscodeApi.js'; @@ -34,7 +33,7 @@ export class MonacoEditorLanguageClientWrapper { private serviceConfig: InitializeServiceConfig; private logger: Logger; - private async init(userConfig: UserConfig) { + private init(userConfig: UserConfig) { if (userConfig.wrapperConfig.editorAppConfig.useDiffEditor && !userConfig.wrapperConfig.editorAppConfig.codeOriginal) { throw new Error('Use diff editor was used without a valid config.'); } @@ -42,7 +41,9 @@ export class MonacoEditorLanguageClientWrapper { this.id = userConfig.id ?? Math.floor(Math.random() * 101).toString(); this.logger = new Logger(userConfig.loggerConfig); this.serviceConfig = userConfig.wrapperConfig.serviceConfig ?? {}; + } + private async initServices() { // always set required services if not configure this.serviceConfig.userServices = this.serviceConfig.userServices ?? {}; const configureService = this.serviceConfig.userServices.configure; @@ -53,6 +54,7 @@ export class MonacoEditorLanguageClientWrapper { }; mergeServices(mlcDefautServices, this.serviceConfig.userServices); } + mergeServices(this.editorApp?.specifyService() ?? {}, this.serviceConfig.userServices); // overrule debug log flag this.serviceConfig.debugLogging = this.logger.isEnabled() && (this.serviceConfig.debugLogging || this.logger.isDebugEnabled()); @@ -63,26 +65,28 @@ export class MonacoEditorLanguageClientWrapper { this.logger.debug('Init Services', this.serviceConfig.debugLogging); await initServices(this.serviceConfig); } - this.languageClientWrapper = new LanguageClientWrapper(userConfig.languageClientConfig, this.logger); } async start(userConfig: UserConfig, htmlElement: HTMLElement | null) { if (!htmlElement) { throw new Error('No HTMLElement provided for monaco-editor.'); } - await this.init(userConfig); - // Always dispose old instances before start this.editorApp?.disposeApp(); + this.init(userConfig); + if (isVscodeApiEditorApp(userConfig.wrapperConfig)) { this.editorApp = new EditorAppVscodeApi(this.id, userConfig, this.logger); } else { this.editorApp = new EditorAppClassic(this.id, userConfig, this.logger); } - this.languageClientWrapper.init(this.editorApp.getConfig().languageId); - this.logger.info(`Starting monaco-editor (${this.id})`); + await this.initServices(); + + this.languageClientWrapper = new LanguageClientWrapper(this.editorApp.getConfig().languageId, + userConfig.languageClientConfig, this.logger); + this.logger.info(`Starting monaco-editor (${this.id})`); await this.editorApp?.init(); await this.editorApp.createEditors(htmlElement); diff --git a/packages/monaco-editor-wrapper/test/languageClientWrapper.test.ts b/packages/monaco-editor-wrapper/test/languageClientWrapper.test.ts index 09dfb51..2cc62ce 100644 --- a/packages/monaco-editor-wrapper/test/languageClientWrapper.test.ts +++ b/packages/monaco-editor-wrapper/test/languageClientWrapper.test.ts @@ -4,14 +4,14 @@ import { LanguageClientConfig, LanguageClientWrapper } from 'monaco-editor-wrapp describe('Test LanguageClientWrapper', () => { test('Not Running after construction', () => { - const languageClientWrapper = new LanguageClientWrapper(); + const languageClientWrapper = new LanguageClientWrapper('my-lang'); expect(languageClientWrapper.haveLanguageClient()).toBeFalsy(); expect(languageClientWrapper.haveLanguageClientConfig()).toBeFalsy(); expect(languageClientWrapper.isStarted()).toBeFalsy(); }); test('Constructor: no config', async () => { - const languageClientWrapper = new LanguageClientWrapper(); + const languageClientWrapper = new LanguageClientWrapper('my-lang'); expect(async () => { await languageClientWrapper.start(); }).rejects.toEqual({ @@ -37,7 +37,7 @@ describe('Test LanguageClientWrapper', () => { }); // setup the wrapper - const languageClientWrapper = new LanguageClientWrapper({ + const languageClientWrapper = new LanguageClientWrapper('my-lang', { options: { $type: 'WorkerDirect', worker @@ -61,7 +61,7 @@ describe('Test LanguageClientWrapper', () => { url: 'ws://localhost:12345/Tester' } }; - const languageClientWrapper = new LanguageClientWrapper(languageClientConfig); + const languageClientWrapper = new LanguageClientWrapper('my-lang', languageClientConfig); expect(languageClientWrapper.haveLanguageClientConfig()).toBeTruthy(); }); @@ -73,7 +73,7 @@ describe('Test LanguageClientWrapper', () => { name: 'test-unreachable' } }; - const languageClientWrapper = new LanguageClientWrapper(languageClientConfig); + const languageClientWrapper = new LanguageClientWrapper('my-lang', languageClientConfig); expect(languageClientWrapper.haveLanguageClientConfig()).toBeTruthy(); await expect(languageClientWrapper.start()).rejects.toEqual({ message: 'languageClientWrapper (test-unreachable): Websocket connection failed.', @@ -100,7 +100,7 @@ describe('Test LanguageClientWrapper', () => { type: 'classic' } }; - const languageClientWrapper = new LanguageClientWrapper(languageClientConfig); + const languageClientWrapper = new LanguageClientWrapper('my-lang', languageClientConfig); expect(languageClientWrapper.haveLanguageClientConfig()).toBeTruthy(); await expect(languageClientWrapper.start()).rejects.toEqual({ message: 'languageClientWrapper (unnamed): Illegal worker configuration detected. Potentially the url is wrong.',