Skip to content

Commit b098a1c

Browse files
authored
Merge pull request microsoft#90808 from microsoft/alex/minimap
New minimap rendering mode
2 parents b29d61c + 95cc4e3 commit b098a1c

13 files changed

Lines changed: 1390 additions & 240 deletions

File tree

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

Lines changed: 765 additions & 199 deletions
Large diffs are not rendered by default.

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ 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;
41-
if (dx + charWidth > target.width || dy + charHeight > target.height) {
42+
const renderHeight = (force1pxHeight ? 1 : charHeight);
43+
if (dx + charWidth > target.width || dy + renderHeight > target.height) {
4244
console.warn('bad render request outside image data');
4345
return;
4446
}
@@ -60,7 +62,7 @@ export class MinimapCharRenderer {
6062
let sourceOffset = charIndex * charWidth * charHeight;
6163

6264
let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT;
63-
for (let y = 0; y < charHeight; y++) {
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;
@@ -80,11 +82,13 @@ export class MinimapCharRenderer {
8082
dy: number,
8183
color: RGBA8,
8284
backgroundColor: RGBA8,
83-
useLighterFont: boolean
85+
useLighterFont: boolean,
86+
force1pxHeight: boolean
8487
): void {
8588
const charWidth = Constants.BASE_CHAR_WIDTH * this.scale;
8689
const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale;
87-
if (dx + charWidth > target.width || dy + charHeight > target.height) {
90+
const renderHeight = (force1pxHeight ? 1 : charHeight);
91+
if (dx + charWidth > target.width || dy + renderHeight > target.height) {
8892
console.warn('bad render request outside image data');
8993
return;
9094
}
@@ -108,7 +112,7 @@ export class MinimapCharRenderer {
108112
const dest = target.data;
109113

110114
let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT;
111-
for (let y = 0; y < charHeight; y++) {
115+
for (let y = 0; y < renderHeight; y++) {
112116
let column = row;
113117
for (let x = 0; x < charWidth; x++) {
114118
dest[column++] = colorR;

src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,10 @@ export class DecorationsOverviewRuler extends ViewPart {
276276
return true;
277277
}
278278
public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean {
279-
return true;
279+
if (e.affectsOverviewRuler) {
280+
return true;
281+
}
282+
return false;
280283
}
281284
public onFlushed(e: viewEvents.ViewFlushedEvent): boolean {
282285
return true;

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

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

289289
private _isDominatedByLongLines: boolean;
290+
private _maxLineNumber: number;
290291
private _lineNumbersDigitCount: number;
291292

292293
private _rawOptions: IEditorOptions;
@@ -298,6 +299,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
298299
this.isSimpleWidget = isSimpleWidget;
299300

300301
this._isDominatedByLongLines = false;
302+
this._maxLineNumber = 1;
301303
this._lineNumbersDigitCount = 1;
302304

303305
this._rawOptions = deepCloneAndMigrateOptions(_options);
@@ -347,6 +349,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
347349
fontInfo: this.readConfiguration(bareFontInfo),
348350
extraEditorClassName: partialEnv.extraEditorClassName,
349351
isDominatedByLongLines: this._isDominatedByLongLines,
352+
maxLineNumber: this._maxLineNumber,
350353
lineNumbersDigitCount: this._lineNumbersDigitCount,
351354
emptySelectionClipboard: partialEnv.emptySelectionClipboard,
352355
pixelRatio: partialEnv.pixelRatio,
@@ -405,11 +408,11 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
405408
}
406409

407410
public setMaxLineNumber(maxLineNumber: number): void {
408-
let digitCount = CommonEditorConfiguration._digitCount(maxLineNumber);
409-
if (this._lineNumbersDigitCount === digitCount) {
411+
if (this._maxLineNumber === maxLineNumber) {
410412
return;
411413
}
412-
this._lineNumbersDigitCount = digitCount;
414+
this._maxLineNumber = maxLineNumber;
415+
this._lineNumbersDigitCount = CommonEditorConfiguration._digitCount(maxLineNumber);
413416
this._recomputeOptions();
414417
}
415418

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

Lines changed: 108 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,7 @@ export interface IEnvironmentalOptions {
672672
readonly fontInfo: FontInfo;
673673
readonly extraEditorClassName: string;
674674
readonly isDominatedByLongLines: boolean;
675+
readonly maxLineNumber: number;
675676
readonly lineNumbersDigitCount: number;
676677
readonly emptySelectionClipboard: boolean;
677678
readonly pixelRatio: number;
@@ -1685,6 +1686,14 @@ export interface EditorLayoutInfo {
16851686
* The width of the minimap
16861687
*/
16871688
readonly minimapWidth: number;
1689+
readonly minimapHeightIsEditorHeight: boolean;
1690+
readonly minimapIsSampling: boolean;
1691+
readonly minimapScale: number;
1692+
readonly minimapLineHeight: number;
1693+
readonly minimapCanvasInnerWidth: number;
1694+
readonly minimapCanvasInnerHeight: number;
1695+
readonly minimapCanvasOuterWidth: number;
1696+
readonly minimapCanvasOuterHeight: number;
16881697

16891698
/**
16901699
* Minimap render type
@@ -1718,6 +1727,7 @@ export interface EditorLayoutInfoComputerEnv {
17181727
outerWidth: number;
17191728
outerHeight: number;
17201729
lineHeight: number;
1730+
maxLineNumber: number;
17211731
lineNumbersDigitCount: number;
17221732
typicalHalfwidthCharacterWidth: number;
17231733
maxDigitWidth: number;
@@ -1741,13 +1751,28 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
17411751
outerWidth: env.outerWidth,
17421752
outerHeight: env.outerHeight,
17431753
lineHeight: env.fontInfo.lineHeight,
1754+
maxLineNumber: env.maxLineNumber,
17441755
lineNumbersDigitCount: env.lineNumbersDigitCount,
17451756
typicalHalfwidthCharacterWidth: env.fontInfo.typicalHalfwidthCharacterWidth,
17461757
maxDigitWidth: env.fontInfo.maxDigitWidth,
17471758
pixelRatio: env.pixelRatio
17481759
});
17491760
}
17501761

1762+
public static computeContainedMinimapLineCount(input: {
1763+
modelLineCount: number;
1764+
scrollBeyondLastLine: boolean;
1765+
height: number;
1766+
lineHeight: number;
1767+
pixelRatio: number;
1768+
}): { typicalViewportLineCount: number; extraLinesBeyondLastLine: number; desiredRatio: number; minimapLineCount: number; } {
1769+
const typicalViewportLineCount = input.height / input.lineHeight;
1770+
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);
1773+
return { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount };
1774+
}
1775+
17511776
public static computeLayout(options: IComputedEditorOptions, env: EditorLayoutInfoComputerEnv): EditorLayoutInfo {
17521777
const outerWidth = env.outerWidth | 0;
17531778
const outerHeight = env.outerHeight | 0;
@@ -1760,12 +1785,14 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
17601785
const showGlyphMargin = options.get(EditorOption.glyphMargin);
17611786
const showLineNumbers = (options.get(EditorOption.lineNumbers).renderType !== RenderLineNumbersType.Off);
17621787
const lineNumbersMinChars = options.get(EditorOption.lineNumbersMinChars) | 0;
1788+
const scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine);
17631789
const minimap = options.get(EditorOption.minimap);
17641790
const minimapEnabled = minimap.enabled;
17651791
const minimapSide = minimap.side;
17661792
const minimapRenderCharacters = minimap.renderCharacters;
1767-
const minimapScale = (pixelRatio >= 2 ? Math.round(minimap.scale * 2) : minimap.scale);
1793+
let minimapScale = (pixelRatio >= 2 ? Math.round(minimap.scale * 2) : minimap.scale);
17681794
const minimapMaxColumn = minimap.maxColumn | 0;
1795+
const minimapMode = minimap.mode;
17691796

17701797
const scrollbar = options.get(EditorOption.scrollbar);
17711798
const verticalScrollbarWidth = scrollbar.verticalScrollbarSize | 0;
@@ -1805,19 +1832,65 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
18051832

18061833
const remainingWidth = outerWidth - glyphMarginWidth - lineNumbersWidth - lineDecorationsWidth;
18071834

1835+
const baseCharHeight = minimapRenderCharacters ? 2 : 3;
18081836
let renderMinimap: RenderMinimap;
18091837
let minimapLeft: number;
18101838
let minimapWidth: number;
1839+
let minimapCanvasInnerWidth: number;
1840+
let minimapCanvasInnerHeight = Math.floor(pixelRatio * outerHeight);
1841+
let minimapCanvasOuterWidth: number;
1842+
const minimapCanvasOuterHeight = minimapCanvasInnerHeight / pixelRatio;
1843+
let minimapHeightIsEditorHeight = false;
1844+
let minimapIsSampling = false;
1845+
let minimapLineHeight = baseCharHeight * minimapScale;
18111846
let contentWidth: number;
18121847
if (!minimapEnabled) {
18131848
minimapLeft = 0;
18141849
minimapWidth = 0;
1850+
minimapCanvasInnerWidth = 0;
1851+
minimapCanvasOuterWidth = 0;
1852+
minimapLineHeight = 1;
18151853
renderMinimap = RenderMinimap.None;
18161854
contentWidth = remainingWidth;
18171855
} else {
1818-
// The minimapScale is also the pixel width of each character. Adjust
1819-
// for the pixel ratio of the screen.
1820-
const minimapCharWidth = minimapScale / pixelRatio;
1856+
let minimapCharWidth = minimapScale / pixelRatio;
1857+
let minimapWidthMultiplier: number = 1;
1858+
1859+
if (minimapMode === 'cover' || minimapMode === 'contain') {
1860+
const modelLineCount = env.maxLineNumber;
1861+
const { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({
1862+
modelLineCount: modelLineCount,
1863+
scrollBeyondLastLine: scrollBeyondLastLine,
1864+
height: outerHeight,
1865+
lineHeight: lineHeight,
1866+
pixelRatio: pixelRatio
1867+
});
1868+
// ratio is intentionally not part of the layout to avoid the layout changing all the time
1869+
// when doing sampling
1870+
const ratio = modelLineCount / minimapLineCount;
1871+
1872+
if (ratio > 1) {
1873+
minimapHeightIsEditorHeight = true;
1874+
minimapIsSampling = true;
1875+
minimapScale = 1;
1876+
minimapLineHeight = 1;
1877+
minimapCharWidth = minimapScale / pixelRatio;
1878+
} else {
1879+
const effectiveMinimapHeight = Math.ceil((modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight);
1880+
if (minimapMode === 'cover' || effectiveMinimapHeight > minimapCanvasInnerHeight) {
1881+
minimapHeightIsEditorHeight = true;
1882+
const configuredFontScale = minimapScale;
1883+
minimapLineHeight = Math.min(lineHeight * pixelRatio, Math.max(1, Math.floor(1 / desiredRatio)));
1884+
minimapScale = Math.min(configuredFontScale + 1, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight)));
1885+
if (minimapScale > configuredFontScale) {
1886+
minimapWidthMultiplier = Math.min(2, minimapScale / configuredFontScale);
1887+
}
1888+
minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier;
1889+
minimapCanvasInnerHeight = Math.ceil((Math.max(typicalViewportLineCount, modelLineCount + extraLinesBeyondLastLine)) * minimapLineHeight);
1890+
}
1891+
}
1892+
}
1893+
18211894
renderMinimap = minimapRenderCharacters ? RenderMinimap.Text : RenderMinimap.Blocks;
18221895

18231896
// Given:
@@ -1849,6 +1922,10 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
18491922
} else {
18501923
minimapLeft = outerWidth - minimapWidth - verticalScrollbarWidth;
18511924
}
1925+
1926+
minimapCanvasInnerWidth = Math.floor(pixelRatio * minimapWidth);
1927+
minimapCanvasOuterWidth = minimapCanvasInnerWidth / pixelRatio;
1928+
minimapCanvasInnerWidth = Math.floor(minimapCanvasInnerWidth * minimapWidthMultiplier);
18521929
}
18531930

18541931
// (leaving 2px for the cursor to have space after the last character)
@@ -1875,6 +1952,14 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
18751952
renderMinimap: renderMinimap,
18761953
minimapLeft: minimapLeft,
18771954
minimapWidth: minimapWidth,
1955+
minimapHeightIsEditorHeight: minimapHeightIsEditorHeight,
1956+
minimapIsSampling: minimapIsSampling,
1957+
minimapScale: minimapScale,
1958+
minimapLineHeight: minimapLineHeight,
1959+
minimapCanvasInnerWidth: minimapCanvasInnerWidth,
1960+
minimapCanvasInnerHeight: minimapCanvasInnerHeight,
1961+
minimapCanvasOuterWidth: minimapCanvasOuterWidth,
1962+
minimapCanvasOuterHeight: minimapCanvasOuterHeight,
18781963

18791964
viewportColumn: viewportColumn,
18801965

@@ -1975,6 +2060,11 @@ export interface IEditorMinimapOptions {
19752060
* Defaults to 'right'.
19762061
*/
19772062
side?: 'right' | 'left';
2063+
/**
2064+
* Control the minimap rendering mode.
2065+
* Defaults to 'actual'.
2066+
*/
2067+
mode?: 'actual' | 'cover' | 'contain';
19782068
/**
19792069
* Control the rendering of the minimap slider.
19802070
* Defaults to 'mouseover'.
@@ -1990,7 +2080,6 @@ export interface IEditorMinimapOptions {
19902080
* Defaults to 120.
19912081
*/
19922082
maxColumn?: number;
1993-
19942083
/**
19952084
* Relative size of the font in the minimap. Defaults to 1.
19962085
*/
@@ -2004,6 +2093,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
20042093
constructor() {
20052094
const defaults: EditorMinimapOptions = {
20062095
enabled: true,
2096+
mode: 'actual',
20072097
side: 'right',
20082098
showSlider: 'mouseover',
20092099
renderCharacters: true,
@@ -2018,6 +2108,17 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
20182108
default: defaults.enabled,
20192109
description: nls.localize('minimap.enabled', "Controls whether the minimap is shown.")
20202110
},
2111+
'editor.minimap.mode': {
2112+
type: 'string',
2113+
enum: ['actual', 'cover', 'contain'],
2114+
enumDescriptions: [
2115+
nls.localize('minimap.mode.actual', "The minimap will be displayed in its original size, so it might be higher than the editor."),
2116+
nls.localize('minimap.mode.cover', "The minimap will always have the height of the editor and will stretch or shrink as necessary."),
2117+
nls.localize('minimap.mode.contain', "The minimap will shrink as necessary to never be higher than the editor."),
2118+
],
2119+
default: defaults.mode,
2120+
description: nls.localize('minimap.mode', "Controls the rendering mode of the minimap.")
2121+
},
20212122
'editor.minimap.side': {
20222123
type: 'string',
20232124
enum: ['left', 'right'],
@@ -2046,7 +2147,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
20462147
type: 'number',
20472148
default: defaults.maxColumn,
20482149
description: nls.localize('minimap.maxColumn', "Limit the width of the minimap to render at most a certain number of columns.")
2049-
},
2150+
}
20502151
}
20512152
);
20522153
}
@@ -2058,6 +2159,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
20582159
const input = _input as IEditorMinimapOptions;
20592160
return {
20602161
enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled),
2162+
mode: EditorStringEnumOption.stringSet<'actual' | 'cover' | 'contain'>(input.mode, this.defaultValue.mode, ['actual', 'cover', 'contain']),
20612163
side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']),
20622164
showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']),
20632165
renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters),

0 commit comments

Comments
 (0)