monaco-editor-comp
Version:
Lit Component for Monaco-Editor and Monaco Languageclient Wrapper
346 lines (290 loc) • 11.8 kB
text/typescript
import { css, html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { createRef, Ref, ref } from 'lit/directives/ref.js';
import { getMonacoCss } from 'monaco-editor-wrapper/monaco-css';
import { WebSocketConfigOptions, WorkerConfigOptions, MonacoEditorLanguageClientWrapper } from 'monaco-editor-wrapper';
export class MonacoEditorWebComponent extends LitElement {
private container: Ref<HTMLElement> = createRef();
private monacoWrapper = new MonacoEditorLanguageClientWrapper(this.id);
private monacoEditorOptions: Record<string, unknown> = {
readOnly: false
};
private monacoDiffEditorOptions: Record<string, unknown> = {
readOnly: false
};
private languageDef: unknown | undefined = undefined;
private themeData: unknown | undefined = undefined;
code = '';
languageId = 'javascript';
modifiedCode = '';
modifiedLanguageId = 'javascript';
theme = 'vs-light';
automaticLayout = true;
enableInlineConfig = false;
useDiffEditor = false;
useLanguageClient = false;
useWebSocket = true;
wsSecured = false;
wsHost = 'localhost';
wsPort = 8080;
wsPath = '';
workerURL = '';
useModuleWorker = false;
workerName = '';
static override styles = css`
:host {
--editor-width: 100%;
--editor-height: 100%;
}
main {
width: var(--editor-width);
height: var(--editor-height);
}
`;
override render() {
return html`
<style>${getMonacoCss()}</style>
<style>${MonacoEditorWebComponent.styles}</style>
<main ${ref(this.container)} id="monacoContainer${this.id}" class="main"></main>
`;
}
setCode(code: string): void {
this.code = code;
}
setLanguageId(languageId: string): void {
this.languageId = languageId;
}
setModifiedCode(modifiedCode: string): void {
this.modifiedCode = modifiedCode;
}
setModifiedLanguageId(modifiedLanguageId: string): void {
this.modifiedLanguageId = modifiedLanguageId;
}
setTheme(theme: string): void {
this.theme = theme;
}
setAutomaticLayout(automaticLayout: boolean): void {
this.automaticLayout = automaticLayout;
}
setUseLanguageClient(useLanguageClient: boolean) {
this.useLanguageClient = useLanguageClient;
}
setUseWebSocket(useWebSocket: boolean) {
this.useWebSocket = useWebSocket;
}
setWsSecured(wsSecured: boolean) {
this.wsSecured = wsSecured;
}
setWsHost(wsHost: string) {
this.wsHost = wsHost;
}
setWsPort(wsPort: number) {
this.wsPort = wsPort;
}
setWsPath(wsPath: string) {
this.wsPath = wsPath;
}
setWorkerURL(workerURL: string) {
this.workerURL = workerURL;
}
setUseModuleWorker(useModuleWorker: boolean) {
this.useModuleWorker = useModuleWorker;
}
setWorkerName(workerName: string) {
this.workerName = workerName;
}
setLanguageDef(languageDef: unknown) {
this.languageDef = languageDef;
}
setThemeData(themeData: unknown) {
this.themeData = themeData;
}
setUseDiffEditor(useDiffEditor: boolean) {
this.useDiffEditor = useDiffEditor;
}
startEditor(reloadInlineConfig: boolean) {
this.syncPropertiesAndEditorConfig(reloadInlineConfig);
this.monacoWrapper.startEditor(this.container.value!);
this.registerListeners();
}
updateDiffEditorContent(code: string, languageId: string, modifiedCode: string, modifiedLanguageId: string) {
this.setCode(code);
this.setLanguageId(languageId);
this.setModifiedCode(modifiedCode);
this.setModifiedLanguageId(modifiedLanguageId);
}
loadInlineConfig() {
if (this.isEnableInlineConfig()) {
this.retrieveMonacoEditorOptions();
this.retrieveMonacoDiffEditorOptions();
this.retrieveLanguageClientOptions();
}
}
updateLayout() {
this.monacoWrapper.updateLayout();
}
private syncPropertiesAndEditorConfig(reloadInlineConfig: boolean) {
if (reloadInlineConfig) {
this.loadInlineConfig();
}
const wrapperConfig = this.monacoWrapper.getEditorConfig();
wrapperConfig.setMainLanguageId(this.languageId);
wrapperConfig.setMainCode(this.code);
if (this.modifiedCode) {
wrapperConfig.setDiffCode(this.modifiedCode);
}
if (this.modifiedLanguageId) {
wrapperConfig.setDiffLanguageId(this.modifiedLanguageId);
}
wrapperConfig.setTheme(this.theme);
wrapperConfig.setAutomaticLayout(this.automaticLayout);
wrapperConfig.setMonacoEditorOptions(this.monacoEditorOptions);
wrapperConfig.setUseLanguageClient(this.useLanguageClient === true);
wrapperConfig.setUseWebSocket(this.useWebSocket === true);
let lcConfigOptions: WebSocketConfigOptions | WorkerConfigOptions;
if (wrapperConfig.isUseWebSocket()) {
lcConfigOptions = wrapperConfig.getDefaultWebSocketConfig();
lcConfigOptions.wsSecured = this.wsSecured === true;
lcConfigOptions.wsHost = this.wsHost;
lcConfigOptions.wsPort = this.wsPort;
lcConfigOptions.wsPath = this.wsPath;
}
else {
lcConfigOptions = wrapperConfig.getDefaultWorkerConfig();
if (this.workerURL) {
lcConfigOptions.workerURL = this.workerURL;
}
lcConfigOptions.workerType = this.useModuleWorker ? 'module' : 'classic';
if (this.workerName) {
lcConfigOptions.workerName = this.workerName;
}
}
wrapperConfig.setLanguageClientConfigOptions(lcConfigOptions);
wrapperConfig.setUseDiffEditor(this.useDiffEditor || false);
if (wrapperConfig.isUseDiffEditor()) {
wrapperConfig.setMonacoDiffEditorOptions(this.monacoDiffEditorOptions);
}
if (this.languageDef) {
wrapperConfig.setMonarchTokensProvider(this.languageDef);
}
if (this.themeData) {
wrapperConfig.setEditorThemeData(this.themeData);
}
}
private buildAndCallConfigFunction(basename: string, loggingName: string): Record<string, unknown> | undefined {
const winRec = window as unknown as Record<string, unknown>;
const funcName = basename;
const funcNamePlusId = `${funcName}${this.id}`;
if (Object.prototype.hasOwnProperty.call(window, funcName) && typeof winRec[funcName] === 'function') {
console.log(`Using config supplied by ${funcName} for ${loggingName}.`);
return (winRec[funcName] as () => Record<string, unknown>)();
} else if (Object.prototype.hasOwnProperty.call(winRec, funcNamePlusId) && typeof winRec[funcNamePlusId] === 'function') {
console.log(`Using config supplied by ${funcNamePlusId} for ${loggingName}.`);
return (winRec[funcNamePlusId] as () => Record<string, unknown>)();
}
else {
return undefined;
}
}
private retrieveMonacoEditorOptions() {
if (!this.isEnableInlineConfig()) return;
const options = this.buildAndCallConfigFunction('getMonacoEditorOptions', 'editor');
this.setMonacoEditorOptions(options);
}
setMonacoEditorOptions(options: Record<string, unknown> | undefined) {
if (!options) return;
if (options.code) {
this.setCode(options.code as string);
}
if (options.languageId) {
this.setLanguageId(options.languageId as string);
}
if (options.theme) {
this.setTheme(options.theme as string);
// ensure theme is removed from global config object and kept separate
options.theme = undefined;
}
for (const [k, v] of Object.entries(options)) {
this.monacoEditorOptions[k] = v;
}
}
private retrieveMonacoDiffEditorOptions() {
if (!this.isEnableInlineConfig()) return;
const options = this.buildAndCallConfigFunction('getMonacoDiffEditorOptions', 'diff editor');
this.setMonacoDiffEditorOptions(options);
}
setMonacoDiffEditorOptions(options: Record<string, unknown> | undefined) {
if (!options) return;
if (options.code) {
this.setCode(options.code as string);
}
if (options.languageId) {
this.setLanguageId(options.languageId as string);
}
if (options.modifiedCode) {
this.setModifiedCode(options.modifiedCode as string);
}
if (options.modifiedLanguageId) {
this.setModifiedLanguageId(options.modifiedLanguageId as string);
}
if (options.theme) {
this.setTheme(options.theme as string);
}
for (const [k, v] of Object.entries(options)) {
this.monacoDiffEditorOptions[k] = v;
}
}
private retrieveLanguageClientOptions() {
if (!this.isEnableInlineConfig()) return;
const options = this.buildAndCallConfigFunction('getLanguageClientOptions', 'language server connection');
this.setLanguageClientOptions(options);
}
setLanguageClientOptions(options: Record<string, unknown> | undefined) {
if (!options) return;
if (this.useWebSocket) {
const input = options as WebSocketConfigOptions;
this.setWsSecured(input.wsSecured === true);
if (input.wsHost) {
this.setWsHost(input.wsHost);
}
if (input.wsPort) {
this.setWsPort(input.wsPort);
}
if (input.wsPath) {
this.setWsPath(input.wsPath);
}
} else {
const input = options as WorkerConfigOptions;
if (input.workerURL) {
this.setWorkerURL(input.workerURL);
}
if (input.workerType) {
this.setUseModuleWorker(input.workerType === 'module');
}
if (input.workerName) {
this.setWorkerName(input.workerName);
}
}
}
private isEnableInlineConfig() {
const content = this.children.length === 1 ? this.children[0] : undefined;
return content instanceof HTMLScriptElement && this.enableInlineConfig;
}
firstUpdated() {
this.startEditor(true);
}
registerListeners() {
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', (e) => {
this.setTheme(e.matches ? 'vs-dark' : 'vs-light');
this.monacoWrapper.updateTheme();
});
}
}
declare global {
interface HTMLElementTagNameMap {
'monaco-editor-comp': MonacoEditorWebComponent;
}
}