Skip to content
This repository has been archived by the owner on Mar 22, 2024. It is now read-only.

Commit

Permalink
Properly separate classic and extended app (code and terminology)
Browse files Browse the repository at this point in the history
  • Loading branch information
kaisalmen committed Oct 7, 2023
1 parent a662b88 commit 9c4bff5
Show file tree
Hide file tree
Showing 17 changed files with 151 additions and 154 deletions.
5 changes: 0 additions & 5 deletions packages/examples/src/langium/config/wrapperLangiumClassic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,11 @@ export const setupLangiumClientClassic = async (): Promise<UserConfig> => {
code: code,
useDiffEditor: false,
theme: 'vs-dark',
// configure it like this or in the userConfiguration
editorOptions: {
'semanticHighlighting.enabled': true
},
languageExtensionConfig: { id: 'langium' },
languageDef: LangiumMonarchContent,
userConfiguration: {
// or configure the semantic highlighting like this:
// `{ json: "editor.semanticHighlighting.enabled": true }`
}
}
},
languageClientConfig: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { UserConfig } from 'monaco-editor-wrapper';
import { getTextContent } from '../../common.js';
import { loadLangiumWorker } from '../wrapperLangium.js';

export const setupLangiumClientVscodeApi = async (): Promise<UserConfig> => {
export const setupLangiumClientExtended = async (): Promise<UserConfig> => {
const code = await getTextContent(new URL('./src/langium/content/example.langium', window.location.href));

const extensionFilesOrContents = new Map<string, string | URL>();
Expand All @@ -26,7 +26,7 @@ export const setupLangiumClientVscodeApi = async (): Promise<UserConfig> => {
debugLogging: true
},
editorAppConfig: {
$type: 'vscodeApi',
$type: 'extended',
languageId: 'langium',
code: code,
useDiffEditor: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const createLangiumGlobalConfig = async (): Promise<UserConfig> => {
debugLogging: true
},
editorAppConfig: {
$type: 'vscodeApi',
$type: 'extended',
languageId: 'statemachine',
code: code,
useDiffEditor: false,
Expand Down
8 changes: 4 additions & 4 deletions packages/examples/src/langium/wrapperLangium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* ------------------------------------------------------------------------------------------ */

import { MonacoEditorLanguageClientWrapper } from 'monaco-editor-wrapper';
import { setupLangiumClientVscodeApi } from './config/wrapperLangiumVscode.js';
import { setupLangiumClientExtended } from './config/wrapperLangiumExtended.js';
import { setupLangiumClientClassic } from './config/wrapperLangiumClassic.js';
import { buildWorkerDefinition } from 'monaco-editor-workers';

Expand All @@ -16,17 +16,17 @@ const htmlElement = document.getElementById('monaco-editor-root');
export const run = async () => {
try {
document.querySelector('#button-start-classic')?.addEventListener('click', startLangiumClientClassic);
document.querySelector('#button-start-vscode-api')?.addEventListener('click', startLangiumClientVscodeApi);
document.querySelector('#button-start-extended')?.addEventListener('click', startLangiumClientExtended);
document.querySelector('#button-dispose')?.addEventListener('click', disposeEditor);
} catch (e) {
console.error(e);
}
};

export const startLangiumClientVscodeApi = async () => {
export const startLangiumClientExtended = async () => {
try {
if (checkStarted()) return;
const config = await setupLangiumClientVscodeApi();
const config = await setupLangiumClientExtended();
wrapper = new MonacoEditorLanguageClientWrapper();
wrapper.start(config, htmlElement);
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/src/reactPython.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const userConfig: UserConfig = {
debugLogging: true
},
editorAppConfig: {
$type: 'vscodeApi',
$type: 'extended',
languageId: 'python',
codeUri: '/workspace/python.py',
awaitExtensionReadiness: [whenReadyPython],
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/src/wrapperTs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const userConfig: UserConfig = {
editorAppConfig: {
$type: 'classic',
languageId: 'typescript',
code: code,
code,
codeUri: codeUri,
codeOriginal: codeOriginal,
useDiffEditor: false,
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/src/wrapperWs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const userConfig: UserConfig = {
debugLogging: true
},
editorAppConfig: {
$type: 'vscodeApi',
$type: 'extended',
languageId: languageId,
code: codeMain,
useDiffEditor: false,
Expand Down
2 changes: 1 addition & 1 deletion packages/examples/wrapper_langium.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<h2>Langium Grammar DSL Language Client & Server (Worker)</h2>
<div style="padding: 0px 5px 5px 0px">
<button type="button" id="button-start-classic">Start Classic</button>
<button type="button" id="button-start-vscode-api">Start VscodeApi</button>
<button type="button" id="button-start-extended">Start Exentended</button>
<button type="button" id="button-dispose">Dispose</button>
</div>
<div id="monaco-editor-root" style="height: 80vh; border: 1px solid grey"></div>
Expand Down
61 changes: 44 additions & 17 deletions packages/monaco-editor-wrapper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,44 @@ npm run build
This will clean, compile and build a bundle of the `monaco-editor-wrapper`, which you can reference in your own projects. For examples, you can see the top-level [README](../../README.md#getting-started) with details on running a local dev instance.

## Configuration
s
With release 2.0.0, the configuration approach is completely revised.

The `UserConfig` now contains everything and is passed to the `start` function of the wrapper. Because [monaco-vscode-api](https://github.com/CodinGame/monaco-vscode-api) uses a VS Code extension like configuration approach, the `UserConfig` allows to configure monaco-editor the [classical way](./src/editorAppClassic.ts) or to use [monaco-vscode-api way](./src/editorAppVscodeApi.ts). Additionally, [monaco-vscode-api](https://github.com/CodinGame/monaco-vscode-api) brings VS Code services to monaco-editor it usually does not have (Textmate Support, VS Code Theme Support, Keybindings, etc.). The wrapper initializes the following services from monaco-vscode-api independent of the editor mode: *model*, *editor* and *configuration*.
With release >2.0.0, the configuration approach is completely revised.

The `UserConfig` now contains everything and is passed to the `start` function of the wrapper along with the HTML element `monaco-editor` is bound to.

[@codingame/monaco-vscode-api](https://github.com/CodinGame/monaco-vscode-api) implements the VSCode api and redirects calls to `monaco-editor`. It allows to add serivccs that are usually only available in VSCode and not with pure `monaco-editor`.
`UserConfig` allows two possible configuration modes:

- **Classical**: Configure `monaco-editor` as you would when using it directly, [see](./src/editorAppClassic.ts)
- **Extended**: Configure `monaco-editor` like a VSCode extension, [see](./src/editorAppExtended.ts)

[This](https://github.com/CodinGame/monaco-vscode-api#monaco-standalone-services) is the list of services defined by [@codingame/monaco-vscode-api](https://github.com/CodinGame/monaco-vscode-api).
The following services are enabled by default in both editor modes:

- layout
- environment
- extension
- files
- quickAccess
- languages
- model
- configuration

**Extended** mode adds the following:

- theme
- textmate

If you want any further services than the ones initialized by default, you should use the **extended** mode as some service (like *theme* and *textmate*) are incompatible with the **classic** mode.

Monarch grammars and themes can only be used in **classical** mode and textmate grammars and themes can only be used in **extended** mode.

## Usage

Monaco Editor with TypeScript language support in web worker and relying on regular monaco-editor configuration:
Monaco Editor with TypeScript language support in web worker and relying on classic mode:

```typescript
```ts
import { MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper';

import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution.js';
import 'monaco-editor/esm/vs/language/typescript/monaco.contribution.js';

Expand All @@ -38,30 +64,31 @@ buildWorkerDefinition('./node_modules/monaco-editor-workers/dist/workers', impor
// no top-level await
const run = async () => {
const wrapper = new MonacoEditorLanguageClientWrapper();
const code: `function sayHello(): string {
return "Hello";
};`,
const userConfig = {
htmlElement: document.getElementById('monaco-editor-root') as HTMLElement,
// rely on regular monaco-editor configuration
wrapperConfig: {
editorAppConfig: {
$type: 'classic',
languageId: 'typescript',
code: `function sayHello(): string {
return "Hello";
};`,
code,
useDiffEditor: false,
}
}
};
await wrapper.start(userConfig);

const htmlElement = document.getElementById('monaco-editor-root');
await wrapper.start(userConfig, htmlElement);
}
```

## Examples

These are the examples specifically for `monaco-editor-wrapper` you find in the repository:

- TypeScript editor worker using classical configuration, [see](../examples/wrapper_ts.html)
- Language client & web socket language server example using vscode-api configuration [see](../examples/wrapper_ws.html) It requires the json language server to run. Use `start:server:json` from [here](../examples/package.json)
- Multiple editors using classical configuration [see](../examples/wrapper_adv.html)
- Langium statemachine language client and web worker based language server using vscode-api configuration [see](../examples/wrapper_statemachine.html)
- Langium grammar language client and web worker based language server using vscode-api configuration [see](../examples/wrapper_langium.html)
- TypeScript editor worker using classical mode, [see](../examples/wrapper_ts.html)
- Language client & web socket language server example using extended mode [see](../examples/wrapper_ws.html) It requires the json language server to run. Use `start:server:json` from [here](../examples/package.json)
- Multiple editors using classical mode [see](../examples/wrapper_adv.html)
- Langium statemachine language client and web worker based language server using extended mode [see](../examples/wrapper_statemachine.html)
- Langium grammar language client and web worker based language server allowing to choose classical or extended mode [see](../examples/wrapper_langium.html)
47 changes: 20 additions & 27 deletions packages/monaco-editor-wrapper/src/editorAppBase.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { editor, Uri } from 'monaco-editor';
import { createConfiguredEditor, createConfiguredDiffEditor, createModelReference, ITextFileEditorModel } from 'vscode/monaco';
import { IReference } from 'vscode/service-override/editor';
import { UserConfig, WrapperConfig } from './wrapper.js';
import { WrapperConfig } from './wrapper.js';
import { updateUserConfiguration as vscodeUpdateUserConfiguration } from 'vscode/service-override/configuration';
import { EditorAppConfigClassic } from './editorAppClassic.js';
import { EditorAppConfigVscodeApi } from './editorAppVscodeApi.js';
import { EditorAppConfigExtended } from './editorAppExtended.js';

export type ModelUpdate = {
languageId: string;
Expand All @@ -18,20 +18,15 @@ export type EditorAppBaseConfig = ModelUpdate & {
useDiffEditor: boolean;
domReadOnly?: boolean;
readOnly?: boolean;
awaitExtensionReadiness?: Array<() => Promise<void>>
userConfiguration?: UserConfiguration;
awaitExtensionReadiness?: Array<() => Promise<void>>;
}

export type EditorAppType = 'vscodeApi' | 'classic';

export type UserConfiguration = {
json?: string;
}
export type EditorAppType = 'extended' | 'classic';

/**
* This is the base class for both Monaco Ediotor Apps:
* - EditorAppClassic
* - EditorAppVscodeApi
* - EditorAppExtended
*
* It provides the generic functionality for both implementations.
*/
Expand All @@ -49,8 +44,7 @@ export abstract class EditorAppBase {
this.id = id;
}

protected buildConfig(userConfig: UserConfig): EditorAppBaseConfig {
const userAppConfig = userConfig.wrapperConfig.editorAppConfig;
protected buildConfig(userAppConfig: EditorAppConfigExtended | EditorAppConfigClassic): EditorAppBaseConfig {
return {
languageId: userAppConfig.languageId,
code: userAppConfig.code ?? '',
Expand All @@ -60,7 +54,6 @@ export abstract class EditorAppBase {
codeOriginalUri: userAppConfig.codeOriginalUri ?? undefined,
readOnly: userAppConfig.readOnly ?? false,
domReadOnly: userAppConfig.domReadOnly ?? false,
userConfiguration: userAppConfig.userConfiguration ?? undefined,
awaitExtensionReadiness: userAppConfig.awaitExtensionReadiness ?? undefined
};
}
Expand Down Expand Up @@ -221,9 +214,9 @@ export abstract class EditorAppBase {
return Promise.resolve();
}

async updateUserConfiguration(config?: UserConfiguration) {
if (config?.json) {
return vscodeUpdateUserConfiguration(config.json);
protected async updateUserConfiguration(json?: string) {
if (json) {
return vscodeUpdateUserConfiguration(json);
}
return Promise.resolve();
}
Expand All @@ -232,12 +225,12 @@ export abstract class EditorAppBase {
abstract init(): Promise<void>;
abstract specifyService(): editor.IEditorOverrideServices;
abstract createEditors(container: HTMLElement): Promise<void>;
abstract getConfig(): EditorAppConfigClassic | EditorAppConfigVscodeApi;
abstract getConfig(): EditorAppConfigClassic | EditorAppConfigExtended;
abstract disposeApp(): void;
}

export const isVscodeApiEditorApp = (wrapperConfig: WrapperConfig) => {
return wrapperConfig.editorAppConfig?.$type === 'vscodeApi';
export const isExtendedEditorApp = (wrapperConfig: WrapperConfig) => {
return wrapperConfig.editorAppConfig?.$type === 'extended';
};

export const isCodeUpdateRequired = (config: EditorAppBaseConfig, modelUpdate: ModelUpdate) => {
Expand All @@ -263,33 +256,33 @@ export enum ModelUpdateType {
model
}

export const isAppConfigDifferent = (orgConfig: EditorAppConfigClassic | EditorAppConfigVscodeApi,
config: EditorAppConfigClassic | EditorAppConfigVscodeApi, includeModelData: boolean, includeEditorOptions: boolean): boolean => {
export const isAppConfigDifferent = (orgConfig: EditorAppConfigClassic | EditorAppConfigExtended,
config: EditorAppConfigClassic | EditorAppConfigExtended, includeModelData: boolean, includeEditorOptions: boolean): boolean => {

let different = includeModelData ? isModelUpdateRequired(orgConfig, config) !== ModelUpdateType.none : false;
if (orgConfig.$type === config.$type) {

type ClassicKeys = keyof typeof orgConfig;
const propsClassic = ['useDiffEditor', 'readOnly', 'domReadOnly', 'awaitExtensionReadiness', 'userConfiguration', 'automaticLayout', 'languageDef', 'languageExtensionConfig', 'theme', 'themeData'];
const propsClassic = ['useDiffEditor', 'readOnly', 'domReadOnly', 'awaitExtensionReadiness', 'automaticLayout', 'languageDef', 'languageExtensionConfig', 'theme', 'themeData'];
const propsClassicEditorOptions = ['editorOptions', 'diffEditorOptions'];

const propCompareClassic = (name: string) => {
return orgConfig[name as ClassicKeys] !== config[name as ClassicKeys];
};

const propsVscode = ['useDiffEditor', 'readOnly', 'domReadOnly', 'awaitExtensionReadiness', 'userConfiguration', 'extensions'];
type VscodeApiKeys = keyof typeof orgConfig;
const propCompareVscodeApi = (name: string) => {
return orgConfig[name as VscodeApiKeys] !== config[name as VscodeApiKeys];
type ExtendedKeys = keyof typeof orgConfig;
const propCompareExtended = (name: string) => {
return orgConfig[name as ExtendedKeys] !== config[name as ExtendedKeys];
};

if (orgConfig.$type === 'classic' && config.$type === 'classic') {
different = different || propsClassic.some(propCompareClassic);
if (includeEditorOptions) {
different = different || propsClassicEditorOptions.some(propCompareClassic);
}
} else if (orgConfig.$type === 'vscodeApi' && config.$type === 'vscodeApi') {
different = different || propsVscode.some(propCompareVscodeApi);
} else if (orgConfig.$type === 'extended' && config.$type === 'extended') {
different = different || propsVscode.some(propCompareExtended);
}
} else {
throw new Error('Provided configurations are not of the same type.');
Expand Down
Loading

0 comments on commit 9c4bff5

Please sign in to comment.