Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/editTelemetry/browser/telemetry/editTracker.ts
5251 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
7
import { Disposable } from '../../../../../base/common/lifecycle.js';
8
import { observableSignal, runOnChange, IReader } from '../../../../../base/common/observable.js';
9
import { AnnotatedStringEdit } from '../../../../../editor/common/core/edits/stringEdit.js';
10
import { OffsetRange } from '../../../../../editor/common/core/ranges/offsetRange.js';
11
import { TextModelEditSource } from '../../../../../editor/common/textModelEditSource.js';
12
import { IDocumentWithAnnotatedEdits, EditKeySourceData, EditSource } from '../helpers/documentWithAnnotatedEdits.js';
13
14
/**
15
* Tracks a single document.
16
*/
17
export class DocumentEditSourceTracker<T = void> extends Disposable {
18
private _edits: AnnotatedStringEdit<EditKeySourceData> = AnnotatedStringEdit.empty;
19
private _pendingExternalEdits: AnnotatedStringEdit<EditKeySourceData> = AnnotatedStringEdit.empty;
20
21
private readonly _update = observableSignal(this);
22
private readonly _representativePerKey: Map<string, TextModelEditSource> = new Map();
23
private readonly _sumAddedCharactersPerKey: Map</* key */string, number> = new Map();
24
25
constructor(
26
private readonly _doc: IDocumentWithAnnotatedEdits,
27
public readonly data: T,
28
) {
29
super();
30
31
this._register(runOnChange(this._doc.value, (_val, _prevVal, edits) => {
32
const eComposed = AnnotatedStringEdit.compose(edits.map(e => e.edit));
33
if (eComposed.replacements.every(e => e.data.source.category === 'external')) {
34
if (this._edits.isEmpty()) {
35
// Ignore initial external edits
36
} else {
37
// queue pending external edits
38
this._pendingExternalEdits = this._pendingExternalEdits.compose(eComposed);
39
}
40
} else {
41
if (!this._pendingExternalEdits.isEmpty()) {
42
this._applyEdit(this._pendingExternalEdits);
43
this._pendingExternalEdits = AnnotatedStringEdit.empty;
44
}
45
this._applyEdit(eComposed);
46
}
47
48
this._update.trigger(undefined);
49
}));
50
}
51
52
private _applyEdit(e: AnnotatedStringEdit<EditKeySourceData>): void {
53
for (const r of e.replacements) {
54
let existing = this._sumAddedCharactersPerKey.get(r.data.key);
55
if (existing === undefined) {
56
existing = 0;
57
this._representativePerKey.set(r.data.key, r.data.representative);
58
}
59
const newCount = existing + r.getNewLength();
60
this._sumAddedCharactersPerKey.set(r.data.key, newCount);
61
}
62
63
this._edits = this._edits.compose(e);
64
}
65
66
async waitForQueue(): Promise<void> {
67
await this._doc.waitForQueue();
68
}
69
70
public getTotalInsertedCharactersCount(key: string): number {
71
const val = this._sumAddedCharactersPerKey.get(key);
72
return val ?? 0;
73
}
74
75
public getAllKeys(): string[] {
76
return Array.from(this._sumAddedCharactersPerKey.keys());
77
}
78
79
public getRepresentative(key: string): TextModelEditSource | undefined {
80
return this._representativePerKey.get(key);
81
}
82
83
public getTrackedRanges(reader?: IReader): TrackedEdit[] {
84
this._update.read(reader);
85
const ranges = this._edits.getNewRanges();
86
return ranges.map((r, idx) => {
87
const e = this._edits.replacements[idx];
88
const te = new TrackedEdit(e.replaceRange, r, e.data.key, e.data.source, e.data.representative);
89
return te;
90
});
91
}
92
93
public isEmpty(): boolean {
94
return this._edits.isEmpty();
95
}
96
97
public _getDebugVisualization() {
98
const ranges = this.getTrackedRanges();
99
const txt = this._doc.value.get().value;
100
101
return {
102
...{ $fileExtension: 'text.w' },
103
'value': txt,
104
'decorations': ranges.map(r => {
105
return {
106
range: [r.range.start, r.range.endExclusive],
107
color: r.source.getColor(),
108
};
109
})
110
};
111
}
112
}
113
114
export class TrackedEdit {
115
constructor(
116
public readonly originalRange: OffsetRange,
117
public readonly range: OffsetRange,
118
public readonly sourceKey: string,
119
public readonly source: EditSource,
120
public readonly sourceRepresentative: TextModelEditSource,
121
) { }
122
}
123
124