Skip to content

Commit 080c694

Browse files
authored
Refactoring sticky scroll widget file (microsoft#251218)
* refactoring sticky scroll widget file * wip * polish * wip
1 parent 8def6db commit 080c694

File tree

2 files changed

+77
-82
lines changed

2 files changed

+77
-82
lines changed

src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
box-shadow: var(--vscode-editorStickyScroll-shadow) 0 4px 2px -2px;
1111
z-index: 4;
1212
right: initial !important;
13+
margin-left: '0px';
1314
}
1415

1516
.monaco-editor .sticky-widget .sticky-widget-line-numbers {

src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts

Lines changed: 76 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { CharacterMapping, RenderLineInput, renderViewLine } from '../../../comm
2020
import { foldingCollapsedIcon, foldingExpandedIcon } from '../../folding/browser/foldingDecorations.js';
2121
import { FoldingModel } from '../../folding/browser/foldingModel.js';
2222
import { Emitter } from '../../../../base/common/event.js';
23+
import { IViewModel } from '../../../common/viewModel.js';
2324

2425
export class StickyScrollWidgetState {
2526
constructor(
@@ -58,7 +59,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
5859

5960
private readonly _editor: ICodeEditor;
6061

61-
private _previousState: StickyScrollWidgetState | undefined;
62+
private _state: StickyScrollWidgetState | undefined;
6263
private _lineHeight: number;
6364
private _renderedStickyLines: RenderedStickyLine[] = [];
6465
private _lineNumbers: number[] = [];
@@ -142,49 +143,48 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
142143
return this._lineNumbers;
143144
}
144145

145-
setState(_state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | undefined, _rebuildFromLine?: number): void {
146-
if (_rebuildFromLine === undefined &&
147-
((!this._previousState && !_state) || (this._previousState && this._previousState.equals(_state)))
148-
) {
146+
setState(state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | undefined, rebuildFromIndexCandidate?: number): void {
147+
const currentStateAndPreviousStateUndefined = !this._state && !state;
148+
const currentStateDefinedAndEqualsPreviousState = this._state && this._state.equals(state);
149+
if (currentStateAndPreviousStateUndefined || currentStateDefinedAndEqualsPreviousState) {
149150
return;
150151
}
151-
const isWidgetHeightZero = this._isWidgetHeightZero(_state);
152-
const state = isWidgetHeightZero ? undefined : _state;
153-
const rebuildFromLine = isWidgetHeightZero ? 0 : this._findLineToRebuildWidgetFrom(_state, _rebuildFromLine);
154-
155-
this._renderRootNode(state, foldingModel, rebuildFromLine);
156-
this._previousState = _state;
152+
const data = this._findRenderingData(state);
153+
const previousLineNumbers = this._lineNumbers;
154+
this._lineNumbers = data.lineNumbers;
155+
this._lastLineRelativePosition = data.lastLineRelativePosition;
156+
const rebuildFromIndex = this._findIndexToRebuildFrom(previousLineNumbers, this._lineNumbers, rebuildFromIndexCandidate);
157+
this._renderRootNode(this._lineNumbers, this._lastLineRelativePosition, foldingModel, rebuildFromIndex);
158+
this._state = state;
157159
}
158160

159-
private _isWidgetHeightZero(state: StickyScrollWidgetState | undefined): boolean {
161+
private _findRenderingData(state: StickyScrollWidgetState | undefined): { lineNumbers: number[]; lastLineRelativePosition: number } {
160162
if (!state) {
161-
return true;
163+
return { lineNumbers: [], lastLineRelativePosition: 0 };
162164
}
163-
const futureWidgetHeight = this._getHeightOfLines(state.startLineNumbers, state.lastLineRelativePosition);
164-
if (futureWidgetHeight > 0) {
165-
this._lastLineRelativePosition = state.lastLineRelativePosition;
166-
const lineNumbers = [...state.startLineNumbers];
167-
if (state.showEndForLine !== null) {
168-
lineNumbers[state.showEndForLine] = state.endLineNumbers[state.showEndForLine];
169-
}
170-
this._lineNumbers = lineNumbers;
171-
} else {
172-
this._lastLineRelativePosition = 0;
173-
this._lineNumbers = [];
165+
const candidateLineNumbers = [...state.startLineNumbers];
166+
if (state.showEndForLine !== null) {
167+
candidateLineNumbers[state.showEndForLine] = state.endLineNumbers[state.showEndForLine];
168+
}
169+
let totalHeight = 0;
170+
for (let i = 0; i < candidateLineNumbers.length; i++) {
171+
totalHeight += this._editor.getLineHeightForPosition(new Position(candidateLineNumbers[i], 1));
174172
}
175-
return futureWidgetHeight === 0;
173+
if (totalHeight === 0) {
174+
return { lineNumbers: [], lastLineRelativePosition: 0 };
175+
}
176+
return { lineNumbers: candidateLineNumbers, lastLineRelativePosition: state.lastLineRelativePosition };
176177
}
177178

178-
private _findLineToRebuildWidgetFrom(state: StickyScrollWidgetState | undefined, _rebuildFromLine?: number): number {
179-
if (!state || !this._previousState) {
179+
private _findIndexToRebuildFrom(previousLineNumbers: number[], newLineNumbers: number[], rebuildFromIndexCandidate?: number): number {
180+
if (newLineNumbers.length === 0) {
180181
return 0;
181182
}
182-
if (_rebuildFromLine !== undefined) {
183-
return _rebuildFromLine;
183+
if (rebuildFromIndexCandidate !== undefined) {
184+
return rebuildFromIndexCandidate;
184185
}
185-
const previousState = this._previousState;
186-
const indexOfLinesAlreadyRendered = state.startLineNumbers.findIndex(startLineNumber => !previousState.startLineNumbers.includes(startLineNumber));
187-
return (indexOfLinesAlreadyRendered === -1) ? 0 : indexOfLinesAlreadyRendered;
186+
const validIndex = newLineNumbers.findIndex(startLineNumber => !previousLineNumbers.includes(startLineNumber));
187+
return validIndex === -1 ? 0 : validIndex;
188188
}
189189

190190
private _updateWidgetWidth(): void {
@@ -195,18 +195,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
195195
this._rootDomNode.style.width = `${layoutInfo.width - layoutInfo.verticalScrollbarWidth}px`;
196196
}
197197

198-
private _clearStickyLinesFromLine(clearFromLine: number) {
199-
this._foldingIconStore.clear();
200-
// Removing only the lines that need to be rerendered
201-
for (let i = clearFromLine; i < this._renderedStickyLines.length; i++) {
202-
const stickyLine = this._renderedStickyLines[i];
203-
stickyLine.lineNumberDomNode.remove();
204-
stickyLine.lineDomNode.remove();
205-
}
206-
// Keep the lines that need to be updated
207-
this._renderedStickyLines = this._renderedStickyLines.slice(0, clearFromLine);
208-
}
209-
210198
private _useFoldingOpacityTransition(requireTransitions: boolean) {
211199
this._lineNumbersDomNode.style.setProperty('--vscode-editorStickyScroll-foldingOpacityTransition', `opacity ${requireTransitions ? 0.5 : 0}s`);
212200
}
@@ -221,51 +209,55 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
221209
}
222210
}
223211

224-
private async _renderRootNode(state: StickyScrollWidgetState | undefined, foldingModel: FoldingModel | undefined, rebuildFromLine: number): Promise<void> {
225-
this._clearStickyLinesFromLine(rebuildFromLine);
226-
if (!state) {
227-
// make sure the dom is 0 height and display:none
228-
this._setHeight(0);
212+
private async _renderRootNode(lineNumbers: number[], lastLineRelativePosition: number, foldingModel: FoldingModel | undefined, rebuildFromIndex: number): Promise<void> {
213+
const viewModel = this._editor._getViewModel();
214+
if (!viewModel) {
215+
this._clearWidget();
216+
return;
217+
}
218+
if (lineNumbers.length === 0) {
219+
this._clearWidget();
229220
return;
230221
}
222+
const renderedStickyLines: RenderedStickyLine[] = [];
223+
const lastLineNumber = lineNumbers[lineNumbers.length - 1];
231224
let top: number = 0;
232-
// For existing sticky lines update the top and z-index
233-
for (const stickyLine of this._renderedStickyLines) {
234-
this._updatePosition(stickyLine, top);
235-
top += stickyLine.height;
225+
for (let i = 0; i < this._renderedStickyLines.length; i++) {
226+
if (i < rebuildFromIndex) {
227+
const renderedLine = this._renderedStickyLines[i];
228+
renderedStickyLines.push(this._updatePosition(renderedLine, top, renderedLine.lineNumber === lastLineNumber));
229+
top += renderedLine.height;
230+
} else {
231+
const renderedLine = this._renderedStickyLines[i];
232+
renderedLine.lineNumberDomNode.remove();
233+
renderedLine.lineDomNode.remove();
234+
}
236235
}
237-
// For new sticky lines
238236
const layoutInfo = this._editor.getLayoutInfo();
239-
const linesToRender = this._lineNumbers.slice(rebuildFromLine);
240-
for (const [index, line] of linesToRender.entries()) {
241-
const stickyLine = this._renderChildNode(index + rebuildFromLine, line, top, foldingModel, layoutInfo);
242-
if (!stickyLine) {
243-
continue;
244-
}
237+
for (let i = rebuildFromIndex; i < lineNumbers.length; i++) {
238+
const stickyLine = this._renderChildNode(viewModel, i, lineNumbers[i], top, lastLineNumber === lineNumbers[i], foldingModel, layoutInfo);
245239
top += stickyLine.height;
246240
this._linesDomNode.appendChild(stickyLine.lineDomNode);
247241
this._lineNumbersDomNode.appendChild(stickyLine.lineNumberDomNode);
248-
this._renderedStickyLines.push(stickyLine);
242+
renderedStickyLines.push(stickyLine);
249243
}
250244
if (foldingModel) {
251245
this._setFoldingHoverListeners();
252246
this._useFoldingOpacityTransition(!this._isOnGlyphMargin);
253247
}
254-
255-
const widgetHeight = top + this._lastLineRelativePosition;
256-
this._setHeight(widgetHeight);
257-
258-
this._rootDomNode.style.marginLeft = '0px';
259248
this._minContentWidthInPx = Math.max(...this._renderedStickyLines.map(l => l.scrollWidth)) + layoutInfo.verticalScrollbarWidth;
249+
this._renderedStickyLines = renderedStickyLines;
250+
this._setHeight(top + lastLineRelativePosition);
260251
this._editor.layoutOverlayWidget(this);
261252
}
262253

263-
private _getHeightOfLines(lineNumbers: number[], lastLineRelativePosition: number): number {
264-
let totalHeight = 0;
265-
for (let i = 0; i < lineNumbers.length; i++) {
266-
totalHeight += this._editor.getLineHeightForPosition(new Position(lineNumbers[i], 1));
254+
private _clearWidget(): void {
255+
for (let i = 0; i < this._renderedStickyLines.length; i++) {
256+
const stickyLine = this._renderedStickyLines[i];
257+
stickyLine.lineNumberDomNode.remove();
258+
stickyLine.lineDomNode.remove();
267259
}
268-
return totalHeight + lastLineRelativePosition;
260+
this._setHeight(0);
269261
}
270262

271263
private _setHeight(height: number): void {
@@ -302,11 +294,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
302294
}));
303295
}
304296

305-
private _renderChildNode(index: number, line: number, top: number, foldingModel: FoldingModel | undefined, layoutInfo: EditorLayoutInfo): RenderedStickyLine | undefined {
306-
const viewModel = this._editor._getViewModel();
307-
if (!viewModel) {
308-
return;
309-
}
297+
private _renderChildNode(viewModel: IViewModel, index: number, line: number, top: number, isLastLine: boolean, foldingModel: FoldingModel | undefined, layoutInfo: EditorLayoutInfo): RenderedStickyLine {
310298
const viewLineNumber = viewModel.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber;
311299
const lineRenderingData = viewModel.getViewLineRenderingData(viewLineNumber);
312300
const lineNumberOption = this._editor.getOption(EditorOption.lineNumbers);
@@ -376,21 +364,27 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
376364
this._editor.applyFontInfo(lineHTMLNode);
377365
this._editor.applyFontInfo(lineNumberHTMLNode);
378366

379-
380367
lineNumberHTMLNode.style.lineHeight = `${lineHeight}px`;
381368
lineHTMLNode.style.lineHeight = `${lineHeight}px`;
382369
lineNumberHTMLNode.style.height = `${lineHeight}px`;
383370
lineHTMLNode.style.height = `${lineHeight}px`;
384371

385-
const renderedLine = new RenderedStickyLine(index, line, lineHTMLNode, lineNumberHTMLNode, foldingIcon, renderOutput.characterMapping, lineHTMLNode.scrollWidth, lineHeight);
386-
return this._updatePosition(renderedLine, top);
372+
const renderedLine = new RenderedStickyLine(
373+
index,
374+
line,
375+
lineHTMLNode,
376+
lineNumberHTMLNode,
377+
foldingIcon,
378+
renderOutput.characterMapping,
379+
lineHTMLNode.scrollWidth,
380+
lineHeight
381+
);
382+
return this._updatePosition(renderedLine, top, isLastLine);
387383
}
388384

389-
private _updatePosition(stickyLine: RenderedStickyLine, top: number): RenderedStickyLine {
390-
const index = stickyLine.index;
385+
private _updatePosition(stickyLine: RenderedStickyLine, top: number, isLastLine: boolean): RenderedStickyLine {
391386
const lineHTMLNode = stickyLine.lineDomNode;
392387
const lineNumberHTMLNode = stickyLine.lineNumberDomNode;
393-
const isLastLine = index === this._lineNumbers.length - 1;
394388
if (isLastLine) {
395389
const zIndex = '0';
396390
lineHTMLNode.style.zIndex = zIndex;

0 commit comments

Comments
 (0)