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

Commit

Permalink
Moved languageclient related things to own class, implemented review …
Browse files Browse the repository at this point in the history
…comments, added first languageclient related tests
  • Loading branch information
kaisalmen committed Aug 11, 2023
1 parent cfd1014 commit c7a5cdf
Show file tree
Hide file tree
Showing 13 changed files with 467 additions and 321 deletions.
2 changes: 1 addition & 1 deletion packages/examples/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const startEditor = async (userConfig: UserConfig, code: string, codeOrig
};

export const updateModel = async (modelUpdate: ModelUpdate) => {
if (wrapper.getMonacoEditorApp()?.getAppConfig().useDiffEditor) {
if (wrapper.getMonacoEditorApp()?.getConfig().useDiffEditor) {
await wrapper?.updateDiffModel(modelUpdate);
} else {
await wrapper?.updateModel(modelUpdate);
Expand Down
4 changes: 2 additions & 2 deletions packages/examples/src/wrapperTs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ try {
swapEditors(userConfig, code, codeOriginal);
});
document.querySelector('#button-swap-code')?.addEventListener('click', () => {
if (wrapper.getMonacoEditorApp()?.getAppConfig().codeUri === codeUri) {
if (wrapper.getMonacoEditorApp()?.getConfig().codeUri === codeUri) {
updateModel({
code: codeOriginal,
uri: codeOriginalUri,
Expand All @@ -73,7 +73,7 @@ try {
}
});
document.querySelector('#button-dispose')?.addEventListener('click', async () => {
if (wrapper.getMonacoEditorApp()?.getAppConfig().codeUri === codeUri) {
if (wrapper.getMonacoEditorApp()?.getConfig().codeUri === codeUri) {
code = await disposeEditor(userConfig);
} else {
codeOriginal = await disposeEditor(userConfig);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import { editor, Uri } from 'monaco-editor/esm/vs/editor/editor.api.js';
import { createConfiguredEditor, createConfiguredDiffEditor, createModelReference, ITextFileEditorModel } from 'vscode/monaco';
import { IReference } from 'vscode/service-override/editor';
import { ModelUpdate, UserConfig, WrapperConfig } from './wrapper.js';
import { EditorAppConfigClassic } from './editorAppClassic.js';
import { EditorAppConfigVscodeApi } from './editorAppVscodeApi.js';

export type VscodeUserConfiguration = {
json?: string;
}

export type EditorAppConfig = {
export type EditorAppBaseConfig = {
languageId: string;
code: string;
codeUri?: string;
Expand All @@ -20,26 +22,30 @@ export type EditorAppConfig = {

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

/**
* This is the base class for both Monaco Ediotor Apps:
* - EditorAppClassic
* - EditorAppVscodeApi
*
* It provides the generic functionality for both implementations.
*/
export abstract class EditorAppBase {

private id: string;
protected appConfig: EditorAppConfig;

protected editor: editor.IStandaloneCodeEditor | undefined;
protected diffEditor: editor.IStandaloneDiffEditor | undefined;

protected editorOptions: editor.IStandaloneEditorConstructionOptions;
protected diffEditorOptions: editor.IStandaloneDiffEditorConstructionOptions;
private editor: editor.IStandaloneCodeEditor | undefined;
private diffEditor: editor.IStandaloneDiffEditor | undefined;

private modelRef: IReference<ITextFileEditorModel> | undefined;
private modelOriginalRef: IReference<ITextFileEditorModel> | undefined;

constructor(id: string, userConfig: UserConfig) {
constructor(id: string) {
this.id = id;
console.log(`Starting monaco-editor (${this.id})`);
}

buildConfig(userConfig: UserConfig) {
const userAppConfig = userConfig.wrapperConfig.editorAppConfig;
this.appConfig = {
return {
languageId: userAppConfig.languageId,
code: userAppConfig.code ?? '',
codeOriginal: userAppConfig.codeOriginal ?? '',
Expand All @@ -61,14 +67,14 @@ export abstract class EditorAppBase {
return this.diffEditor;
}

async createEditors(container: HTMLElement): Promise<void> {
if (this.appConfig.useDiffEditor) {
this.diffEditor = createConfiguredDiffEditor(container!, this.diffEditorOptions);
await this.updateDiffEditorModel();
} else {
this.editor = createConfiguredEditor(container!, this.editorOptions);
await this.updateEditorModel();
}
protected async createEditor(container: HTMLElement, editorOptions?: editor.IStandaloneEditorConstructionOptions): Promise<void> {
this.editor = createConfiguredEditor(container!, editorOptions);
await this.updateEditorModel();
}

protected async createDiffEditor(container: HTMLElement, diffEditorOptions?: editor.IStandaloneDiffEditorConstructionOptions): Promise<void> {
this.diffEditor = createConfiguredDiffEditor(container!, diffEditorOptions);
await this.updateDiffEditorModel();
}

disposeEditor() {
Expand All @@ -90,7 +96,7 @@ export abstract class EditorAppBase {
}

getModel(original?: boolean): editor.ITextModel | undefined {
if (this.appConfig.useDiffEditor) {
if (this.getConfig().useDiffEditor) {
return ((original === true) ? this.modelOriginalRef?.object.textEditorModel : this.modelRef?.object.textEditorModel) ?? undefined;
} else {
return this.modelRef?.object.textEditorModel ?? undefined;
Expand All @@ -102,16 +108,17 @@ export abstract class EditorAppBase {
return Promise.reject(new Error('You cannot update the editor model, because the regular editor is not configured.'));
}

this.updateEditorConfig(modelUpdate);
this.updateAppConfig(modelUpdate);
await this.updateEditorModel();
}

private async updateEditorModel(): Promise<void> {
const config = this.getConfig();
this.modelRef?.dispose();

const uri: Uri = this.getEditorUri('code');
this.modelRef = await createModelReference(uri, this.appConfig.code) as unknown as IReference<ITextFileEditorModel>;
this.modelRef.object.setLanguageId(this.appConfig.languageId);
this.modelRef = await createModelReference(uri, config.code) as unknown as IReference<ITextFileEditorModel>;
this.modelRef.object.setLanguageId(config.languageId);
if (this.editor) {
this.editor.setModel(this.modelRef.object.textEditorModel);
}
Expand All @@ -122,26 +129,27 @@ export abstract class EditorAppBase {
return Promise.reject(new Error('You cannot update the diff editor models, because the diffEditor is not configured.'));
}

this.updateEditorConfig(modelUpdate);
this.updateAppConfig(modelUpdate);
return this.updateDiffEditorModel();
}

private async updateDiffEditorModel(): Promise<void> {
const config = this.getConfig();
this.modelRef?.dispose();
this.modelOriginalRef?.dispose();

const uri: Uri = this.getEditorUri('code');
const uriOriginal: Uri = this.getEditorUri('codeOriginal');

const promises = [];
promises.push(createModelReference(uri, this.appConfig.code));
promises.push(createModelReference(uriOriginal, this.appConfig.codeOriginal));
promises.push(createModelReference(uri, config.code));
promises.push(createModelReference(uriOriginal, config.codeOriginal));

const refs = await Promise.all(promises);
this.modelRef = refs[0] as unknown as IReference<ITextFileEditorModel>;
this.modelRef.object.setLanguageId(this.appConfig.languageId);
this.modelRef.object.setLanguageId(config.languageId);
this.modelOriginalRef = refs[1] as unknown as IReference<ITextFileEditorModel>;
this.modelOriginalRef.object.setLanguageId(this.appConfig.languageId);
this.modelOriginalRef.object.setLanguageId(config.languageId);

if (this.diffEditor && this.modelRef.object.textEditorModel !== null && this.modelOriginalRef.object.textEditorModel !== null) {
this.diffEditor?.setModel({
Expand All @@ -151,49 +159,56 @@ export abstract class EditorAppBase {
}
}

private updateEditorConfig(modelUpdate: ModelUpdate) {
private updateAppConfig(modelUpdate: ModelUpdate) {
const config = this.getConfig();
if (modelUpdate.code !== undefined) {
this.appConfig.code = modelUpdate.code;
config.code = modelUpdate.code;
}

if (modelUpdate.languageId !== undefined) {
this.appConfig.languageId = modelUpdate.languageId;
config.languageId = modelUpdate.languageId;
}

if (modelUpdate.uri !== undefined) {
this.appConfig.codeUri = modelUpdate.uri;
config.codeUri = modelUpdate.uri;
}

if (modelUpdate.codeOriginal !== undefined) {
this.appConfig.codeOriginal = modelUpdate.codeOriginal;
config.codeOriginal = modelUpdate.codeOriginal;
}

if (modelUpdate.codeOriginalUri !== undefined) {
this.appConfig.codeOriginalUri = modelUpdate.codeOriginalUri;
config.codeOriginalUri = modelUpdate.codeOriginalUri;
}
}

getEditorUri(uriType: 'code' | 'codeOriginal') {
const uri = uriType === 'code' ? this.appConfig.codeUri : this.appConfig.codeOriginalUri;
const config = this.getConfig();
const uri = uriType === 'code' ? config.codeUri : config.codeOriginalUri;
if (uri) {
return Uri.parse(uri);
} else {
return Uri.parse(`/tmp/model${uriType === 'codeOriginal' ? 'Original' : ''}${this.id}.${this.appConfig.languageId}`);
return Uri.parse(`/tmp/model${uriType === 'codeOriginal' ? 'Original' : ''}${this.id}.${config.languageId}`);
}
}

updateLayout() {
if (this.appConfig.useDiffEditor) {
if (this.getConfig().useDiffEditor) {
this.diffEditor?.layout();
} else {
this.editor?.layout();
}
}

updateMonacoEditorOptions(options: editor.IEditorOptions & editor.IGlobalEditorOptions) {
this.editor?.updateOptions(options);
}

abstract getAppType(): string;
abstract init(): Promise<void>;
abstract updateConfig(options: editor.IEditorOptions & editor.IGlobalEditorOptions | VscodeUserConfiguration): void;
abstract getAppConfig(): EditorAppConfig;
abstract createEditors(container: HTMLElement): Promise<void>;
abstract updateEditorOptions(options: editor.IEditorOptions & editor.IGlobalEditorOptions | VscodeUserConfiguration): void;
abstract getConfig(): EditorAppConfigClassic | EditorAppConfigVscodeApi;
}

export const isVscodeApiEditorApp = (wrapperConfig: WrapperConfig) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EditorAppBase, EditorAppConfig, EditorAppType } from './editor.js';
import { EditorAppBase, EditorAppBaseConfig, EditorAppType } from './editorAppBase.js';
import { editor, languages } from 'monaco-editor/esm/vs/editor/editor.api.js';
import { UserConfig } from './wrapper.js';
/**
Expand All @@ -15,7 +15,7 @@ export type MonacoLanguageExtensionConfig = {
mimetypes?: string[];
}

export type EditorAppConfigClassic = EditorAppConfig & {
export type EditorAppConfigClassic = EditorAppBaseConfig & {
editorAppType: 'classic';
automaticLayout?: boolean;
theme?: string;
Expand All @@ -26,66 +26,83 @@ export type EditorAppConfigClassic = EditorAppConfig & {
themeData?: editor.IStandaloneThemeData;
};

/**
* The classic monaco-editor app uses the classic monaco-editor configuration.
*/
export class EditorAppClassic extends EditorAppBase {

private editorOptions: editor.IStandaloneEditorConstructionOptions;
private diffEditorOptions: editor.IStandaloneDiffEditorConstructionOptions;

private config: EditorAppConfigClassic;

constructor(id: string, userConfig: UserConfig) {
super(id, userConfig);
super(id);
this.config = this.buildConfig(userConfig) as EditorAppConfigClassic;
const userInput = userConfig.wrapperConfig.editorAppConfig as EditorAppConfigClassic;
// default to vs-light
this.getAppConfig().theme = userInput.theme ?? 'vs-light';
this.config.theme = userInput.theme ?? 'vs-light';
// default to true
this.getAppConfig().automaticLayout = userInput.automaticLayout ?? true;
this.config.automaticLayout = userInput.automaticLayout ?? true;

this.editorOptions = userInput.editorOptions ?? {};
this.editorOptions.automaticLayout = userInput.automaticLayout ?? true;

this.diffEditorOptions = userInput.diffEditorOptions ?? {};
this.diffEditorOptions.automaticLayout = userInput.automaticLayout ?? true;

this.getAppConfig().languageExtensionConfig = userInput.languageExtensionConfig ?? undefined;
this.getAppConfig().languageDef = userInput.languageDef ?? undefined;
this.getAppConfig().themeData = userInput.themeData ?? undefined;
this.config.languageExtensionConfig = userInput.languageExtensionConfig ?? undefined;
this.config.languageDef = userInput.languageDef ?? undefined;
this.config.themeData = userInput.themeData ?? undefined;
}

getAppType(): EditorAppType {
return 'classic';
}

getAppConfig(): EditorAppConfigClassic {
return this.appConfig as EditorAppConfigClassic;
getConfig(): EditorAppConfigClassic {
return this.config;
}

async createEditors(container: HTMLElement): Promise<void> {
if (this.config.useDiffEditor) {
await this.createDiffEditor(container, this.diffEditorOptions);
} else {
await this.createEditor(container, this.editorOptions);
}
}

async init() {
// register own language first
const extLang = this.getAppConfig().languageExtensionConfig;
const extLang = this.config.languageExtensionConfig;
if (extLang) {
languages.register(extLang);
}

const languageRegistered = languages.getLanguages().filter(x => x.id === this.appConfig.languageId);
const languageRegistered = languages.getLanguages().filter(x => x.id === this.config.languageId);
if (languageRegistered.length === 0) {
// this is only meaningful for languages supported by monaco out of the box
languages.register({
id: this.appConfig.languageId
id: this.config.languageId
});
}

// apply monarch definitions
const tokenProvider = this.getAppConfig().languageDef;
const tokenProvider = this.config.languageDef;
if (tokenProvider) {
languages.setMonarchTokensProvider(this.appConfig.languageId, tokenProvider);
languages.setMonarchTokensProvider(this.config.languageId, tokenProvider);
}
const themeData = this.getAppConfig().themeData;
const themeData = this.config.themeData;
if (themeData) {
editor.defineTheme(this.getAppConfig().theme!, themeData);
editor.defineTheme(this.config.theme!, themeData);
}
editor.setTheme(this.getAppConfig().theme!);
editor.setTheme(this.config.theme!);

console.log('Init of MonacoConfig was completed.');
return Promise.resolve();
}

async updateConfig(options: editor.IEditorOptions & editor.IGlobalEditorOptions) {
this.editor?.updateOptions(options);
async updateEditorOptions(options: editor.IEditorOptions & editor.IGlobalEditorOptions) {
this.updateMonacoEditorOptions(options);
}
}
Loading

0 comments on commit c7a5cdf

Please sign in to comment.