Path: blob/main/src/vs/workbench/contrib/editTelemetry/browser/telemetry/editTracker.ts
5251 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/456import { Disposable } from '../../../../../base/common/lifecycle.js';7import { observableSignal, runOnChange, IReader } from '../../../../../base/common/observable.js';8import { AnnotatedStringEdit } from '../../../../../editor/common/core/edits/stringEdit.js';9import { OffsetRange } from '../../../../../editor/common/core/ranges/offsetRange.js';10import { TextModelEditSource } from '../../../../../editor/common/textModelEditSource.js';11import { IDocumentWithAnnotatedEdits, EditKeySourceData, EditSource } from '../helpers/documentWithAnnotatedEdits.js';1213/**14* Tracks a single document.15*/16export class DocumentEditSourceTracker<T = void> extends Disposable {17private _edits: AnnotatedStringEdit<EditKeySourceData> = AnnotatedStringEdit.empty;18private _pendingExternalEdits: AnnotatedStringEdit<EditKeySourceData> = AnnotatedStringEdit.empty;1920private readonly _update = observableSignal(this);21private readonly _representativePerKey: Map<string, TextModelEditSource> = new Map();22private readonly _sumAddedCharactersPerKey: Map</* key */string, number> = new Map();2324constructor(25private readonly _doc: IDocumentWithAnnotatedEdits,26public readonly data: T,27) {28super();2930this._register(runOnChange(this._doc.value, (_val, _prevVal, edits) => {31const eComposed = AnnotatedStringEdit.compose(edits.map(e => e.edit));32if (eComposed.replacements.every(e => e.data.source.category === 'external')) {33if (this._edits.isEmpty()) {34// Ignore initial external edits35} else {36// queue pending external edits37this._pendingExternalEdits = this._pendingExternalEdits.compose(eComposed);38}39} else {40if (!this._pendingExternalEdits.isEmpty()) {41this._applyEdit(this._pendingExternalEdits);42this._pendingExternalEdits = AnnotatedStringEdit.empty;43}44this._applyEdit(eComposed);45}4647this._update.trigger(undefined);48}));49}5051private _applyEdit(e: AnnotatedStringEdit<EditKeySourceData>): void {52for (const r of e.replacements) {53let existing = this._sumAddedCharactersPerKey.get(r.data.key);54if (existing === undefined) {55existing = 0;56this._representativePerKey.set(r.data.key, r.data.representative);57}58const newCount = existing + r.getNewLength();59this._sumAddedCharactersPerKey.set(r.data.key, newCount);60}6162this._edits = this._edits.compose(e);63}6465async waitForQueue(): Promise<void> {66await this._doc.waitForQueue();67}6869public getTotalInsertedCharactersCount(key: string): number {70const val = this._sumAddedCharactersPerKey.get(key);71return val ?? 0;72}7374public getAllKeys(): string[] {75return Array.from(this._sumAddedCharactersPerKey.keys());76}7778public getRepresentative(key: string): TextModelEditSource | undefined {79return this._representativePerKey.get(key);80}8182public getTrackedRanges(reader?: IReader): TrackedEdit[] {83this._update.read(reader);84const ranges = this._edits.getNewRanges();85return ranges.map((r, idx) => {86const e = this._edits.replacements[idx];87const te = new TrackedEdit(e.replaceRange, r, e.data.key, e.data.source, e.data.representative);88return te;89});90}9192public isEmpty(): boolean {93return this._edits.isEmpty();94}9596public _getDebugVisualization() {97const ranges = this.getTrackedRanges();98const txt = this._doc.value.get().value;99100return {101...{ $fileExtension: 'text.w' },102'value': txt,103'decorations': ranges.map(r => {104return {105range: [r.range.start, r.range.endExclusive],106color: r.source.getColor(),107};108})109};110}111}112113export class TrackedEdit {114constructor(115public readonly originalRange: OffsetRange,116public readonly range: OffsetRange,117public readonly sourceKey: string,118public readonly source: EditSource,119public readonly sourceRepresentative: TextModelEditSource,120) { }121}122123124