Skip to content

Commit aadc10f

Browse files
committed
Fixes microsoft#90990: The minimap layout computation should use view line count not model line count
1 parent 83b7504 commit aadc10f

6 files changed

Lines changed: 73 additions & 44 deletions

File tree

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

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import * as platform from 'vs/base/common/platform';
1313
import * as strings from 'vs/base/common/strings';
1414
import { ILine, RenderedLinesCollection } from 'vs/editor/browser/view/viewLayer';
1515
import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart';
16-
import { RenderMinimap, EditorOption, MINIMAP_GUTTER_WIDTH, EditorLayoutInfoComputer, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions';
16+
import { RenderMinimap, EditorOption, MINIMAP_GUTTER_WIDTH, EditorLayoutInfoComputer } from 'vs/editor/common/config/editorOptions';
1717
import { Range } from 'vs/editor/common/core/range';
1818
import { RGBA8 } from 'vs/editor/common/core/rgba';
1919
import { IConfiguration, ScrollType } from 'vs/editor/common/editorCommon';
@@ -91,6 +91,8 @@ class MinimapOptions {
9191
*/
9292
public readonly canvasOuterHeight: number;
9393

94+
public readonly isSampling: boolean;
95+
public readonly editorHeight: number;
9496
public readonly fontScale: number;
9597
public readonly minimapLineHeight: number;
9698
public readonly minimapCharWidth: number;
@@ -122,6 +124,8 @@ class MinimapOptions {
122124
this.canvasOuterWidth = layoutInfo.minimapCanvasOuterWidth;
123125
this.canvasOuterHeight = layoutInfo.minimapCanvasOuterHeight;
124126

127+
this.isSampling = layoutInfo.minimapIsSampling;
128+
this.editorHeight = layoutInfo.height;
125129
this.fontScale = layoutInfo.minimapScale;
126130
this.minimapLineHeight = layoutInfo.minimapLineHeight;
127131
this.minimapCharWidth = Constants.BASE_CHAR_WIDTH * this.fontScale;
@@ -154,6 +158,8 @@ class MinimapOptions {
154158
&& this.canvasInnerHeight === other.canvasInnerHeight
155159
&& this.canvasOuterWidth === other.canvasOuterWidth
156160
&& this.canvasOuterHeight === other.canvasOuterHeight
161+
&& this.isSampling === other.isSampling
162+
&& this.editorHeight === other.editorHeight
157163
&& this.fontScale === other.fontScale
158164
&& this.minimapLineHeight === other.minimapLineHeight
159165
&& this.minimapCharWidth === other.minimapCharWidth
@@ -527,26 +533,24 @@ type SamplingStateEvent = SamplingStateLinesInsertedEvent | SamplingStateLinesDe
527533

528534
class MinimapSamplingState {
529535

530-
public static compute(options: IComputedEditorOptions, modelLineCount: number, oldSamplingState: MinimapSamplingState | null): [MinimapSamplingState | null, SamplingStateEvent[]] {
531-
const minimapOpts = options.get(EditorOption.minimap);
532-
const layoutInfo = options.get(EditorOption.layoutInfo);
533-
if (!minimapOpts.enabled || !layoutInfo.minimapIsSampling) {
536+
public static compute(options: MinimapOptions, /* options: IComputedEditorOptions, */ viewLineCount: number, oldSamplingState: MinimapSamplingState | null): [MinimapSamplingState | null, SamplingStateEvent[]] {
537+
if (options.renderMinimap === RenderMinimap.None || !options.isSampling) {
534538
return [null, []];
535539
}
536540

537541
// ratio is intentionally not part of the layout to avoid the layout changing all the time
538542
// so we need to recompute it again...
539-
const pixelRatio = options.get(EditorOption.pixelRatio);
540-
const lineHeight = options.get(EditorOption.lineHeight);
541-
const scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine);
543+
const pixelRatio = options.pixelRatio;
544+
const lineHeight = options.lineHeight;
545+
const scrollBeyondLastLine = options.scrollBeyondLastLine;
542546
const { minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({
543-
modelLineCount: modelLineCount,
547+
viewLineCount: viewLineCount,
544548
scrollBeyondLastLine: scrollBeyondLastLine,
545-
height: layoutInfo.height,
549+
height: options.editorHeight,
546550
lineHeight: lineHeight,
547551
pixelRatio: pixelRatio
548552
});
549-
const ratio = modelLineCount / minimapLineCount;
553+
const ratio = viewLineCount / minimapLineCount;
550554
const halfRatio = ratio / 2;
551555

552556
if (!oldSamplingState || oldSamplingState.minimapLines.length === 0) {
@@ -556,7 +560,7 @@ class MinimapSamplingState {
556560
for (let i = 0, lastIndex = minimapLineCount - 1; i < lastIndex; i++) {
557561
result[i] = Math.round(i * ratio + halfRatio);
558562
}
559-
result[minimapLineCount - 1] = modelLineCount;
563+
result[minimapLineCount - 1] = viewLineCount;
560564
}
561565
return [new MinimapSamplingState(ratio, result), []];
562566
}
@@ -566,15 +570,15 @@ class MinimapSamplingState {
566570
let result: number[] = [];
567571
let oldIndex = 0;
568572
let oldDeltaLineCount = 0;
569-
let minModelLineNumber = 1;
573+
let minViewLineNumber = 1;
570574
const MAX_EVENT_COUNT = 10; // generate at most 10 events, if there are more than 10 changes, just flush all previous data
571575
let events: SamplingStateEvent[] = [];
572576
let lastEvent: SamplingStateEvent | null = null;
573577
for (let i = 0; i < minimapLineCount; i++) {
574-
const fromModelLineNumber = Math.max(minModelLineNumber, Math.round(i * ratio));
575-
const toModelLineNumber = Math.max(fromModelLineNumber, Math.round((i + 1) * ratio));
578+
const fromViewLineNumber = Math.max(minViewLineNumber, Math.round(i * ratio));
579+
const toViewLineNumber = Math.max(fromViewLineNumber, Math.round((i + 1) * ratio));
576580

577-
while (oldIndex < oldLength && oldMinimapLines[oldIndex] < fromModelLineNumber) {
581+
while (oldIndex < oldLength && oldMinimapLines[oldIndex] < fromViewLineNumber) {
578582
if (events.length < MAX_EVENT_COUNT) {
579583
const oldMinimapLineNumber = oldIndex + 1 + oldDeltaLineCount;
580584
if (lastEvent && lastEvent.type === 'deleted' && lastEvent._oldIndex === oldIndex - 1) {
@@ -588,18 +592,18 @@ class MinimapSamplingState {
588592
oldIndex++;
589593
}
590594

591-
let selectedModelLineNumber: number;
592-
if (oldIndex < oldLength && oldMinimapLines[oldIndex] <= toModelLineNumber) {
595+
let selectedViewLineNumber: number;
596+
if (oldIndex < oldLength && oldMinimapLines[oldIndex] <= toViewLineNumber) {
593597
// reuse the old sampled line
594-
selectedModelLineNumber = oldMinimapLines[oldIndex];
598+
selectedViewLineNumber = oldMinimapLines[oldIndex];
595599
oldIndex++;
596600
} else {
597601
if (i === 0) {
598-
selectedModelLineNumber = 1;
602+
selectedViewLineNumber = 1;
599603
} else if (i + 1 === minimapLineCount) {
600-
selectedModelLineNumber = modelLineCount;
604+
selectedViewLineNumber = viewLineCount;
601605
} else {
602-
selectedModelLineNumber = Math.round(i * ratio + halfRatio);
606+
selectedViewLineNumber = Math.round(i * ratio + halfRatio);
603607
}
604608
if (events.length < MAX_EVENT_COUNT) {
605609
const oldMinimapLineNumber = oldIndex + 1 + oldDeltaLineCount;
@@ -613,8 +617,8 @@ class MinimapSamplingState {
613617
}
614618
}
615619

616-
result[i] = selectedModelLineNumber;
617-
minModelLineNumber = selectedModelLineNumber;
620+
result[i] = selectedViewLineNumber;
621+
minViewLineNumber = selectedViewLineNumber;
618622
}
619623

620624
if (events.length < MAX_EVENT_COUNT) {
@@ -743,7 +747,7 @@ export class Minimap extends ViewPart implements IMinimapModel {
743747
this._minimapSelections = null;
744748

745749
this.options = new MinimapOptions(this._context.configuration, this._context.theme, this.tokensColorTracker);
746-
const [samplingState,] = MinimapSamplingState.compute(this._context.configuration.options, this._context.model.getLineCount(), null);
750+
const [samplingState,] = MinimapSamplingState.compute(this.options, this._context.model.getLineCount(), null);
747751
this._samplingState = samplingState;
748752
this._shouldCheckSampling = false;
749753

@@ -787,6 +791,9 @@ export class Minimap extends ViewPart implements IMinimapModel {
787791
return false;
788792
}
789793
public onFlushed(e: viewEvents.ViewFlushedEvent): boolean {
794+
if (this._samplingState) {
795+
this._shouldCheckSampling = true;
796+
}
790797
return this._actual.onFlushed();
791798
}
792799
public onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean {
@@ -898,7 +905,7 @@ export class Minimap extends ViewPart implements IMinimapModel {
898905
this._minimapSelections = null;
899906

900907
const wasSampling = Boolean(this._samplingState);
901-
const [samplingState, events] = MinimapSamplingState.compute(this._context.configuration.options, this._context.model.getLineCount(), this._samplingState);
908+
const [samplingState, events] = MinimapSamplingState.compute(this.options, this._context.model.getLineCount(), this._samplingState);
902909
this._samplingState = samplingState;
903910

904911
if (wasSampling && this._samplingState) {

src/vs/editor/common/config/commonEditorConfig.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
287287
public options!: ComputedEditorOptions;
288288

289289
private _isDominatedByLongLines: boolean;
290-
private _maxLineNumber: number;
290+
private _viewLineCount: number;
291291
private _lineNumbersDigitCount: number;
292292

293293
private _rawOptions: IEditorOptions;
@@ -299,7 +299,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
299299
this.isSimpleWidget = isSimpleWidget;
300300

301301
this._isDominatedByLongLines = false;
302-
this._maxLineNumber = 1;
302+
this._viewLineCount = 1;
303303
this._lineNumbersDigitCount = 1;
304304

305305
this._rawOptions = deepCloneAndMigrateOptions(_options);
@@ -349,7 +349,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
349349
fontInfo: this.readConfiguration(bareFontInfo),
350350
extraEditorClassName: partialEnv.extraEditorClassName,
351351
isDominatedByLongLines: this._isDominatedByLongLines,
352-
maxLineNumber: this._maxLineNumber,
352+
viewLineCount: this._viewLineCount,
353353
lineNumbersDigitCount: this._lineNumbersDigitCount,
354354
emptySelectionClipboard: partialEnv.emptySelectionClipboard,
355355
pixelRatio: partialEnv.pixelRatio,
@@ -408,11 +408,19 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
408408
}
409409

410410
public setMaxLineNumber(maxLineNumber: number): void {
411-
if (this._maxLineNumber === maxLineNumber) {
411+
const lineNumbersDigitCount = CommonEditorConfiguration._digitCount(maxLineNumber);
412+
if (this._lineNumbersDigitCount === lineNumbersDigitCount) {
412413
return;
413414
}
414-
this._maxLineNumber = maxLineNumber;
415-
this._lineNumbersDigitCount = CommonEditorConfiguration._digitCount(maxLineNumber);
415+
this._lineNumbersDigitCount = lineNumbersDigitCount;
416+
this._recomputeOptions();
417+
}
418+
419+
public setViewLineCount(viewLineCount: number): void {
420+
if (this._viewLineCount === viewLineCount) {
421+
return;
422+
}
423+
this._viewLineCount = viewLineCount;
416424
this._recomputeOptions();
417425
}
418426

src/vs/editor/common/config/editorOptions.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ export interface IEnvironmentalOptions {
672672
readonly fontInfo: FontInfo;
673673
readonly extraEditorClassName: string;
674674
readonly isDominatedByLongLines: boolean;
675-
readonly maxLineNumber: number;
675+
readonly viewLineCount: number;
676676
readonly lineNumbersDigitCount: number;
677677
readonly emptySelectionClipboard: boolean;
678678
readonly pixelRatio: number;
@@ -1727,7 +1727,7 @@ export interface EditorLayoutInfoComputerEnv {
17271727
outerWidth: number;
17281728
outerHeight: number;
17291729
lineHeight: number;
1730-
maxLineNumber: number;
1730+
viewLineCount: number;
17311731
lineNumbersDigitCount: number;
17321732
typicalHalfwidthCharacterWidth: number;
17331733
maxDigitWidth: number;
@@ -1751,7 +1751,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
17511751
outerWidth: env.outerWidth,
17521752
outerHeight: env.outerHeight,
17531753
lineHeight: env.fontInfo.lineHeight,
1754-
maxLineNumber: env.maxLineNumber,
1754+
viewLineCount: env.viewLineCount,
17551755
lineNumbersDigitCount: env.lineNumbersDigitCount,
17561756
typicalHalfwidthCharacterWidth: env.fontInfo.typicalHalfwidthCharacterWidth,
17571757
maxDigitWidth: env.fontInfo.maxDigitWidth,
@@ -1760,16 +1760,16 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
17601760
}
17611761

17621762
public static computeContainedMinimapLineCount(input: {
1763-
modelLineCount: number;
1763+
viewLineCount: number;
17641764
scrollBeyondLastLine: boolean;
17651765
height: number;
17661766
lineHeight: number;
17671767
pixelRatio: number;
17681768
}): { typicalViewportLineCount: number; extraLinesBeyondLastLine: number; desiredRatio: number; minimapLineCount: number; } {
17691769
const typicalViewportLineCount = input.height / input.lineHeight;
17701770
const extraLinesBeyondLastLine = input.scrollBeyondLastLine ? (typicalViewportLineCount - 1) : 0;
1771-
const desiredRatio = (input.modelLineCount + extraLinesBeyondLastLine) / (input.pixelRatio * input.height);
1772-
const minimapLineCount = Math.floor(input.modelLineCount / desiredRatio);
1771+
const desiredRatio = (input.viewLineCount + extraLinesBeyondLastLine) / (input.pixelRatio * input.height);
1772+
const minimapLineCount = Math.floor(input.viewLineCount / desiredRatio);
17731773
return { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount };
17741774
}
17751775

@@ -1857,17 +1857,17 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
18571857
let minimapWidthMultiplier: number = 1;
18581858

18591859
if (minimapMode === 'cover' || minimapMode === 'contain') {
1860-
const modelLineCount = env.maxLineNumber;
1860+
const viewLineCount = env.viewLineCount;
18611861
const { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({
1862-
modelLineCount: modelLineCount,
1862+
viewLineCount: viewLineCount,
18631863
scrollBeyondLastLine: scrollBeyondLastLine,
18641864
height: outerHeight,
18651865
lineHeight: lineHeight,
18661866
pixelRatio: pixelRatio
18671867
});
18681868
// ratio is intentionally not part of the layout to avoid the layout changing all the time
18691869
// when doing sampling
1870-
const ratio = modelLineCount / minimapLineCount;
1870+
const ratio = viewLineCount / minimapLineCount;
18711871

18721872
if (ratio > 1) {
18731873
minimapHeightIsEditorHeight = true;
@@ -1876,7 +1876,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
18761876
minimapLineHeight = 1;
18771877
minimapCharWidth = minimapScale / pixelRatio;
18781878
} else {
1879-
const effectiveMinimapHeight = Math.ceil((modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight);
1879+
const effectiveMinimapHeight = Math.ceil((viewLineCount + extraLinesBeyondLastLine) * minimapLineHeight);
18801880
if (minimapMode === 'cover' || effectiveMinimapHeight > minimapCanvasInnerHeight) {
18811881
minimapHeightIsEditorHeight = true;
18821882
const configuredFontScale = minimapScale;
@@ -1886,7 +1886,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
18861886
minimapWidthMultiplier = Math.min(2, minimapScale / configuredFontScale);
18871887
}
18881888
minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier;
1889-
minimapCanvasInnerHeight = Math.ceil((Math.max(typicalViewportLineCount, modelLineCount + extraLinesBeyondLastLine)) * minimapLineHeight);
1889+
minimapCanvasInnerHeight = Math.ceil((Math.max(typicalViewportLineCount, viewLineCount + extraLinesBeyondLastLine)) * minimapLineHeight);
18901890
}
18911891
}
18921892
}

src/vs/editor/common/editorCommon.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ export interface IConfiguration extends IDisposable {
154154
readonly options: IComputedEditorOptions;
155155

156156
setMaxLineNumber(maxLineNumber: number): void;
157+
setViewLineCount(viewLineCount: number): void;
157158
updateOptions(newOptions: IEditorOptions): void;
158159
getRawOptions(): IEditorOptions;
159160
observeReferenceElement(dimension?: IDimension): void;

src/vs/editor/common/viewModel/viewModelImpl.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
3333
private readonly configuration: IConfiguration;
3434
private readonly model: ITextModel;
3535
private readonly _tokenizeViewportSoon: RunOnceScheduler;
36+
private readonly _updateConfigurationViewLineCount: RunOnceScheduler;
3637
private hasFocus: boolean;
3738
private viewportStartLine: number;
3839
private viewportStartLineTrackedRange: string | null;
@@ -56,6 +57,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
5657
this.configuration = configuration;
5758
this.model = model;
5859
this._tokenizeViewportSoon = this._register(new RunOnceScheduler(() => this.tokenizeViewport(), 50));
60+
this._updateConfigurationViewLineCount = this._register(new RunOnceScheduler(() => this._updateConfigurationViewLineCountNow(), 0));
5961
this.hasFocus = false;
6062
this.viewportStartLine = -1;
6163
this.viewportStartLineTrackedRange = null;
@@ -130,6 +132,8 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
130132
this._endEmit();
131133
}
132134
}));
135+
136+
this._updateConfigurationViewLineCountNow();
133137
}
134138

135139
public dispose(): void {
@@ -142,6 +146,10 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
142146
this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, null, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
143147
}
144148

149+
private _updateConfigurationViewLineCountNow(): void {
150+
this.configuration.setViewLineCount(this.lines.getViewLineCount());
151+
}
152+
145153
public tokenizeViewport(): void {
146154
const linesViewportData = this.viewLayout.getLinesViewportData();
147155
const startPosition = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(linesViewportData.startLineNumber, 1));
@@ -180,6 +188,8 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
180188
// Never change the scroll position from 0 to something else...
181189
restorePreviousViewportStart = true;
182190
}
191+
192+
this._updateConfigurationViewLineCount.schedule();
183193
}
184194

185195
if (e.hasChanged(EditorOption.readOnly)) {
@@ -301,6 +311,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
301311
// Update the configuration and reset the centered view line
302312
this.viewportStartLine = -1;
303313
this.configuration.setMaxLineNumber(this.model.getLineCount());
314+
this._updateConfigurationViewLineCountNow();
304315

305316
// Recover viewport
306317
if (!this.hasFocus && this.model.getAttachedEditorCount() >= 2 && this.viewportStartLineTrackedRange) {
@@ -358,6 +369,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
358369
} finally {
359370
this._endEmit();
360371
}
372+
this._updateConfigurationViewLineCount.schedule();
361373
}
362374
}));
363375

@@ -387,6 +399,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
387399
} finally {
388400
this._endEmit();
389401
}
402+
this._updateConfigurationViewLineCount.schedule();
390403
}
391404

392405
public getVisibleRanges(): Range[] {

src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
8080
outerWidth: input.outerWidth,
8181
outerHeight: input.outerHeight,
8282
lineHeight: input.lineHeight,
83-
maxLineNumber: input.maxLineNumber || Math.pow(10, input.lineNumbersDigitCount) - 1,
83+
viewLineCount: input.maxLineNumber || Math.pow(10, input.lineNumbersDigitCount) - 1,
8484
lineNumbersDigitCount: input.lineNumbersDigitCount,
8585
typicalHalfwidthCharacterWidth: input.typicalHalfwidthCharacterWidth,
8686
maxDigitWidth: input.maxDigitWidth,

0 commit comments

Comments
 (0)