Skip to content

Commit 429fee5

Browse files
committed
Recreate sampling incrementally
1 parent b4ca4e4 commit 429fee5

3 files changed

Lines changed: 156 additions & 26 deletions

File tree

src/vs/editor/browser/viewParts/minimap/minimap.ts

Lines changed: 150 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ class MinimapLayout {
234234
scrollHeight: number,
235235
previousLayout: MinimapLayout | null
236236
): MinimapLayout {
237-
// console.log(`create - ${viewportStartLineNumber}, ${viewportEndLineNumber}, ${viewportHeight}, ${viewportContainsWhitespaceGaps}, ${lineCount}, ${scrollTop}, ${scrollHeight}`);
238237
const pixelRatio = options.pixelRatio;
239238
const minimapLineHeight = options.minimapLineHeight;
240239
const minimapLinesFitting = Math.floor(options.canvasInnerHeight / minimapLineHeight);
@@ -502,9 +501,10 @@ export class Minimap extends ViewPart implements IMinimapModel {
502501
private _selections: Selection[];
503502
private _minimapSelections: Selection[];
504503

505-
private _samplingRatio!: number;
506-
private _minimapLines!: number[];
507-
private _isSampling = false;//true;
504+
private _samplingRatio: number;
505+
private _minimapLines: number[];
506+
private _isSampling = false;
507+
private _shouldCheckSampling: boolean;
508508

509509
public readonly tokensColorTracker: MinimapTokensColorTracker;
510510
public options: MinimapOptions;
@@ -517,7 +517,10 @@ export class Minimap extends ViewPart implements IMinimapModel {
517517
this._selections = [];
518518
this._minimapSelections = [];
519519

520-
this._recreateLineMapping();
520+
this._samplingRatio = 1;
521+
this._minimapLines = [];
522+
this._shouldCheckSampling = false;
523+
this._recreateLineSampling(null);
521524

522525
this.tokensColorTracker = MinimapTokensColorTracker.getInstance();
523526
this.options = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker, this._isSampling);
@@ -594,7 +597,8 @@ export class Minimap extends ViewPart implements IMinimapModel {
594597
if (changeStartIndex <= changeEndIndex) {
595598
this._actual.onLinesChanged(changeStartIndex + 1, changeEndIndex + 1);
596599
}
597-
return this._checkMapping();
600+
this._shouldCheckSampling = true;
601+
return true;
598602
} else {
599603
return this._actual.onLinesDeleted(e.fromLineNumber, e.toLineNumber);
600604
}
@@ -609,7 +613,8 @@ export class Minimap extends ViewPart implements IMinimapModel {
609613
}
610614
this._minimapLines[i] += insertedLineCount;
611615
}
612-
return this._checkMapping();
616+
this._shouldCheckSampling = true;
617+
return true;
613618
} else {
614619
return this._actual.onLinesInserted(e.fromLineNumber, e.toLineNumber);
615620
}
@@ -651,7 +656,10 @@ export class Minimap extends ViewPart implements IMinimapModel {
651656
// --- end event handlers
652657

653658
public prepareRender(ctx: RenderingContext): void {
654-
// nothing to read
659+
if (this._shouldCheckSampling) {
660+
this._shouldCheckSampling = false;
661+
this._recreateLineSampling(this._minimapLines);
662+
}
655663
}
656664

657665
public render(ctx: RestrictedRenderingContext): void {
@@ -684,12 +692,23 @@ export class Minimap extends ViewPart implements IMinimapModel {
684692

685693
//#region IMinimapModel
686694

687-
private _checkMapping(): boolean {
688-
// console.log(`TODO: I SHOULD ADJUST THE MAPPING!!!`, this._minimapLines);
689-
return false;
695+
private _createLineSampling(minimapLineCount: number, modelLineCount: number, ratio: number): number[] {
696+
let result: number[] = [];
697+
result[0] = 1;
698+
if (minimapLineCount > 1) {
699+
const halfRatio = ratio / 2;
700+
for (let i = 0, lastIndex = minimapLineCount - 1; i < lastIndex; i++) {
701+
result[i] = Math.round(i * ratio + halfRatio);
702+
}
703+
result[minimapLineCount - 1] = modelLineCount;
704+
}
705+
return result;
690706
}
691707

692-
private _recreateLineMapping(): void {
708+
private _recreateLineSampling(oldMinimapLines: number[] | null): void {
709+
// generate at most 10 events, if there are more than 10 changes, just flush all previous data
710+
const MAX_EVENT_COUNT = 10;
711+
693712
const options = this._context.configuration.options;
694713
const layoutInfo = options.get(EditorOption.layoutInfo);
695714
const pixelRatio = options.get(EditorOption.pixelRatio);
@@ -702,10 +721,113 @@ export class Minimap extends ViewPart implements IMinimapModel {
702721
const minimapLineCount = Math.floor(modelLineCount / desiredRatio);
703722
const ratio = modelLineCount / minimapLineCount;
704723

724+
if (!oldMinimapLines) {
725+
this._minimapLines = this._createLineSampling(minimapLineCount, modelLineCount, ratio);
726+
this._samplingRatio = ratio;
727+
return;
728+
}
729+
730+
const halfRatio = ratio / 2;
731+
const oldLength = oldMinimapLines.length;
705732
let result: number[] = [];
733+
let oldIndex = 0;
734+
let oldDeltaLineCount = 0;
735+
let minModelLineNumber = 1;
736+
let eventCount = 0;
737+
let currentDeleteStart = 0, currentDeleteEnd = 0;
738+
let currentInsertStart = 0, currentInsertEnd = 0;
706739
for (let i = 0; i < minimapLineCount; i++) {
707-
const desiredLine = Math.min(modelLineCount, Math.round((i + 1) * ratio));
708-
result[i] = desiredLine;
740+
const fromModelLineNumber = Math.max(minModelLineNumber, Math.round(i * ratio));
741+
const toModelLineNumber = Math.max(fromModelLineNumber, Math.round((i + 1) * ratio));
742+
743+
while (oldIndex < oldLength && oldMinimapLines[oldIndex] < fromModelLineNumber) {
744+
if (eventCount < MAX_EVENT_COUNT) {
745+
if (currentInsertEnd !== 0) {
746+
// must deliver previous insert event
747+
this._actual.onLinesInserted(currentInsertStart, currentInsertEnd);
748+
oldDeltaLineCount += (currentInsertEnd - currentInsertStart + 1);
749+
currentInsertStart = 0;
750+
currentInsertEnd = 0;
751+
eventCount++;
752+
}
753+
754+
if (currentDeleteStart === 0) {
755+
currentDeleteStart = oldIndex + 1 + oldDeltaLineCount;
756+
currentDeleteEnd = currentDeleteStart;
757+
} else {
758+
currentDeleteEnd++;
759+
}
760+
}
761+
oldIndex++;
762+
}
763+
764+
let selectedModelLineNumber: number;
765+
if (oldIndex < oldLength && oldMinimapLines[oldIndex] <= toModelLineNumber) {
766+
// reuse the old sampled line
767+
selectedModelLineNumber = oldMinimapLines[oldIndex];
768+
oldIndex++;
769+
} else {
770+
if (i === 0) {
771+
selectedModelLineNumber = 1;
772+
} else if (i + 1 === minimapLineCount) {
773+
selectedModelLineNumber = modelLineCount;
774+
} else {
775+
selectedModelLineNumber = Math.round(i * ratio + halfRatio);
776+
}
777+
if (eventCount < MAX_EVENT_COUNT) {
778+
if (currentDeleteEnd !== 0) {
779+
// must deliver previous delete event
780+
this._actual.onLinesDeleted(currentDeleteStart, currentDeleteEnd);
781+
oldDeltaLineCount -= (currentDeleteEnd - currentDeleteStart + 1);
782+
currentDeleteStart = 0;
783+
currentDeleteEnd = 0;
784+
eventCount++;
785+
}
786+
787+
if (currentInsertStart === 0) {
788+
currentInsertStart = oldIndex + 1 + oldDeltaLineCount;
789+
currentInsertEnd = currentInsertStart;
790+
} else {
791+
currentInsertEnd++;
792+
}
793+
}
794+
}
795+
796+
result[i] = selectedModelLineNumber;
797+
if (selectedModelLineNumber === modelLineCount) {
798+
minModelLineNumber = selectedModelLineNumber;
799+
} else {
800+
minModelLineNumber = selectedModelLineNumber + 1;
801+
}
802+
}
803+
804+
if (eventCount < MAX_EVENT_COUNT) {
805+
if (currentInsertEnd !== 0) {
806+
// must deliver previous insert event
807+
this._actual.onLinesInserted(currentInsertStart, currentInsertEnd);
808+
oldDeltaLineCount += (currentInsertEnd - currentInsertStart + 1);
809+
currentInsertStart = 0;
810+
currentInsertEnd = 0;
811+
}
812+
while (oldIndex < oldLength) {
813+
if (currentDeleteStart === 0) {
814+
currentDeleteStart = oldIndex + 1 + oldDeltaLineCount;
815+
currentDeleteEnd = currentDeleteStart;
816+
} else {
817+
currentDeleteEnd++;
818+
}
819+
oldIndex++;
820+
}
821+
if (currentDeleteEnd !== 0) {
822+
// must deliver previous delete event
823+
this._actual.onLinesDeleted(currentDeleteStart, currentDeleteEnd);
824+
oldDeltaLineCount -= (currentDeleteEnd - currentDeleteStart + 1);
825+
currentDeleteStart = 0;
826+
currentDeleteEnd = 0;
827+
}
828+
} else {
829+
// too many events, just give up
830+
this._actual.onFlushed();
709831
}
710832

711833
this._samplingRatio = ratio;
@@ -1288,8 +1410,6 @@ class InnerMinimap extends Disposable {
12881410
}
12891411

12901412
private renderLines(layout: MinimapLayout): RenderData | null {
1291-
const renderMinimap = this._model.options.renderMinimap;
1292-
const charRenderer = this._model.options.charRenderer();
12931413
const startLineNumber = layout.startLineNumber;
12941414
const endLineNumber = layout.endLineNumber;
12951415
const minimapLineHeight = this._model.options.minimapLineHeight;
@@ -1322,7 +1442,13 @@ class InnerMinimap extends Disposable {
13221442
const lineInfo = this._model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed);
13231443
const tabSize = this._model.getOptions().tabSize;
13241444
const background = this._model.options.backgroundColor;
1325-
const useLighterFont = this._model.tokensColorTracker.backgroundIsLight();
1445+
const tokensColorTracker = this._model.tokensColorTracker;
1446+
const useLighterFont = tokensColorTracker.backgroundIsLight();
1447+
const renderMinimap = this._model.options.renderMinimap;
1448+
const charRenderer = this._model.options.charRenderer();
1449+
const isSampling = this._model.options.isSampling;
1450+
const fontScale = this._model.options.fontScale;
1451+
const minimapCharWidth = this._model.options.minimapCharWidth;
13261452

13271453
// Render the rest of lines
13281454
let dy = 0;
@@ -1334,13 +1460,14 @@ class InnerMinimap extends Disposable {
13341460
background,
13351461
useLighterFont,
13361462
renderMinimap,
1337-
this._model.options.minimapCharWidth,
1338-
this._model.tokensColorTracker,
1463+
minimapCharWidth,
1464+
tokensColorTracker,
13391465
charRenderer,
13401466
dy,
13411467
tabSize,
13421468
lineInfo[lineIndex]!,
1343-
this._model.options.fontScale
1469+
fontScale,
1470+
isSampling
13441471
);
13451472
}
13461473
renderedLines[lineIndex] = new MinimapLine(dy);
@@ -1466,7 +1593,8 @@ class InnerMinimap extends Disposable {
14661593
dy: number,
14671594
tabSize: number,
14681595
lineData: ViewLineData,
1469-
fontScale: number
1596+
fontScale: number,
1597+
isSampling: boolean
14701598
): void {
14711599
const content = lineData.content;
14721600
const tokens = lineData.tokens;
@@ -1504,7 +1632,7 @@ class InnerMinimap extends Disposable {
15041632
if (renderMinimap === RenderMinimap.Blocks) {
15051633
minimapCharRenderer.blockRenderChar(target, dx, dy, tokenColor, backgroundColor, useLighterFont);
15061634
} else { // RenderMinimap.Text
1507-
minimapCharRenderer.renderChar(target, dx, dy, charCode, tokenColor, backgroundColor, fontScale, useLighterFont);
1635+
minimapCharRenderer.renderChar(target, dx, dy, charCode, tokenColor, backgroundColor, fontScale, useLighterFont, isSampling);
15081636
}
15091637

15101638
dx += charWidth;

src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ export class MinimapCharRenderer {
3434
color: RGBA8,
3535
backgroundColor: RGBA8,
3636
fontScale: number,
37-
useLighterFont: boolean
37+
useLighterFont: boolean,
38+
force1pxHeight: boolean
3839
): void {
3940
const charWidth = Constants.BASE_CHAR_WIDTH * this.scale;
4041
const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale;
@@ -60,7 +61,8 @@ export class MinimapCharRenderer {
6061
let sourceOffset = charIndex * charWidth * charHeight;
6162

6263
let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT;
63-
for (let y = 0; y < charHeight; y++) {
64+
const renderHeight = (force1pxHeight ? 1 : charHeight);
65+
for (let y = 0; y < renderHeight; y++) {
6466
let column = row;
6567
for (let x = 0; x < charWidth; x++) {
6668
const c = charData[sourceOffset++] / 255;

src/vs/editor/test/browser/view/minimapCharRenderer.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ suite('MinimapCharRenderer', () => {
7878
imageData.data[4 * i + 2] = background.b;
7979
imageData.data[4 * i + 3] = 255;
8080
}
81-
renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 2, false);
81+
renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 2, false, false);
8282

8383
let actual: number[] = [];
8484
for (let i = 0; i < imageData.data.length; i++) {
@@ -108,7 +108,7 @@ suite('MinimapCharRenderer', () => {
108108
imageData.data[4 * i + 3] = 255;
109109
}
110110

111-
renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 1, false);
111+
renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 1, false, false);
112112

113113
let actual: number[] = [];
114114
for (let i = 0; i < imageData.data.length; i++) {

0 commit comments

Comments
 (0)