Path: blob/main/extensions/merge-conflict/src/documentTracker.ts
3296 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*--------------------------------------------------------------------------------------------*/45import * as vscode from 'vscode';6import { MergeConflictParser } from './mergeConflictParser';7import * as interfaces from './interfaces';8import { Delayer } from './delayer';9import TelemetryReporter from '@vscode/extension-telemetry';1011class ScanTask {12public origins: Set<string> = new Set<string>();13public delayTask: Delayer<interfaces.IDocumentMergeConflict[]>;1415constructor(delayTime: number, initialOrigin: string) {16this.origins.add(initialOrigin);17this.delayTask = new Delayer<interfaces.IDocumentMergeConflict[]>(delayTime);18}1920public addOrigin(name: string): void {21this.origins.add(name);22}2324public hasOrigin(name: string): boolean {25return this.origins.has(name);26}27}2829class OriginDocumentMergeConflictTracker implements interfaces.IDocumentMergeConflictTracker {30constructor(private parent: DocumentMergeConflictTracker, private origin: string) {31}3233getConflicts(document: vscode.TextDocument): PromiseLike<interfaces.IDocumentMergeConflict[]> {34return this.parent.getConflicts(document, this.origin);35}3637isPending(document: vscode.TextDocument): boolean {38return this.parent.isPending(document, this.origin);39}4041forget(document: vscode.TextDocument) {42this.parent.forget(document);43}44}4546export default class DocumentMergeConflictTracker implements vscode.Disposable, interfaces.IDocumentMergeConflictTrackerService {47private cache: Map<string, ScanTask> = new Map();48private delayExpireTime: number = 0;4950constructor(private readonly telemetryReporter: TelemetryReporter) { }5152getConflicts(document: vscode.TextDocument, origin: string): PromiseLike<interfaces.IDocumentMergeConflict[]> {53// Attempt from cache5455const key = this.getCacheKey(document);5657if (!key) {58// Document doesn't have a uri, can't cache it, so return59return Promise.resolve(this.getConflictsOrEmpty(document, [origin]));60}6162let cacheItem = this.cache.get(key);63if (!cacheItem) {64cacheItem = new ScanTask(this.delayExpireTime, origin);65this.cache.set(key, cacheItem);66}67else {68cacheItem.addOrigin(origin);69}7071return cacheItem.delayTask.trigger(() => {72const conflicts = this.getConflictsOrEmpty(document, Array.from(cacheItem!.origins));7374this.cache?.delete(key!);7576return conflicts;77});78}7980isPending(document: vscode.TextDocument, origin: string): boolean {81if (!document) {82return false;83}8485const key = this.getCacheKey(document);86if (!key) {87return false;88}8990const task = this.cache.get(key);91if (!task) {92return false;93}9495return task.hasOrigin(origin);96}9798createTracker(origin: string): interfaces.IDocumentMergeConflictTracker {99return new OriginDocumentMergeConflictTracker(this, origin);100}101102forget(document: vscode.TextDocument) {103const key = this.getCacheKey(document);104105if (key) {106this.cache.delete(key);107}108}109110dispose() {111this.cache.clear();112}113114private readonly seenDocumentsWithConflicts = new Set<string>();115116private getConflictsOrEmpty(document: vscode.TextDocument, _origins: string[]): interfaces.IDocumentMergeConflict[] {117const containsConflict = MergeConflictParser.containsConflict(document);118119if (!containsConflict) {120return [];121}122123const conflicts = MergeConflictParser.scanDocument(document, this.telemetryReporter);124125const key = document.uri.toString();126// Don't report telemetry for the same document twice. This is an approximation, but good enough.127// Otherwise redo/undo could trigger this event multiple times.128if (!this.seenDocumentsWithConflicts.has(key)) {129this.seenDocumentsWithConflicts.add(key);130131/* __GDPR__132"mergeMarkers.documentWithConflictMarkersOpened" : {133"owner": "hediet",134"comment": "Used to determine how many documents with conflicts are opened.",135"conflictCount": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "comment": "Total number of conflict counts" }136}137*/138this.telemetryReporter.sendTelemetryEvent('mergeMarkers.documentWithConflictMarkersOpened', {}, {139conflictCount: conflicts.length,140});141}142143return conflicts;144}145146private getCacheKey(document: vscode.TextDocument): string | null {147if (document.uri) {148return document.uri.toString();149}150151return null;152}153}154155156157