diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 0f32c575cb4dc..5b36951fc7ac7 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -438,7 +438,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib if (lineNumberOption.renderType === RenderLineNumbersType.Relative) { this._sessionStore.add(this._editor.onDidChangeCursorPosition(() => { this._showEndForLine = null; - this._renderStickyScroll(-1); + this._renderStickyScroll(0); })); } } @@ -457,8 +457,8 @@ export class StickyScrollController extends Disposable implements IEditorContrib private _onTokensChange(event: IModelTokensChangedEvent) { if (this._needsUpdate(event)) { - // Rebuilding the whole widget from line -1 - this._renderStickyScroll(-1); + // Rebuilding the whole widget from line 0 + this._renderStickyScroll(0); } } @@ -469,17 +469,23 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._maxStickyLines = Math.round(theoreticalLines * .25); } - private async _renderStickyScroll(rebuildFromLine: number = Infinity) { + private async _renderStickyScroll(_rebuildFromLine?: number) { const model = this._editor.getModel(); if (!model || model.isTooLargeForTokenization()) { this._foldingModel = null; - this._stickyScrollWidget.setState(undefined, null, rebuildFromLine); + this._stickyScrollWidget.setState(undefined, null); return; } const stickyLineVersion = this._stickyLineCandidateProvider.getVersionId(); if (stickyLineVersion === undefined || stickyLineVersion === model.getVersionId()) { this._foldingModel = await FoldingController.get(this._editor)?.getFoldingModel() ?? null; + const previousWidgetState = this._widgetState; this._widgetState = this.findScrollWidgetState(); + let rebuildFromLine = _rebuildFromLine; + if (rebuildFromLine === undefined) { + const _rebuildFromLine = this._widgetState.startLineNumbers.findIndex(startLineNumber => !previousWidgetState.startLineNumbers.includes(startLineNumber)); + rebuildFromLine = _rebuildFromLine === -1 ? 0 : _rebuildFromLine; + } this._stickyScrollVisibleContextKey.set(!(this._widgetState.startLineNumbers.length === 0)); if (!this._focused) { diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 46dd3875f8965..8e3abcec96c25 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -126,14 +126,13 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return this._lineNumbers; } - setState(state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | null, rebuildFromLine: number = Infinity): void { - if (((!this._previousState && !state) || (this._previousState && this._previousState.equals(state))) - && rebuildFromLine === Infinity) { + setState(state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | null, rebuildFromLine: number = 0): void { + if ((!this._previousState && !state) || (this._previousState && this._previousState.equals(state))) { return; } this._previousState = state; const previousStickyLines = this._stickyLines; - this._clearStickyWidget(); + this._clearStickyWidget(rebuildFromLine); if (!state || !this._editor._getViewModel()) { return; } @@ -161,11 +160,16 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._rootDomNode.style.width = `${layoutInfo.width - layoutInfo.verticalScrollbarWidth}px`; } - private _clearStickyWidget() { - this._stickyLines = []; + private _clearStickyWidget(clearFromLine: number) { this._foldingIconStore.clear(); - dom.clearNode(this._lineNumbersDomNode); - dom.clearNode(this._linesDomNode); + // Removing only the lines that need to be rerendered + for (let i = clearFromLine; i < this._stickyLines.length; i++) { + const stickyLine = this._stickyLines[i]; + stickyLine.lineNumberDomNode.remove(); + stickyLine.lineDomNode.remove(); + } + // Keep the lines that need to be updated + this._stickyLines = []; this._rootDomNode.style.display = 'none'; } @@ -183,14 +187,19 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } } - private async _renderRootNode(previousStickyLines: RenderedStickyLine[], foldingModel: FoldingModel | null, rebuildFromLine: number = Infinity): Promise { + private async _renderRootNode(previousStickyLines: RenderedStickyLine[], foldingModel: FoldingModel | null, rebuildFromLine: number = 0): Promise { const layoutInfo = this._editor.getLayoutInfo(); - for (const [index, line] of this._lineNumbers.entries()) { - const previousStickyLine = previousStickyLines[index]; - const stickyLine = (line >= rebuildFromLine || previousStickyLine?.lineNumber !== line) - ? this._renderChildNode(index, line, foldingModel, layoutInfo) - : this._updateTopAndZIndexOfStickyLine(previousStickyLine); + // For existing sticky lines update the top and z-index + const linesToUpdate = previousStickyLines.slice(0, rebuildFromLine); + for (const stickyLine of linesToUpdate) { + this._updateTopAndZIndexOfStickyLine(stickyLine); + this._stickyLines.push(stickyLine); + } + // For new sticky lines + const linesToRender = this._lineNumbers.slice(rebuildFromLine); + for (const [index, line] of linesToRender.entries()) { + const stickyLine = this._renderChildNode(index + rebuildFromLine, line, foldingModel, layoutInfo); if (!stickyLine) { continue; } @@ -205,7 +214,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const widgetHeight: number = this._lineNumbers.length * this._lineHeight + this._lastLineRelativePosition; if (widgetHeight === 0) { - this._clearStickyWidget(); + this._clearStickyWidget(0); return; } this._rootDomNode.style.display = 'block'; @@ -214,7 +223,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._rootDomNode.style.height = `${widgetHeight}px`; this._rootDomNode.style.marginLeft = '0px'; - this._updateMinContentWidth(); + this._minContentWidthInPx = Math.max(...this._stickyLines.map(l => l.scrollWidth)) + this._editor.getLayoutInfo().verticalScrollbarWidth; this._editor.layoutOverlayWidget(this); } @@ -223,7 +232,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (showFoldingControls !== 'mouseover') { return; } - this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_ENTER, (e) => { + this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_ENTER, () => { this._isOnGlyphMargin = true; this._setFoldingIconsVisibility(true); })); @@ -231,7 +240,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._isOnGlyphMargin = false; this._useFoldingOpacityTransition(true); this._setFoldingIconsVisibility(false); - })); } @@ -241,7 +249,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return; } const viewLineNumber = viewModel.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; - const lineRenderingData = viewModel!.getViewLineRenderingData(viewLineNumber); + const lineRenderingData = viewModel.getViewLineRenderingData(viewLineNumber); const lineNumberOption = this._editor.getOption(EditorOption.lineNumbers); let actualInlineDecorations: LineDecoration[]; @@ -264,7 +272,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { let newLine; if (_ttPolicy) { - newLine = _ttPolicy.createHTML(sb.build() as string); + newLine = _ttPolicy.createHTML(sb.build()); } else { newLine = sb.build(); } @@ -313,7 +321,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { lineNumberHTMLNode.style.height = `${this._lineHeight}px`; lineHTMLNode.style.height = `${this._lineHeight}px`; - const renderedLine = new RenderedStickyLine(index, line, lineHTMLNode, lineNumberHTMLNode, foldingIcon, renderOutput.characterMapping); + const renderedLine = new RenderedStickyLine(index, line, lineHTMLNode, lineNumberHTMLNode, foldingIcon, renderOutput.characterMapping, lineHTMLNode.scrollWidth); return this._updateTopAndZIndexOfStickyLine(renderedLine); } @@ -354,16 +362,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return foldingIcon; } - private _updateMinContentWidth() { - this._minContentWidthInPx = 0; - for (const stickyLine of this._stickyLines) { - if (stickyLine.lineDomNode.scrollWidth > this._minContentWidthInPx) { - this._minContentWidthInPx = stickyLine.lineDomNode.scrollWidth; - } - } - this._minContentWidthInPx += this._editor.getLayoutInfo().verticalScrollbarWidth; - } - getId(): string { return 'editor.contrib.stickyScrollWidget'; } @@ -466,7 +464,8 @@ class RenderedStickyLine { public readonly lineDomNode: HTMLElement, public readonly lineNumberDomNode: HTMLElement, public readonly foldingIcon: StickyFoldingIcon | undefined, - public readonly characterMapping: CharacterMapping + public readonly characterMapping: CharacterMapping, + public readonly scrollWidth: number, ) { } }