Path: blob/main/src/vs/editor/browser/view/viewOverlays.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { FastDomNode, createFastDomNode } from '../../../base/browser/fastDomNode.js';6import { applyFontInfo } from '../config/domFontInfo.js';7import { DynamicViewOverlay } from './dynamicViewOverlay.js';8import { IVisibleLine, VisibleLinesCollection } from './viewLayer.js';9import { ViewPart } from './viewPart.js';10import { StringBuilder } from '../../common/core/stringBuilder.js';11import { RenderingContext, RestrictedRenderingContext } from './renderingContext.js';12import { ViewContext } from '../../common/viewModel/viewContext.js';13import * as viewEvents from '../../common/viewEvents.js';14import { ViewportData } from '../../common/viewLayout/viewLinesViewportData.js';15import { EditorOption } from '../../common/config/editorOptions.js';1617export class ViewOverlays extends ViewPart {18private readonly _visibleLines: VisibleLinesCollection<ViewOverlayLine>;19protected readonly domNode: FastDomNode<HTMLElement>;20private _dynamicOverlays: DynamicViewOverlay[] = [];21private _isFocused: boolean = false;2223constructor(context: ViewContext) {24super(context);2526this._visibleLines = new VisibleLinesCollection(this._context, {27createLine: () => new ViewOverlayLine(this._dynamicOverlays)28});29this.domNode = this._visibleLines.domNode;3031const options = this._context.configuration.options;32const fontInfo = options.get(EditorOption.fontInfo);33applyFontInfo(this.domNode, fontInfo);3435this.domNode.setClassName('view-overlays');36}3738public override shouldRender(): boolean {39if (super.shouldRender()) {40return true;41}4243for (let i = 0, len = this._dynamicOverlays.length; i < len; i++) {44const dynamicOverlay = this._dynamicOverlays[i];45if (dynamicOverlay.shouldRender()) {46return true;47}48}4950return false;51}5253public override dispose(): void {54super.dispose();5556for (let i = 0, len = this._dynamicOverlays.length; i < len; i++) {57const dynamicOverlay = this._dynamicOverlays[i];58dynamicOverlay.dispose();59}60this._dynamicOverlays = [];61}6263public getDomNode(): FastDomNode<HTMLElement> {64return this.domNode;65}6667public addDynamicOverlay(overlay: DynamicViewOverlay): void {68this._dynamicOverlays.push(overlay);69}7071// ----- event handlers7273public override onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {74this._visibleLines.onConfigurationChanged(e);7576const options = this._context.configuration.options;77const fontInfo = options.get(EditorOption.fontInfo);78applyFontInfo(this.domNode, fontInfo);7980return true;81}82public override onFlushed(e: viewEvents.ViewFlushedEvent): boolean {83return this._visibleLines.onFlushed(e);84}85public override onFocusChanged(e: viewEvents.ViewFocusChangedEvent): boolean {86this._isFocused = e.isFocused;87return true;88}89public override onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean {90return this._visibleLines.onLinesChanged(e);91}92public override onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean {93return this._visibleLines.onLinesDeleted(e);94}95public override onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean {96return this._visibleLines.onLinesInserted(e);97}98public override onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean {99return this._visibleLines.onScrollChanged(e) || true;100}101public override onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean {102return this._visibleLines.onTokensChanged(e);103}104public override onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean {105return this._visibleLines.onZonesChanged(e);106}107108// ----- end event handlers109110public prepareRender(ctx: RenderingContext): void {111const toRender = this._dynamicOverlays.filter(overlay => overlay.shouldRender());112113for (let i = 0, len = toRender.length; i < len; i++) {114const dynamicOverlay = toRender[i];115dynamicOverlay.prepareRender(ctx);116dynamicOverlay.onDidRender();117}118}119120public render(ctx: RestrictedRenderingContext): void {121// Overwriting to bypass `shouldRender` flag122this._viewOverlaysRender(ctx);123124this.domNode.toggleClassName('focused', this._isFocused);125}126127_viewOverlaysRender(ctx: RestrictedRenderingContext): void {128this._visibleLines.renderLines(ctx.viewportData);129}130}131132export class ViewOverlayLine implements IVisibleLine {133134private readonly _dynamicOverlays: DynamicViewOverlay[];135private _domNode: FastDomNode<HTMLElement> | null;136private _renderedContent: string | null;137138constructor(dynamicOverlays: DynamicViewOverlay[]) {139this._dynamicOverlays = dynamicOverlays;140141this._domNode = null;142this._renderedContent = null;143}144145public getDomNode(): HTMLElement | null {146if (!this._domNode) {147return null;148}149return this._domNode.domNode;150}151public setDomNode(domNode: HTMLElement): void {152this._domNode = createFastDomNode(domNode);153}154155public onContentChanged(): void {156// Nothing157}158public onTokensChanged(): void {159// Nothing160}161162public renderLine(lineNumber: number, deltaTop: number, lineHeight: number, viewportData: ViewportData, sb: StringBuilder): boolean {163let result = '';164for (let i = 0, len = this._dynamicOverlays.length; i < len; i++) {165const dynamicOverlay = this._dynamicOverlays[i];166result += dynamicOverlay.render(viewportData.startLineNumber, lineNumber);167}168169if (this._renderedContent === result) {170// No rendering needed171return false;172}173174this._renderedContent = result;175176sb.appendString('<div style="top:');177sb.appendString(String(deltaTop));178sb.appendString('px;height:');179sb.appendString(String(lineHeight));180sb.appendString('px;line-height:');181sb.appendString(String(lineHeight));182sb.appendString('px;">');183sb.appendString(result);184sb.appendString('</div>');185186return true;187}188189public layoutLine(lineNumber: number, deltaTop: number, lineHeight: number): void {190if (this._domNode) {191this._domNode.setTop(deltaTop);192this._domNode.setHeight(lineHeight);193this._domNode.setLineHeight(lineHeight);194}195}196}197198export class ContentViewOverlays extends ViewOverlays {199200private _contentWidth: number;201202constructor(context: ViewContext) {203super(context);204const options = this._context.configuration.options;205const layoutInfo = options.get(EditorOption.layoutInfo);206this._contentWidth = layoutInfo.contentWidth;207208this.domNode.setHeight(0);209}210211// --- begin event handlers212213public override onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {214const options = this._context.configuration.options;215const layoutInfo = options.get(EditorOption.layoutInfo);216this._contentWidth = layoutInfo.contentWidth;217return super.onConfigurationChanged(e) || true;218}219public override onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean {220return super.onScrollChanged(e) || e.scrollWidthChanged;221}222223// --- end event handlers224225override _viewOverlaysRender(ctx: RestrictedRenderingContext): void {226super._viewOverlaysRender(ctx);227228this.domNode.setWidth(Math.max(ctx.scrollWidth, this._contentWidth));229}230}231232export class MarginViewOverlays extends ViewOverlays {233234private _contentLeft: number;235236constructor(context: ViewContext) {237super(context);238239const options = this._context.configuration.options;240const layoutInfo = options.get(EditorOption.layoutInfo);241this._contentLeft = layoutInfo.contentLeft;242243this.domNode.setClassName('margin-view-overlays');244this.domNode.setWidth(1);245246applyFontInfo(this.domNode, options.get(EditorOption.fontInfo));247}248249public override onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean {250const options = this._context.configuration.options;251applyFontInfo(this.domNode, options.get(EditorOption.fontInfo));252const layoutInfo = options.get(EditorOption.layoutInfo);253this._contentLeft = layoutInfo.contentLeft;254return super.onConfigurationChanged(e) || true;255}256257public override onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean {258return super.onScrollChanged(e) || e.scrollHeightChanged;259}260261override _viewOverlaysRender(ctx: RestrictedRenderingContext): void {262super._viewOverlaysRender(ctx);263const height = Math.min(ctx.scrollHeight, 1000000);264this.domNode.setHeight(height);265this.domNode.setWidth(this._contentLeft);266}267}268269270