From 4f3262dd1bba349e5be1e415a60874662da2c038 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Mon, 8 Jul 2019 15:43:04 -0700 Subject: [PATCH] Correctly render multiline decorations in the minimap, fixes #76133 --- .../browser/viewParts/minimap/minimap.ts | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 1641841c2f8..564ed6482d1 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -738,61 +738,61 @@ export class Minimap extends ViewPart { const lineHeightRatio = renderMinimap === RenderMinimap.LargeBlocks || renderMinimap === RenderMinimap.SmallBlocks ? 0.5 : 1; const height = lineHeight * lineHeightRatio; - // Loop over decorations, ignoring those that don't have the minimap property set and rendering those on the same line together - let i = 0; - for (; i < decorations.length; i++) { - if (!decorations[i].options.minimap) { + // Loop over decorations, ignoring those that don't have the minimap property set and rendering rectangles for each line the decoration spans + const lineOffsetMap = new Map(); + for (let i = 0; i < decorations.length; i++) { + const decoration = decorations[i]; + + if (!decoration.options.minimap) { continue; } - let decorationsForLine = [decorations[i]]; - let currentLine = decorations[i].range.startLineNumber; - let j = i + 1; - while (j < decorations.length && decorations[j].range.startLineNumber === currentLine) { - if (decorations[j].options.minimap) { - decorationsForLine.push(decorations[j]); - } - - j += 1; + for (let line = decoration.range.startLineNumber; line <= decoration.range.endLineNumber; line++) { + this.renderDecorationOnLine(canvasContext, lineOffsetMap, decoration, layout, line, height, lineHeight, tabSize, characterWidth); } - - i = j - 1; - this.renderDecorationsForLine(canvasContext, decorationsForLine, layout, currentLine, height, lineHeight, tabSize, characterWidth); } } } - private renderDecorationsForLine(canvasContext: CanvasRenderingContext2D, - decorations: ViewModelDecoration[], + private renderDecorationOnLine(canvasContext: CanvasRenderingContext2D, + lineOffsetMap: Map, + decoration: ViewModelDecoration, layout: MinimapLayout, - startLineNumber: number, + lineNumber: number, height: number, lineHeight: number, tabSize: number, - charWidth: number) { - const y = (startLineNumber - layout.startLineNumber) * lineHeight; - - const lineData = this._context.model.getLineContent(startLineNumber); - const lineIndexToXOffset = [0]; - for (let i = 1; i < lineData.length + 1; i++) { - const charCode = lineData.charCodeAt(i - 1); - const dx = charCode === CharCode.Tab - ? tabSize * charWidth - : strings.isFullWidthCharacter(charCode) - ? 2 * charWidth - : charWidth; - - lineIndexToXOffset[i] = lineIndexToXOffset[i - 1] + dx; + charWidth: number): void { + const y = (lineNumber - layout.startLineNumber) * lineHeight; + + // Cache line offset data so that it is only read once per line + let lineIndexToXOffset = lineOffsetMap.get(lineNumber); + if (!lineIndexToXOffset) { + const lineData = this._context.model.getLineContent(lineNumber); + lineIndexToXOffset = [0]; + for (let i = 1; i < lineData.length + 1; i++) { + const charCode = lineData.charCodeAt(i - 1); + const dx = charCode === CharCode.Tab + ? tabSize * charWidth + : strings.isFullWidthCharacter(charCode) + ? 2 * charWidth + : charWidth; + + lineIndexToXOffset[i] = lineIndexToXOffset[i - 1] + dx; + } + + lineOffsetMap.set(lineNumber, lineIndexToXOffset); } - for (let i = 0; i < decorations.length; i++) { - const currentDecoration = decorations[i]; - const { startColumn, endColumn } = currentDecoration.range; - const x = lineIndexToXOffset[startColumn - 1]; - const width = lineIndexToXOffset[endColumn - 1] - x; + const { startColumn, endColumn, startLineNumber, endLineNumber } = decoration.range; + const x = startLineNumber === lineNumber ? lineIndexToXOffset[startColumn - 1] : 0; - this.renderDecoration(canvasContext, currentDecoration.options.minimap, x, y, width, height); - } + const endColumnForLine = endLineNumber > lineNumber ? lineIndexToXOffset.length - 1 : endColumn - 1; + + // If the decoration starts at the last character of the column and spans over it, ensure it has a width + const width = lineIndexToXOffset[endColumnForLine] - x || 2; + + this.renderDecoration(canvasContext, decoration.options.minimap, x, y, width, height); } private renderDecoration(canvasContext: CanvasRenderingContext2D, minimapOptions: ModelDecorationMinimapOptions, x: number, y: number, width: number, height: number) { -- GitLab