From 864688a2c1ed148bdc03b1c793831ca273351bde Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 6 May 2020 14:12:19 +0200 Subject: [PATCH] Keep minimap layout stable (#95178) --- src/vs/editor/common/config/editorOptions.ts | 152 ++++++++----------- 1 file changed, 66 insertions(+), 86 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 3f91eb4a293..89f2c1c7a25 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -706,12 +706,14 @@ export interface IEnvironmentalOptions { */ export class ComputeOptionsMemory { - public lastInput: IEditorLayoutComputerInput | null; - public lastResult: EditorLayoutInfo | null; + public stableMinimapLayoutInput: IMinimapLayoutInput | null; + public stableFitMaxMinimapScale: number; + public stableFitRemainingWidth: number; constructor() { - this.lastInput = null; - this.lastResult = null; + this.stableMinimapLayoutInput = null; + this.stableFitMaxMinimapScale = 0; + this.stableFitRemainingWidth = 0; } } @@ -1813,51 +1815,6 @@ export interface IEditorLayoutComputerInput { readonly accessibilitySupport: AccessibilitySupport; } -/** - * @internal - */ -function editorLayoutComputerInputSoftEquals(a: IEditorLayoutComputerInput, b: IEditorLayoutComputerInput): boolean { - return ( - a.outerWidth === b.outerWidth - && a.outerHeight === b.outerHeight - && a.isDominatedByLongLines === b.isDominatedByLongLines - && a.lineHeight === b.lineHeight - && a.lineNumbersDigitCount === b.lineNumbersDigitCount - && a.typicalHalfwidthCharacterWidth === b.typicalHalfwidthCharacterWidth - && a.maxDigitWidth === b.maxDigitWidth - && a.pixelRatio === b.pixelRatio - && a.glyphMargin === b.glyphMargin - && a.lineDecorationsWidth === b.lineDecorationsWidth - && a.folding === b.folding - && a.minimap.enabled === b.minimap.enabled - && a.minimap.side === b.minimap.side - && a.minimap.size === b.minimap.size - && a.minimap.showSlider === b.minimap.showSlider - && a.minimap.renderCharacters === b.minimap.renderCharacters - && a.minimap.maxColumn === b.minimap.maxColumn - && a.minimap.scale === b.minimap.scale - && a.scrollbar.arrowSize === b.scrollbar.arrowSize - && a.scrollbar.vertical === b.scrollbar.vertical - && a.scrollbar.horizontal === b.scrollbar.horizontal - && a.scrollbar.useShadows === b.scrollbar.useShadows - && a.scrollbar.verticalHasArrows === b.scrollbar.verticalHasArrows - && a.scrollbar.horizontalHasArrows === b.scrollbar.horizontalHasArrows - && a.scrollbar.handleMouseWheel === b.scrollbar.handleMouseWheel - && a.scrollbar.alwaysConsumeMouseWheel === b.scrollbar.alwaysConsumeMouseWheel - && a.scrollbar.horizontalScrollbarSize === b.scrollbar.horizontalScrollbarSize - && a.scrollbar.horizontalSliderSize === b.scrollbar.horizontalSliderSize - && a.scrollbar.verticalScrollbarSize === b.scrollbar.verticalScrollbarSize - && a.scrollbar.verticalSliderSize === b.scrollbar.verticalSliderSize - && a.lineNumbers.renderType === b.lineNumbers.renderType - && a.lineNumbersMinChars === b.lineNumbersMinChars - && a.scrollBeyondLastLine === b.scrollBeyondLastLine - && a.wordWrap === b.wordWrap - && a.wordWrapColumn === b.wordWrapColumn - && a.wordWrapMinified === b.wordWrapMinified - && a.accessibilitySupport === b.accessibilitySupport - ); -} - /** * @internal */ @@ -1943,10 +1900,10 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption minimapCanvasInnerHeight) { - if (minimapSize === 'fit' && isViewportWrapping) { - // `fit` becomes `fill`: this can cause the `minimapScale` to change, which causes `minimapWidth` to change. - // With viewport wrapping, this can cause the `viewLineCount` to change, which can lead to a new result - // where `fit` would not become `fill`... and so on... - // to break the loop, we store the result in memory and then reuse the same result on the next call. - storeResultInMemory = true; + let fitBecomesFill = false; + let maxMinimapScale = minimapScale + 1; + + if (minimapSize === 'fit') { + const effectiveMinimapHeight = Math.ceil((viewLineCount + extraLinesBeyondLastLine) * minimapLineHeight); + if (isViewportWrapping && couldUseMemory && remainingWidth <= memory.stableFitRemainingWidth) { + // There is a loop when using `fit` and viewport wrapping: + // - view line count impacts minimap layout + // - minimap layout impacts viewport width + // - viewport width impacts view line count + // To break the loop, once we go to a smaller minimap scale, we try to stick with it. + fitBecomesFill = true; + maxMinimapScale = memory.stableFitMaxMinimapScale; + } else { + fitBecomesFill = (effectiveMinimapHeight > minimapCanvasInnerHeight); + if (isViewportWrapping && fitBecomesFill) { + // remember for next time + memory.stableMinimapLayoutInput = input; + memory.stableFitRemainingWidth = remainingWidth; + } else { + memory.stableMinimapLayoutInput = null; + memory.stableFitRemainingWidth = 0; + } } + } + + if (minimapSize === 'fill' || fitBecomesFill) { minimapHeightIsEditorHeight = true; - const configuredFontScale = minimapScale; + const configuredMinimapScale = minimapScale; minimapLineHeight = Math.min(lineHeight * pixelRatio, Math.max(1, Math.floor(1 / desiredRatio))); - minimapScale = Math.min(configuredFontScale + 1, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight))); - if (minimapScale > configuredFontScale) { - minimapWidthMultiplier = Math.min(2, minimapScale / configuredFontScale); + minimapScale = Math.min(maxMinimapScale, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight))); + if (minimapScale > configuredMinimapScale) { + minimapWidthMultiplier = Math.min(2, minimapScale / configuredMinimapScale); } minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier; minimapCanvasInnerHeight = Math.ceil((Math.max(typicalViewportLineCount, viewLineCount + extraLinesBeyondLastLine)) * minimapLineHeight); + if (isViewportWrapping && fitBecomesFill) { + memory.stableFitMaxMinimapScale = minimapScale; + } } } } @@ -2049,7 +2048,6 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption