Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/merge-conflict/src/documentTracker.ts
3296 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
import * as vscode from 'vscode';
7
import { MergeConflictParser } from './mergeConflictParser';
8
import * as interfaces from './interfaces';
9
import { Delayer } from './delayer';
10
import TelemetryReporter from '@vscode/extension-telemetry';
11
12
class ScanTask {
13
public origins: Set<string> = new Set<string>();
14
public delayTask: Delayer<interfaces.IDocumentMergeConflict[]>;
15
16
constructor(delayTime: number, initialOrigin: string) {
17
this.origins.add(initialOrigin);
18
this.delayTask = new Delayer<interfaces.IDocumentMergeConflict[]>(delayTime);
19
}
20
21
public addOrigin(name: string): void {
22
this.origins.add(name);
23
}
24
25
public hasOrigin(name: string): boolean {
26
return this.origins.has(name);
27
}
28
}
29
30
class OriginDocumentMergeConflictTracker implements interfaces.IDocumentMergeConflictTracker {
31
constructor(private parent: DocumentMergeConflictTracker, private origin: string) {
32
}
33
34
getConflicts(document: vscode.TextDocument): PromiseLike<interfaces.IDocumentMergeConflict[]> {
35
return this.parent.getConflicts(document, this.origin);
36
}
37
38
isPending(document: vscode.TextDocument): boolean {
39
return this.parent.isPending(document, this.origin);
40
}
41
42
forget(document: vscode.TextDocument) {
43
this.parent.forget(document);
44
}
45
}
46
47
export default class DocumentMergeConflictTracker implements vscode.Disposable, interfaces.IDocumentMergeConflictTrackerService {
48
private cache: Map<string, ScanTask> = new Map();
49
private delayExpireTime: number = 0;
50
51
constructor(private readonly telemetryReporter: TelemetryReporter) { }
52
53
getConflicts(document: vscode.TextDocument, origin: string): PromiseLike<interfaces.IDocumentMergeConflict[]> {
54
// Attempt from cache
55
56
const key = this.getCacheKey(document);
57
58
if (!key) {
59
// Document doesn't have a uri, can't cache it, so return
60
return Promise.resolve(this.getConflictsOrEmpty(document, [origin]));
61
}
62
63
let cacheItem = this.cache.get(key);
64
if (!cacheItem) {
65
cacheItem = new ScanTask(this.delayExpireTime, origin);
66
this.cache.set(key, cacheItem);
67
}
68
else {
69
cacheItem.addOrigin(origin);
70
}
71
72
return cacheItem.delayTask.trigger(() => {
73
const conflicts = this.getConflictsOrEmpty(document, Array.from(cacheItem!.origins));
74
75
this.cache?.delete(key!);
76
77
return conflicts;
78
});
79
}
80
81
isPending(document: vscode.TextDocument, origin: string): boolean {
82
if (!document) {
83
return false;
84
}
85
86
const key = this.getCacheKey(document);
87
if (!key) {
88
return false;
89
}
90
91
const task = this.cache.get(key);
92
if (!task) {
93
return false;
94
}
95
96
return task.hasOrigin(origin);
97
}
98
99
createTracker(origin: string): interfaces.IDocumentMergeConflictTracker {
100
return new OriginDocumentMergeConflictTracker(this, origin);
101
}
102
103
forget(document: vscode.TextDocument) {
104
const key = this.getCacheKey(document);
105
106
if (key) {
107
this.cache.delete(key);
108
}
109
}
110
111
dispose() {
112
this.cache.clear();
113
}
114
115
private readonly seenDocumentsWithConflicts = new Set<string>();
116
117
private getConflictsOrEmpty(document: vscode.TextDocument, _origins: string[]): interfaces.IDocumentMergeConflict[] {
118
const containsConflict = MergeConflictParser.containsConflict(document);
119
120
if (!containsConflict) {
121
return [];
122
}
123
124
const conflicts = MergeConflictParser.scanDocument(document, this.telemetryReporter);
125
126
const key = document.uri.toString();
127
// Don't report telemetry for the same document twice. This is an approximation, but good enough.
128
// Otherwise redo/undo could trigger this event multiple times.
129
if (!this.seenDocumentsWithConflicts.has(key)) {
130
this.seenDocumentsWithConflicts.add(key);
131
132
/* __GDPR__
133
"mergeMarkers.documentWithConflictMarkersOpened" : {
134
"owner": "hediet",
135
"comment": "Used to determine how many documents with conflicts are opened.",
136
"conflictCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Total number of conflict counts" }
137
}
138
*/
139
this.telemetryReporter.sendTelemetryEvent('mergeMarkers.documentWithConflictMarkersOpened', {}, {
140
conflictCount: conflicts.length,
141
});
142
}
143
144
return conflicts;
145
}
146
147
private getCacheKey(document: vscode.TextDocument): string | null {
148
if (document.uri) {
149
return document.uri.toString();
150
}
151
152
return null;
153
}
154
}
155
156
157