Skip to content

Commit

Permalink
* line decoration atleast 1px height and not multiple on same display…
Browse files Browse the repository at this point in the history
… line

* resuse of old data fixed
* insert/delete lines fixed for tiny lineheight
* minimap disabled for 1/100 display line height
  • Loading branch information
lxsk committed Oct 28, 2019
1 parent 36eb549 commit f8306b9
Showing 1 changed file with 67 additions and 37 deletions.
104 changes: 67 additions & 37 deletions src/vs/editor/browser/viewParts/minimap/minimap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,29 +372,29 @@ class MinimapLayout {

// We must first establish a desirable slider height.
let sliderHeight: number;
let minimapLineHeight = renderLineHeight;
let displayLineHeight = renderLineHeight;
if (options.entireDocument) {
minimapLinesFitting = lineCount;
minimapLineHeight = options.canvasInnerHeight / totalLineCount;
displayLineHeight = options.canvasInnerHeight / totalLineCount;
}
if (viewportContainsWhitespaceGaps && viewportEndLineNumber !== lineCount) {
// case b) from above: there are whitespace gaps in the viewport.
// In this case, the height of the slider directly reflects the visible line count.
const viewportLineCount = viewportEndLineNumber - viewportStartLineNumber + 1;
sliderHeight = Math.floor(viewportLineCount * minimapLineHeight / pixelRatio);
sliderHeight = Math.floor(viewportLineCount * displayLineHeight / pixelRatio);
} else {
// The slider has a stable height
sliderHeight = Math.floor(expectedViewportLineCount * minimapLineHeight / pixelRatio);
sliderHeight = Math.floor(expectedViewportLineCount * displayLineHeight / pixelRatio);
}
sliderHeight = Math.min(sliderHeight, options.minimapHeight);

let maxMinimapSliderTop: number;
if (options.scrollBeyondLastLine) {
// The minimap slider, when dragged all the way down, will contain the last line at its top
maxMinimapSliderTop = (lineCount - 1) * minimapLineHeight / pixelRatio;
maxMinimapSliderTop = (lineCount - 1) * displayLineHeight / pixelRatio;
} else {
// The minimap slider, when dragged all the way down, will contain the last line at its bottom
maxMinimapSliderTop = Math.max(0, lineCount * minimapLineHeight / pixelRatio - sliderHeight);
maxMinimapSliderTop = Math.max(0, lineCount * displayLineHeight / pixelRatio - sliderHeight);
}
maxMinimapSliderTop = Math.min(options.minimapHeight - sliderHeight, maxMinimapSliderTop);

Expand All @@ -414,11 +414,11 @@ class MinimapLayout {

return new MinimapLayout(scrollTop, scrollHeight
, computedSliderRatio, sliderTop, maxMinimapSliderTop, sliderHeight
, startLineNumber, endLineNumber, minimapLineHeight
, startLineNumber, endLineNumber, displayLineHeight
, renderMinimap, renderLineHeight, emptySpaceBetweenLines, 0, fontScale
, coversAllLines, lineCount, options);
} else {
let startLineNumber = Math.max(1, Math.floor(viewportStartLineNumber - sliderTop * pixelRatio / minimapLineHeight));
let startLineNumber = Math.max(1, Math.floor(viewportStartLineNumber - sliderTop * pixelRatio / displayLineHeight));

// Avoid flickering caused by a partial viewport start line
// by being consistent w.r.t. the previous layout decision
Expand All @@ -434,11 +434,11 @@ class MinimapLayout {
}

const endLineNumber = Math.min(lineCount, startLineNumber + minimapLinesFitting - 1);
const skipLines = Math.max(0, Math.floor(1 / minimapLineHeight) - 1);
const skipLines = Math.max(0, Math.floor(1 / displayLineHeight) - 1);

return new MinimapLayout(scrollTop, scrollHeight
, computedSliderRatio, sliderTop, maxMinimapSliderTop, sliderHeight
, startLineNumber, endLineNumber, minimapLineHeight
, startLineNumber, endLineNumber, displayLineHeight
, renderMinimap, renderLineHeight, emptySpaceBetweenLines, skipLines, fontScale
, coversAllLines, lineCount, options);
}
Expand Down Expand Up @@ -471,18 +471,21 @@ class RenderData {
public readonly renderedLayout: MinimapLayout;
private readonly _imageData: ImageData;
private readonly _renderedLines: RenderedLinesCollection<MinimapLine>;
private _lineOverflow: number = 0;

constructor(
renderedLayout: MinimapLayout,
imageData: ImageData,
lines: MinimapLine[]
lines: MinimapLine[],
lineOverflow: number
) {
this.renderedLayout = renderedLayout;
this._imageData = imageData;
this._renderedLines = new RenderedLinesCollection(
() => MinimapLine.INVALID
);
this._renderedLines._set(renderedLayout.startLineNumber, lines);
this._lineOverflow = lineOverflow;
}

/**
Expand Down Expand Up @@ -513,12 +516,17 @@ class RenderData {
&& this.renderedLayout.endLineNumber === layout.endLineNumber;
}

_get(): { imageData: ImageData; rendLineNumberStart: number; lines: MinimapLine[]; } {
public getLineOverflow(): number {
return this._lineOverflow;
}

_get(): { imageData: ImageData; rendLineNumberStart: number; lines: MinimapLine[]; lineOverflow: number; } {
const tmp = this._renderedLines._get();
return {
imageData: this._imageData,
rendLineNumberStart: tmp.rendLineNumberStart,
lines: tmp.lines
lines: tmp.lines,
lineOverflow: this._lineOverflow
};
}

Expand All @@ -528,14 +536,24 @@ class RenderData {
this.renderedLayout.fromRealLineNumberToImageDataLineNumer(e.toLineNumber));
}
public onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): void {
this._renderedLines.onLinesDeleted(
this.renderedLayout.fromRealLineNumberToImageDataLineNumer(e.fromLineNumber),
this.renderedLayout.fromRealLineNumberToImageDataLineNumer(e.toLineNumber));
const count = (e.toLineNumber - e.fromLineNumber + 1) / (1 + this.renderedLayout.skipLines);
this._lineOverflow -= count;
const count2 = Math.round(this._lineOverflow);
if (count2 < 0) {
const startLine = this.renderedLayout.fromRealLineNumberToImageDataLineNumer(e.fromLineNumber);
this._renderedLines.onLinesDeleted(startLine, startLine - count2 - 1);
this._lineOverflow -= count2;
}
}
public onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): void {
this._renderedLines.onLinesInserted(
this.renderedLayout.fromRealLineNumberToImageDataLineNumer(e.fromLineNumber),
this.renderedLayout.fromRealLineNumberToImageDataLineNumer(e.toLineNumber));
const count = (e.toLineNumber - e.fromLineNumber + 1) / (1 + this.renderedLayout.skipLines);
this._lineOverflow += count;
const count2 = Math.round(this._lineOverflow);
if (count2 > 0) {
const startLine = this.renderedLayout.fromRealLineNumberToImageDataLineNumer(e.fromLineNumber);
this._renderedLines.onLinesInserted(startLine, startLine + count2 - 1);
this._lineOverflow -= count2;
}
}
public onTokensChanged(e: viewEvents.ViewTokensChangedEvent): boolean {
const ranges: { fromLineNumber: number; toLineNumber: number; }[] = [];
Expand Down Expand Up @@ -949,8 +967,15 @@ export class Minimap extends ViewPart {
renderingCtx.scrollHeight,
this._lastRenderData ? this._lastRenderData.renderedLayout : null
);
this._slider.setTop(layout.sliderTop);
this._slider.setHeight(layout.sliderHeight);
this._slider.setTop(Math.round(layout.sliderTop));
this._slider.setHeight(Math.max(1, Math.round(layout.sliderHeight)));

if (layout.displayLineHeight < 0.01) {
this._shadow.setClassName('minimap-shadow-hidden');
this._sliderHorizontal.setWidth(0);
this._sliderHorizontal.setHeight(0);
return;
}

// Compute horizontal slider coordinates
const scrollLeftChars = renderingCtx.scrollLeft / this._options.typicalHalfwidthCharacterWidth;
Expand All @@ -969,28 +994,29 @@ export class Minimap extends ViewPart {
this._renderDecorations = false;

const { canvasInnerWidth, canvasInnerHeight } = this._options;
const lineHeight = layout.displayLineHeight;
const displayLineHeight = layout.displayLineHeight;
const renderLineHeight = Math.max(1, displayLineHeight);
const characterWidth = getMinimapCharWidth(layout.renderMinimap, layout.fontScale);
const tabSize = this._context.model.getOptions().tabSize;
const canvasContext = this._decorationsCanvas.domNode.getContext('2d')!;

canvasContext.clearRect(0, 0, canvasInnerWidth, canvasInnerHeight);

let lastY = -100000000;
const lineOffsetMap = new Map<number, number[]>();
for (let i = 0; i < this._selections.length; i++) {
const selection = this._selections[i];

for (let line = selection.startLineNumber; line <= selection.endLineNumber; line++) {
this.renderDecorationOnLine(canvasContext, lineOffsetMap, selection, this._selectionColor, layout, line, lineHeight, lineHeight, tabSize, characterWidth);
const y = Math.round((line - layout.startLineNumber) * displayLineHeight);
if (y > lastY) {
lastY = y;
this.renderDecorationOnLine(canvasContext, lineOffsetMap, selection, this._selectionColor, line, renderLineHeight, y, tabSize, characterWidth);
}
}
}

if (layout.skipLines > 1) {
// we skip decorations rendering if the effective lineheight gets too small
// they are barely noticeable and can have a huge performance impact on those very large files
return;
}

lastY = -100000000;
const decorations = this._context.model.getDecorationsInViewport(new Range(layout.startLineNumber, 1, layout.endLineNumber, this._context.model.getLineMaxColumn(layout.endLineNumber)));
// Loop over decorations, ignoring those that don't have the minimap property set and rendering rectangles for each line the decoration spans
for (let i = 0; i < decorations.length; i++) {
Expand All @@ -1001,8 +1027,12 @@ export class Minimap extends ViewPart {
}

for (let line = decoration.range.startLineNumber; line <= decoration.range.endLineNumber; line++) {
const decorationColor = (<ModelDecorationMinimapOptions>decoration.options.minimap).getColor(this._context.theme);
this.renderDecorationOnLine(canvasContext, lineOffsetMap, decoration.range, decorationColor, layout, line, lineHeight, lineHeight, tabSize, characterWidth);
const y = (line - layout.startLineNumber) * displayLineHeight;
if (y > lastY) {
lastY = y;
const decorationColor = (<ModelDecorationMinimapOptions>decoration.options.minimap).getColor(this._context.theme);
this.renderDecorationOnLine(canvasContext, lineOffsetMap, decoration.range, decorationColor, line, renderLineHeight, y, tabSize, characterWidth);
}
}
}
}
Expand All @@ -1012,13 +1042,11 @@ export class Minimap extends ViewPart {
lineOffsetMap: Map<number, number[]>,
decorationRange: Range,
decorationColor: Color | undefined,
layout: MinimapLayout,
lineNumber: number,
height: number,
lineHeight: number,
y: number,
tabSize: number,
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);
Expand Down Expand Up @@ -1076,7 +1104,8 @@ export class Minimap extends ViewPart {

if (this._lastRenderData
&& ((this._lastRenderData.renderedLayout.renderLineHeight !== layout.renderLineHeight)
|| (this._lastRenderData.renderedLayout.emptySpaceBetweenLines !== layout.emptySpaceBetweenLines))) {
|| (this._lastRenderData.renderedLayout.emptySpaceBetweenLines !== layout.emptySpaceBetweenLines)
|| (this._lastRenderData.renderedLayout.skipLines !== layout.skipLines))) {
// the line layout in the imageData has changed
// we could still use the data in case only the renderLineHeight changed
// but this would require tweaking _renderUntouchedLines(...) a lot
Expand All @@ -1088,7 +1117,7 @@ export class Minimap extends ViewPart {
if (this._lastRenderData && this._lastRenderData.linesEquals(layout)) {
const _lastData = this._lastRenderData._get();
// Nice!! Nothing changed from last frame
return new RenderData(layout, _lastData.imageData, _lastData.lines);
return new RenderData(layout, _lastData.imageData, _lastData.lines, _lastData.lineOverflow);
}

// Oh well!! We need to repaint some lines...
Expand Down Expand Up @@ -1177,7 +1206,8 @@ export class Minimap extends ViewPart {
return new RenderData(
layout,
imageData,
renderedLines
renderedLines,
this._lastRenderData ? this._lastRenderData.getLineOverflow() : 0
);
}

Expand Down

0 comments on commit f8306b9

Please sign in to comment.