Path: blob/main/src/vs/workbench/contrib/notebook/browser/viewParts/notebookOverviewRuler.ts
3296 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 { getWindow } from '../../../../../base/browser/dom.js';6import { createFastDomNode, FastDomNode } from '../../../../../base/browser/fastDomNode.js';7import { PixelRatio } from '../../../../../base/browser/pixelRatio.js';8import { IThemeService, Themable } from '../../../../../platform/theme/common/themeService.js';9import { INotebookEditorDelegate, NotebookOverviewRulerLane } from '../notebookBrowser.js';1011export class NotebookOverviewRuler extends Themable {12private readonly _domNode: FastDomNode<HTMLCanvasElement>;13private _lanes = 3;1415constructor(readonly notebookEditor: INotebookEditorDelegate, container: HTMLElement, @IThemeService themeService: IThemeService) {16super(themeService);17this._domNode = createFastDomNode(document.createElement('canvas'));18this._domNode.setPosition('relative');19this._domNode.setLayerHinting(true);20this._domNode.setContain('strict');2122container.appendChild(this._domNode.domNode);2324this._register(notebookEditor.onDidChangeDecorations(() => {25this.layout();26}));2728this._register(PixelRatio.getInstance(getWindow(this._domNode.domNode)).onDidChange(() => {29this.layout();30}));31}3233layout() {34const width = 10;35const layoutInfo = this.notebookEditor.getLayoutInfo();36const scrollHeight = layoutInfo.scrollHeight;37const height = layoutInfo.height;38const ratio = PixelRatio.getInstance(getWindow(this._domNode.domNode)).value;39this._domNode.setWidth(width);40this._domNode.setHeight(height);41this._domNode.domNode.width = width * ratio;42this._domNode.domNode.height = height * ratio;43const ctx = this._domNode.domNode.getContext('2d')!;44ctx.clearRect(0, 0, width * ratio, height * ratio);45this._render(ctx, width * ratio, height * ratio, scrollHeight * ratio, ratio);46}4748private _render(ctx: CanvasRenderingContext2D, width: number, height: number, scrollHeight: number, ratio: number) {49const viewModel = this.notebookEditor.getViewModel();50const fontInfo = this.notebookEditor.getLayoutInfo().fontInfo;51const laneWidth = width / this._lanes;5253let currentFrom = 0;5455if (viewModel) {56for (let i = 0; i < viewModel.viewCells.length; i++) {57const viewCell = viewModel.viewCells[i];58const textBuffer = viewCell.textBuffer;59const decorations = viewCell.getCellDecorations();60const cellHeight = (viewCell.layoutInfo.totalHeight / scrollHeight) * ratio * height;6162decorations.filter(decoration => decoration.overviewRuler).forEach(decoration => {63const overviewRuler = decoration.overviewRuler!;64const fillStyle = this.getColor(overviewRuler.color) ?? '#000000';65const lineHeight = Math.min(fontInfo.lineHeight, (viewCell.layoutInfo.editorHeight / scrollHeight / textBuffer.getLineCount()) * ratio * height);66const lineNumbers = overviewRuler.modelRanges.map(range => range.startLineNumber).reduce((previous: number[], current: number) => {67if (previous.length === 0) {68previous.push(current);69} else {70const last = previous[previous.length - 1];71if (last !== current) {72previous.push(current);73}74}7576return previous;77}, [] as number[]);7879let x = 0;80switch (overviewRuler.position) {81case NotebookOverviewRulerLane.Left:82x = 0;83break;84case NotebookOverviewRulerLane.Center:85x = laneWidth;86break;87case NotebookOverviewRulerLane.Right:88x = laneWidth * 2;89break;90default:91break;92}9394const width = overviewRuler.position === NotebookOverviewRulerLane.Full ? laneWidth * 3 : laneWidth;9596for (let i = 0; i < lineNumbers.length; i++) {97ctx.fillStyle = fillStyle;98const lineNumber = lineNumbers[i];99const offset = (lineNumber - 1) * lineHeight;100ctx.fillRect(x, currentFrom + offset, width, lineHeight);101}102103if (overviewRuler.includeOutput) {104ctx.fillStyle = fillStyle;105const outputOffset = (viewCell.layoutInfo.editorHeight / scrollHeight) * ratio * height;106const decorationHeight = (fontInfo.lineHeight / scrollHeight) * ratio * height;107ctx.fillRect(laneWidth, currentFrom + outputOffset, laneWidth, decorationHeight);108}109});110111currentFrom += cellHeight;112}113114const overviewRulerDecorations = viewModel.getOverviewRulerDecorations();115116for (let i = 0; i < overviewRulerDecorations.length; i++) {117const decoration = overviewRulerDecorations[i];118if (!decoration.options.overviewRuler) {119continue;120}121const viewZoneInfo = this.notebookEditor.getViewZoneLayoutInfo(decoration.viewZoneId);122123if (!viewZoneInfo) {124continue;125}126127const fillStyle = this.getColor(decoration.options.overviewRuler.color) ?? '#000000';128let x = 0;129switch (decoration.options.overviewRuler.position) {130case NotebookOverviewRulerLane.Left:131x = 0;132break;133case NotebookOverviewRulerLane.Center:134x = laneWidth;135break;136case NotebookOverviewRulerLane.Right:137x = laneWidth * 2;138break;139default:140break;141}142143const width = decoration.options.overviewRuler.position === NotebookOverviewRulerLane.Full ? laneWidth * 3 : laneWidth;144145ctx.fillStyle = fillStyle;146147const viewZoneHeight = (viewZoneInfo.height / scrollHeight) * ratio * height;148const viewZoneTop = (viewZoneInfo.top / scrollHeight) * ratio * height;149150ctx.fillRect(x, viewZoneTop, width, viewZoneHeight);151}152}153}154}155156157