@theia/monaco
Version:
Theia - Monaco Extension
286 lines (233 loc) • 10.8 kB
text/typescript
// *****************************************************************************
// Copyright (C) 2024 1C-Soft LLC and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { Position, Range } from '@theia/core/shared/vscode-languageserver-protocol';
import { DisposableCollection } from '@theia/core';
import { MonacoEditor } from './monaco-editor';
import * as monaco from '@theia/monaco-editor-core';
import { PeekViewWidget, IPeekViewOptions, IPeekViewStyles } from '@theia/monaco-editor-core/esm/vs/editor/contrib/peekView/browser/peekView';
import { ICodeEditor } from '@theia/monaco-editor-core/esm/vs/editor/browser/editorBrowser';
import { ActionBar } from '@theia/monaco-editor-core/esm/vs/base/browser/ui/actionbar/actionbar';
import { Action } from '@theia/monaco-editor-core/esm/vs/base/common/actions';
import { StandaloneServices } from '@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices';
import { IInstantiationService } from '@theia/monaco-editor-core/esm/vs/platform/instantiation/common/instantiation';
import { IThemeService } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService';
import { Color } from '@theia/monaco-editor-core/esm/vs/base/common/color';
export { peekViewBorder, peekViewTitleBackground, peekViewTitleForeground, peekViewTitleInfoForeground }
from '@theia/monaco-editor-core/esm/vs/editor/contrib/peekView/browser/peekView';
export namespace MonacoEditorPeekViewWidget {
export interface Styles {
frameColor?: string;
arrowColor?: string;
headerBackgroundColor?: string;
primaryHeadingColor?: string;
secondaryHeadingColor?: string;
}
export interface Options {
showFrame?: boolean;
showArrow?: boolean;
frameWidth?: number;
className?: string;
isAccessible?: boolean;
isResizeable?: boolean;
keepEditorSelection?: boolean;
allowUnlimitedHeight?: boolean;
ordinal?: number;
showInHiddenAreas?: boolean;
supportOnTitleClick?: boolean;
}
export interface Action {
readonly id: string;
label: string;
tooltip: string;
class: string | undefined;
enabled: boolean;
checked?: boolean;
run(...args: unknown[]): unknown;
}
export interface ActionOptions {
icon?: boolean;
label?: boolean;
keybinding?: string;
index?: number;
}
}
export class MonacoEditorPeekViewWidget {
protected readonly toDispose = new DisposableCollection();
readonly onDidClose = this.toDispose.onDispose;
private readonly themeService = StandaloneServices.get(IThemeService);
private readonly delegate;
constructor(
readonly editor: MonacoEditor,
options: MonacoEditorPeekViewWidget.Options = {},
protected styles: MonacoEditorPeekViewWidget.Styles = {}
) {
const that = this;
this.toDispose.push(this.delegate = new class extends PeekViewWidget {
get actionBar(): ActionBar | undefined {
return this._actionbarWidget;
}
fillContainer(container: HTMLElement): void {
super._fillContainer(container);
}
protected override _fillContainer(container: HTMLElement): void {
that.fillContainer(container);
}
fillHead(container: HTMLElement, noCloseAction?: boolean): void {
super._fillHead(container, noCloseAction);
}
protected override _fillHead(container: HTMLElement, noCloseAction?: boolean): void {
that.fillHead(container, noCloseAction);
}
fillBody(container: HTMLElement): void {
// super._fillBody is an abstract method
}
protected override _fillBody(container: HTMLElement): void {
that.fillBody(container);
};
doLayoutHead(heightInPixel: number, widthInPixel: number): void {
super._doLayoutHead(heightInPixel, widthInPixel);
}
protected override _doLayoutHead(heightInPixel: number, widthInPixel: number): void {
that.doLayoutHead(heightInPixel, widthInPixel);
}
doLayoutBody(heightInPixel: number, widthInPixel: number): void {
super._doLayoutBody(heightInPixel, widthInPixel);
}
protected override _doLayoutBody(heightInPixel: number, widthInPixel: number): void {
that.doLayoutBody(heightInPixel, widthInPixel);
}
onWidth(widthInPixel: number): void {
super._onWidth(widthInPixel);
}
protected override _onWidth(widthInPixel: number): void {
that.onWidth(widthInPixel);
}
doRevealRange(range: monaco.Range, isLastLine: boolean): void {
super.revealRange(range, isLastLine);
}
protected override revealRange(range: monaco.Range, isLastLine: boolean): void {
that.doRevealRange(that.editor['m2p'].asRange(range), isLastLine);
}
getBodyElement(): HTMLDivElement | undefined {
return this._bodyElement;
}
setBodyElement(element: HTMLDivElement | undefined): void {
this._bodyElement = element;
}
getHeadElement(): HTMLDivElement | undefined {
return this._headElement;
}
setHeadElement(element: HTMLDivElement | undefined): void {
this._headElement = element;
}
override setCssClass(className: string, classToReplace?: string | undefined): void {
super.setCssClass(className, classToReplace);
}
}(
editor.getControl() as unknown as ICodeEditor,
Object.assign(<IPeekViewOptions>{}, options, this.convertStyles(styles)),
StandaloneServices.get(IInstantiationService)
));
this.toDispose.push(this.themeService.onDidColorThemeChange(() => this.style(this.styles)));
}
dispose(): void {
this.toDispose.dispose();
}
create(): void {
this.delegate.create();
}
setTitle(primaryHeading: string, secondaryHeading?: string): void {
this.delegate.setTitle(primaryHeading, secondaryHeading);
}
style(styles: MonacoEditorPeekViewWidget.Styles): void {
this.delegate.style(this.convertStyles(this.styles = styles));
}
show(rangeOrPos: Range | Position, heightInLines: number): void {
this.delegate.show(this.convertRangeOrPosition(rangeOrPos), heightInLines);
}
hide(): void {
this.delegate.hide();
}
clearActions(): void {
this.delegate.actionBar?.clear();
}
addAction(id: string, label: string, cssClass: string | undefined, enabled: boolean, actionCallback: (arg: unknown) => unknown,
options?: MonacoEditorPeekViewWidget.ActionOptions): MonacoEditorPeekViewWidget.Action {
options = cssClass ? { icon: true, label: false, ...options } : { icon: false, label: true, ...options };
const { actionBar } = this.delegate;
if (!actionBar) {
throw new Error('Action bar has not been created.');
}
const action = new Action(id, label, cssClass, enabled, actionCallback);
actionBar.push(action, options);
return action;
}
protected fillContainer(container: HTMLElement): void {
this.delegate.fillContainer(container);
}
protected fillHead(container: HTMLElement, noCloseAction?: boolean): void {
this.delegate.fillHead(container, noCloseAction);
}
protected fillBody(container: HTMLElement): void {
this.delegate.fillBody(container);
}
protected doLayoutHead(heightInPixel: number, widthInPixel: number): void {
this.delegate.doLayoutHead(heightInPixel, widthInPixel);
}
protected doLayoutBody(heightInPixel: number, widthInPixel: number): void {
this.delegate.doLayoutBody(heightInPixel, widthInPixel);
}
protected onWidth(widthInPixel: number): void {
this.delegate.onWidth(widthInPixel);
}
protected doRevealRange(range: Range, isLastLine: boolean): void {
this.delegate.doRevealRange(this.editor['p2m'].asRange(range), isLastLine);
}
protected get bodyElement(): HTMLDivElement | undefined {
return this.delegate.getBodyElement();
}
protected set bodyElement(element: HTMLDivElement | undefined) {
this.delegate.setBodyElement(element);
}
protected get headElement(): HTMLDivElement | undefined {
return this.delegate.getHeadElement();
}
protected set headElement(element: HTMLDivElement | undefined) {
this.delegate.setHeadElement(element);
}
protected setCssClass(className: string, classToReplace?: string | undefined): void {
this.delegate.setCssClass(className, classToReplace);
}
private convertStyles(styles: MonacoEditorPeekViewWidget.Styles): IPeekViewStyles {
return {
frameColor: this.convertColor(styles.frameColor),
arrowColor: this.convertColor(styles.arrowColor),
headerBackgroundColor: this.convertColor(styles.headerBackgroundColor),
primaryHeadingColor: this.convertColor(styles.primaryHeadingColor),
secondaryHeadingColor: this.convertColor(styles.secondaryHeadingColor),
};
}
private convertColor(color?: string): Color | undefined {
if (color === undefined) {
return undefined;
}
return this.themeService.getColorTheme().getColor(color) || Color.fromHex(color);
}
private convertRangeOrPosition(arg: Range | Position): monaco.Range | monaco.Position {
const p2m = this.editor['p2m'];
return Range.is(arg) ? p2m.asRange(arg) : p2m.asPosition(arg);
}
}