mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
Merge pull request #282537 from microsoft/benibenj/sacred-herring
Fix freeze issue in ghost text rendering
This commit is contained in:
@@ -28,7 +28,7 @@ export class LineDecoration {
|
||||
);
|
||||
}
|
||||
|
||||
public static equalsArr(a: LineDecoration[], b: LineDecoration[]): boolean {
|
||||
public static equalsArr(a: readonly LineDecoration[], b: readonly LineDecoration[]): boolean {
|
||||
const aLen = a.length;
|
||||
const bLen = b.length;
|
||||
if (aLen !== bLen) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { renderIcon } from '../../../../../../base/browser/ui/iconLabel/iconLabe
|
||||
import { Codicon } from '../../../../../../base/common/codicons.js';
|
||||
import { Emitter, Event } from '../../../../../../base/common/event.js';
|
||||
import { Disposable, DisposableStore, MutableDisposable, toDisposable } from '../../../../../../base/common/lifecycle.js';
|
||||
import { IObservable, autorun, autorunWithStore, constObservable, derived, observableSignalFromEvent, observableValue } from '../../../../../../base/common/observable.js';
|
||||
import { IObservable, autorun, autorunWithStore, constObservable, derived, derivedOpts, observableSignalFromEvent, observableValue } from '../../../../../../base/common/observable.js';
|
||||
import * as strings from '../../../../../../base/common/strings.js';
|
||||
import { applyFontInfo } from '../../../../../browser/config/domFontInfo.js';
|
||||
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidgetPosition, IViewZoneChangeAccessor, MouseTargetType } from '../../../../../browser/editorBrowser.js';
|
||||
@@ -34,7 +34,8 @@ import { CodeEditorWidget } from '../../../../../browser/widget/codeEditor/codeE
|
||||
import { TokenWithTextArray } from '../../../../../common/tokens/tokenWithTextArray.js';
|
||||
import { InlineCompletionViewData } from '../inlineEdits/inlineEditsViewInterface.js';
|
||||
import { InlineDecorationType } from '../../../../../common/viewModel/inlineDecorations.js';
|
||||
import { sum } from '../../../../../../base/common/arrays.js';
|
||||
import { equals, sum } from '../../../../../../base/common/arrays.js';
|
||||
import { equalsIfDefined, IEquatable, itemEquals } from '../../../../../../base/common/equals.js';
|
||||
|
||||
export interface IGhostTextWidgetData {
|
||||
readonly ghostText: GhostText | GhostTextReplacement;
|
||||
@@ -102,14 +103,14 @@ export class GhostTextView extends Disposable {
|
||||
this._additionalLinesWidget = this._register(
|
||||
new AdditionalLinesWidget(
|
||||
this._editor,
|
||||
derived(reader => {
|
||||
derivedOpts({ owner: this, equalsFn: equalsIfDefined(itemEquals()) }, reader => {
|
||||
/** @description lines */
|
||||
const uiState = this._state.read(reader);
|
||||
return uiState ? {
|
||||
lineNumber: uiState.lineNumber,
|
||||
additionalLines: uiState.additionalLines,
|
||||
minReservedLineCount: uiState.additionalReservedLineCount,
|
||||
} : undefined;
|
||||
return uiState ? new AdditionalLinesData(
|
||||
uiState.lineNumber,
|
||||
uiState.additionalLines,
|
||||
uiState.additionalReservedLineCount,
|
||||
) : undefined;
|
||||
}),
|
||||
this._shouldKeepCursorStable,
|
||||
this._isClickable
|
||||
@@ -243,10 +244,10 @@ export class GhostTextView extends Disposable {
|
||||
const existingContent = t.slice(additionalLinesOriginalSuffix.columnRange.toZeroBasedOffsetRange());
|
||||
content = TokenWithTextArray.fromLineTokens(content).append(existingContent).toLineTokens(content.languageIdCodec);
|
||||
}
|
||||
return {
|
||||
return new LineData(
|
||||
content,
|
||||
decorations: l.decorations,
|
||||
};
|
||||
l.decorations,
|
||||
);
|
||||
});
|
||||
|
||||
const cursorColumn = this._editor.getSelection()?.getStartPosition().column!;
|
||||
@@ -420,6 +421,24 @@ function computeGhostTextViewData(ghostText: GhostText | GhostTextReplacement, t
|
||||
};
|
||||
}
|
||||
|
||||
class AdditionalLinesData implements IEquatable<AdditionalLinesData> {
|
||||
constructor(
|
||||
public readonly lineNumber: number,
|
||||
public readonly additionalLines: readonly LineData[],
|
||||
public readonly minReservedLineCount: number,
|
||||
) { }
|
||||
|
||||
equals(other: AdditionalLinesData): boolean {
|
||||
if (this.lineNumber !== other.lineNumber) {
|
||||
return false;
|
||||
}
|
||||
if (this.minReservedLineCount !== other.minReservedLineCount) {
|
||||
return false;
|
||||
}
|
||||
return equals(this.additionalLines, other.additionalLines, itemEquals());
|
||||
}
|
||||
}
|
||||
|
||||
export class AdditionalLinesWidget extends Disposable {
|
||||
private _viewZoneInfo: { viewZoneId: string; heightInLines: number; lineNumber: number } | undefined;
|
||||
public get viewZoneId(): string | undefined { return this._viewZoneInfo?.viewZoneId; }
|
||||
@@ -440,11 +459,7 @@ export class AdditionalLinesWidget extends Disposable {
|
||||
|
||||
constructor(
|
||||
private readonly _editor: ICodeEditor,
|
||||
private readonly _lines: IObservable<{
|
||||
lineNumber: number;
|
||||
additionalLines: LineData[];
|
||||
minReservedLineCount: number;
|
||||
} | undefined>,
|
||||
private readonly _lines: IObservable<AdditionalLinesData | undefined>,
|
||||
private readonly _shouldKeepCursorStable: boolean,
|
||||
private readonly _isClickable: boolean,
|
||||
) {
|
||||
@@ -500,7 +515,7 @@ export class AdditionalLinesWidget extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
private updateLines(lineNumber: number, additionalLines: LineData[], minReservedLineCount: number): void {
|
||||
private updateLines(lineNumber: number, additionalLines: readonly LineData[], minReservedLineCount: number): void {
|
||||
const textModel = this._editor.getModel();
|
||||
if (!textModel) {
|
||||
return;
|
||||
@@ -581,12 +596,21 @@ function isTargetGhostText(target: EventTarget | null): boolean {
|
||||
return isHTMLElement(target) && target.classList.contains(GHOST_TEXT_CLASS_NAME);
|
||||
}
|
||||
|
||||
export interface LineData {
|
||||
content: LineTokens; // Must not contain a linebreak!
|
||||
decorations: LineDecoration[];
|
||||
export class LineData implements IEquatable<LineData> {
|
||||
constructor(
|
||||
public readonly content: LineTokens, // Must not contain a linebreak!
|
||||
public readonly decorations: readonly LineDecoration[]
|
||||
) { }
|
||||
|
||||
equals(other: LineData): boolean {
|
||||
if (!this.content.equals(other.content)) {
|
||||
return false;
|
||||
}
|
||||
return LineDecoration.equalsArr(this.decorations, other.decorations);
|
||||
}
|
||||
}
|
||||
|
||||
function renderLines(domNode: HTMLElement, tabSize: number, lines: LineData[], opts: IComputedEditorOptions, isClickable: boolean): void {
|
||||
function renderLines(domNode: HTMLElement, tabSize: number, lines: readonly LineData[], opts: IComputedEditorOptions, isClickable: boolean): void {
|
||||
const disableMonospaceOptimizations = opts.get(EditorOption.disableMonospaceOptimizations);
|
||||
const stopRenderingLineAfter = opts.get(EditorOption.stopRenderingLineAfter);
|
||||
// To avoid visual confusion, we don't want to render visible whitespace
|
||||
@@ -625,7 +649,7 @@ function renderLines(domNode: HTMLElement, tabSize: number, lines: LineData[], o
|
||||
containsRTL,
|
||||
0,
|
||||
lineTokens,
|
||||
lineData.decorations,
|
||||
lineData.decorations.slice(),
|
||||
tabSize,
|
||||
0,
|
||||
fontInfo.spaceWidth,
|
||||
|
||||
Reference in New Issue
Block a user