From 8463cd27b3283fda5eeef2e46889ef958ff9710b Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Tue, 17 May 2022 16:06:59 +0200 Subject: [PATCH] Reduce usages of `editor.deltaDecorations` (#149718) --- .../editor/browser/widget/codeEditorWidget.ts | 4 ++ src/vs/editor/common/editorCommon.ts | 6 ++- .../browser/bracketMatching.ts | 11 ++-- .../colorPicker/browser/colorDetector.ts | 12 ++--- .../browser/colorHoverParticipant.ts | 2 +- .../linkedEditing/browser/linkedEditing.ts | 35 +++++++------ .../browser/unicodeHighlighter.ts | 51 +++++++------------ src/vs/monaco.d.ts | 4 ++ src/vs/workbench/browser/codeeditor.ts | 7 ++- .../browser/breakpointEditorContribution.ts | 25 ++++----- 10 files changed, 79 insertions(+), 78 deletions(-) diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 769e04d8b89..801501376ce 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -2179,6 +2179,10 @@ class EditorDecorationsCollection implements editorCommon.IEditorDecorationsColl return result; } + public has(decoration: IModelDecoration): boolean { + return this._decorationIds.includes(decoration.id); + } + public clear(): void { if (this._decorationIds.length === 0) { // nothing to do diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 8fbe1627bed..4b8f8d58fbc 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -11,7 +11,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; -import { IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness, IValidEditOperation, IModelDeltaDecoration } from 'vs/editor/common/model'; +import { IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness, IValidEditOperation, IModelDeltaDecoration, IModelDecoration } from 'vs/editor/common/model'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { IDimension } from 'vs/editor/common/core/dimension'; import { IModelDecorationsChangedEvent } from 'vs/editor/common/textModelEvents'; @@ -538,6 +538,10 @@ export interface IEditorDecorationsCollection { * Get all ranges for decorations. */ getRanges(): Range[]; + /** + * Determine if a decoration is in this collection. + */ + has(decoration: IModelDecoration): boolean; /** * Replace all previous decorations with `newDecorations`. */ diff --git a/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts index 37b38180c3a..0f917f1b5c6 100644 --- a/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts @@ -13,7 +13,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IEditorContribution, IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; @@ -105,7 +105,7 @@ export class BracketMatchingController extends Disposable implements IEditorCont private _lastBracketsData: BracketsData[]; private _lastVersionId: number; - private _decorations: string[]; + private readonly _decorations: IEditorDecorationsCollection; private readonly _updateBracketsSoon: RunOnceScheduler; private _matchBrackets: 'never' | 'near' | 'always'; @@ -116,7 +116,7 @@ export class BracketMatchingController extends Disposable implements IEditorCont this._editor = editor; this._lastBracketsData = []; this._lastVersionId = 0; - this._decorations = []; + this._decorations = this._editor.createDecorationsCollection(); this._updateBracketsSoon = this._register(new RunOnceScheduler(() => this._updateBrackets(), 50)); this._matchBrackets = this._editor.getOption(EditorOption.matchBrackets); @@ -136,7 +136,6 @@ export class BracketMatchingController extends Disposable implements IEditorCont })); this._register(editor.onDidChangeModel((e) => { this._lastBracketsData = []; - this._decorations = []; this._updateBracketsSoon.schedule(); })); this._register(editor.onDidChangeModelLanguageConfiguration((e) => { @@ -146,7 +145,7 @@ export class BracketMatchingController extends Disposable implements IEditorCont this._register(editor.onDidChangeConfiguration((e) => { if (e.hasChanged(EditorOption.matchBrackets)) { this._matchBrackets = this._editor.getOption(EditorOption.matchBrackets); - this._decorations = this._editor.deltaDecorations(this._decorations, []); + this._decorations.clear(); this._lastBracketsData = []; this._lastVersionId = 0; this._updateBracketsSoon.schedule(); @@ -285,7 +284,7 @@ export class BracketMatchingController extends Disposable implements IEditorCont } } - this._decorations = this._editor.deltaDecorations(this._decorations, newDecorations); + this._decorations.set(newDecorations); } private _recomputeBrackets(): void { diff --git a/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts index 2a312a7c9b8..cf9a2bfcd47 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts @@ -16,7 +16,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { IModelDeltaDecoration } from 'vs/editor/common/model'; +import { IModelDecoration, IModelDeltaDecoration } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; @@ -41,7 +41,7 @@ export class ColorDetector extends Disposable implements IEditorContribution { private _decorationsIds: string[] = []; private _colorDatas = new Map(); - private _colorDecoratorIds: ReadonlySet = new Set(); + private readonly _colorDecoratorIds = this._editor.createDecorationsCollection(); private _isEnabled: boolean; @@ -215,12 +215,12 @@ export class ColorDetector extends Disposable implements IEditorContribution { }); } - this._colorDecoratorIds = new Set(this._editor.deltaDecorations([...this._colorDecoratorIds], decorations)); + this._colorDecoratorIds.set(decorations); } private removeAllDecorations(): void { this._decorationsIds = this._editor.deltaDecorations(this._decorationsIds, []); - this._colorDecoratorIds = new Set(this._editor.deltaDecorations([...this._colorDecoratorIds], [])); + this._colorDecoratorIds.clear(); this._colorDecorationClassRefs.clear(); } @@ -241,8 +241,8 @@ export class ColorDetector extends Disposable implements IEditorContribution { return this._colorDatas.get(decorations[0].id)!; } - isColorDecorationId(decorationId: string): boolean { - return this._colorDecoratorIds.has(decorationId); + isColorDecoration(decoration: IModelDecoration): boolean { + return this._colorDecoratorIds.has(decoration); } } diff --git a/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts b/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts index 406407f5fd4..adb039eecf6 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts @@ -70,7 +70,7 @@ export class ColorHoverParticipant implements IEditorHoverParticipant this.updateRanges(), this._debounceDuration ?? this._debounceInformation.get(model)); }; const rangeSyncScheduler = new Delayer(0); - const triggerRangeSync = (decorations: string[]) => { - this._rangeSyncTriggerPromise = rangeSyncScheduler.trigger(() => this._syncRanges(decorations)); + const triggerRangeSync = (token: number) => { + this._rangeSyncTriggerPromise = rangeSyncScheduler.trigger(() => this._syncRanges(token)); }; this._localToDispose.add(this._editor.onDidChangeCursorPosition(() => { triggerRangeUpdate(); @@ -156,9 +158,9 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont this._localToDispose.add(this._editor.onDidChangeModelContent((e) => { if (!this._ignoreChangeEvent) { if (this._currentDecorations.length > 0) { - const referenceRange = model.getDecorationRange(this._currentDecorations[0]); + const referenceRange = this._currentDecorations.getRange(0); if (referenceRange && e.changes.every(c => referenceRange.intersectRanges(c.range))) { - triggerRangeSync(this._currentDecorations); + triggerRangeSync(this._syncRangesToken); return; } } @@ -174,15 +176,15 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont this.updateRanges(); } - private _syncRanges(decorations: string[]): void { + private _syncRanges(token: number): void { // dalayed invocation, make sure we're still on - if (!this._editor.hasModel() || decorations !== this._currentDecorations || decorations.length === 0) { + if (!this._editor.hasModel() || token !== this._syncRangesToken || this._currentDecorations.length === 0) { // nothing to do return; } const model = this._editor.getModel(); - const referenceRange = model.getDecorationRange(decorations[0]); + const referenceRange = this._currentDecorations.getRange(0); if (!referenceRange || referenceRange.startLineNumber !== referenceRange.endLineNumber) { return this.clearRanges(); @@ -198,8 +200,8 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont } let edits: ISingleEditOperation[] = []; - for (let i = 1, len = decorations.length; i < len; i++) { - const mirrorRange = model.getDecorationRange(decorations[i]); + for (let i = 1, len = this._currentDecorations.length; i < len; i++) { + const mirrorRange = this._currentDecorations.getRange(i); if (!mirrorRange) { continue; } @@ -255,7 +257,7 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont public clearRanges(): void { this._visibleContextKey.set(false); - this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, []); + this._currentDecorations.clear(); if (this._currentRequest) { this._currentRequest.cancel(); this._currentRequest = null; @@ -290,8 +292,8 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont if (position.equals(this._currentRequestPosition)) { return; // same position } - if (this._currentDecorations && this._currentDecorations.length > 0) { - const range = model.getDecorationRange(this._currentDecorations[0]); + if (this._currentDecorations.length > 0) { + const range = this._currentDecorations.getRange(0); if (range && range.containsPosition(position)) { return; // just moving inside the existing primary range } @@ -341,7 +343,8 @@ export class LinkedEditingContribution extends Disposable implements IEditorCont const decorations: IModelDeltaDecoration[] = ranges.map(range => ({ range: range, options: LinkedEditingContribution.DECORATION })); this._visibleContextKey.set(true); - this._currentDecorations = this._editor.deltaDecorations(this._currentDecorations, decorations); + this._currentDecorations.set(decorations); + this._syncRangesToken++; // cancel any pending syncRanges call } catch (err) { if (!isCancellationError(err)) { onUnexpectedError(err); diff --git a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts index 326e6567180..44b682dea38 100644 --- a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts +++ b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts @@ -179,9 +179,9 @@ export class UnicodeHighlighter extends Disposable implements IEditorContributio } } - public getDecorationInfo(decorationId: string): UnicodeHighlighterDecorationInfo | null { + public getDecorationInfo(decoration: IModelDecoration): UnicodeHighlighterDecorationInfo | null { if (this._highlighter) { - return this._highlighter.getDecorationInfo(decorationId); + return this._highlighter.getDecorationInfo(decoration); } return null; } @@ -214,7 +214,7 @@ function resolveOptions(trusted: boolean, options: InternalUnicodeHighlightOptio class DocumentUnicodeHighlighter extends Disposable { private readonly _model: ITextModel = this._editor.getModel(); private readonly _updateSoon: RunOnceScheduler; - private _decorationIds = new Set(); + private _decorations = this._editor.createDecorationsCollection(); constructor( private readonly _editor: IActiveCodeEditor, @@ -233,7 +233,7 @@ class DocumentUnicodeHighlighter extends Disposable { } public override dispose() { - this._decorationIds = new Set(this._model.deltaDecorations(Array.from(this._decorationIds), [])); + this._decorations.clear(); super.dispose(); } @@ -243,7 +243,7 @@ class DocumentUnicodeHighlighter extends Disposable { } if (!this._model.mightContainNonBasicASCII()) { - this._decorationIds = new Set(this._editor.deltaDecorations(Array.from(this._decorationIds), [])); + this._decorations.clear(); return; } @@ -271,31 +271,21 @@ class DocumentUnicodeHighlighter extends Disposable { }); } } - this._decorationIds = new Set(this._editor.deltaDecorations( - Array.from(this._decorationIds), - decorations - )); + this._decorations.set(decorations); }); } - public getDecorationInfo(decorationId: string): UnicodeHighlighterDecorationInfo | null { - if (!this._decorationIds.has(decorationId)) { + public getDecorationInfo(decoration: IModelDecoration): UnicodeHighlighterDecorationInfo | null { + if (!this._decorations.has(decoration)) { return null; } const model = this._editor.getModel(); - const range = model.getDecorationRange(decorationId)!; - const decoration = { - range: range, - options: Decorations.instance.getDecorationFromOptions(this._options), - id: decorationId, - ownerId: 0, - }; if ( !isModelDecorationVisible(model, decoration) ) { return null; } - const text = model.getValueInRange(range); + const text = model.getValueInRange(decoration.range); return { reason: computeReason(text, this._options)!, inComment: isModelDecorationInComment(model, decoration), @@ -308,7 +298,7 @@ class ViewportUnicodeHighlighter extends Disposable { private readonly _model: ITextModel = this._editor.getModel(); private readonly _updateSoon: RunOnceScheduler; - private _decorationIds = new Set(); + private readonly _decorations = this._editor.createDecorationsCollection(); constructor( private readonly _editor: IActiveCodeEditor, @@ -336,7 +326,7 @@ class ViewportUnicodeHighlighter extends Disposable { } public override dispose() { - this._decorationIds = new Set(this._model.deltaDecorations(Array.from(this._decorationIds), [])); + this._decorations.clear(); super.dispose(); } @@ -346,7 +336,7 @@ class ViewportUnicodeHighlighter extends Disposable { } if (!this._model.mightContainNonBasicASCII()) { - this._decorationIds = new Set(this._editor.deltaDecorations(Array.from(this._decorationIds), [])); + this._decorations.clear(); return; } @@ -379,22 +369,15 @@ class ViewportUnicodeHighlighter extends Disposable { } this._updateState(totalResult); - this._decorationIds = new Set(this._editor.deltaDecorations(Array.from(this._decorationIds), decorations)); + this._decorations.set(decorations); } - public getDecorationInfo(decorationId: string): UnicodeHighlighterDecorationInfo | null { - if (!this._decorationIds.has(decorationId)) { + public getDecorationInfo(decoration: IModelDecoration): UnicodeHighlighterDecorationInfo | null { + if (!this._decorations.has(decoration)) { return null; } const model = this._editor.getModel(); - const range = model.getDecorationRange(decorationId)!; - const text = model.getValueInRange(range); - const decoration = { - range: range, - options: Decorations.instance.getDecorationFromOptions(this._options), - id: decorationId, - ownerId: 0, - }; + const text = model.getValueInRange(decoration.range); if (!isModelDecorationVisible(model, decoration)) { return null; } @@ -449,7 +432,7 @@ export class UnicodeHighlighterHoverParticipant implements IEditorHoverParticipa let index = 300; for (const d of lineDecorations) { - const highlightInfo = unicodeHighlighter.getDecorationInfo(d.id); + const highlightInfo = unicodeHighlighter.getDecorationInfo(d); if (!highlightInfo) { continue; } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index d96e306b525..8390e8adca5 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2553,6 +2553,10 @@ declare namespace monaco.editor { * Get all ranges for decorations. */ getRanges(): Range[]; + /** + * Determine if a decoration is in this collection. + */ + has(decoration: IModelDecoration): boolean; /** * Replace all previous decorations with `newDecorations`. */ diff --git a/src/vs/workbench/browser/codeeditor.ts b/src/vs/workbench/browser/codeeditor.ts index bc251d1bda1..0b23958dfed 100644 --- a/src/vs/workbench/browser/codeeditor.ts +++ b/src/vs/workbench/browser/codeeditor.ts @@ -47,8 +47,11 @@ export class RangeHighlightDecorations extends Disposable { } removeHighlightRange() { - if (this.editor?.getModel() && this.rangeHighlightDecorationId) { - this.editor.deltaDecorations([this.rangeHighlightDecorationId], []); + if (this.editor && this.rangeHighlightDecorationId) { + const decorationId = this.rangeHighlightDecorationId; + this.editor.changeDecorations((accessor) => { + accessor.removeDecoration(decorationId); + }); this._onHighlightRemoved.fire(); } diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 4eab6791a7f..943b41f8eb5 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -11,7 +11,7 @@ import severity from 'vs/base/common/severity'; import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions'; import { Range } from 'vs/editor/common/core/range'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IContentWidget, IActiveCodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; -import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, ITextModel, OverviewRulerLane, IModelDecorationOverviewRulerOptions } from 'vs/editor/common/model'; +import { IModelDecorationOptions, TrackedRangeStickiness, ITextModel, OverviewRulerLane, IModelDecorationOverviewRulerOptions } from 'vs/editor/common/model'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -158,7 +158,7 @@ async function createCandidateDecorations(model: ITextModel, breakpointDecoratio export class BreakpointEditorContribution implements IBreakpointEditorContribution { - private breakpointHintDecoration: string[] = []; + private breakpointHintDecoration: string | null = null; private breakpointWidget: BreakpointWidget | undefined; private breakpointWidgetVisible: IContextKey; private toDispose: IDisposable[] = []; @@ -443,20 +443,21 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi } private ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber: number): void { - const newDecoration: IModelDeltaDecoration[] = []; - if (showBreakpointHintAtLineNumber !== -1) { - newDecoration.push({ - options: breakpointHelperDecoration, - range: { + this.editor.changeDecorations((accessor) => { + if (this.breakpointHintDecoration) { + accessor.removeDecoration(this.breakpointHintDecoration); + this.breakpointHintDecoration = null; + } + if (showBreakpointHintAtLineNumber !== -1) { + this.breakpointHintDecoration = accessor.addDecoration({ startLineNumber: showBreakpointHintAtLineNumber, startColumn: 1, endLineNumber: showBreakpointHintAtLineNumber, endColumn: 1 - } - }); - } - - this.breakpointHintDecoration = this.editor.deltaDecorations(this.breakpointHintDecoration, newDecoration); + }, breakpointHelperDecoration + ); + } + }); } private async setDecorations(): Promise {