diff --git a/extensions/merge-conflict/src/codelensProvider.ts b/extensions/merge-conflict/src/codelensProvider.ts index fb31f4c382df994826f6ae9e3192e5cc6b6f6924..7213594505476886de7160fe12ce341b7d2e1ae6 100644 --- a/extensions/merge-conflict/src/codelensProvider.ts +++ b/extensions/merge-conflict/src/codelensProvider.ts @@ -9,11 +9,12 @@ import { loadMessageBundle } from 'vscode-nls'; const localize = loadMessageBundle(); export default class MergeConflictCodeLensProvider implements vscode.CodeLensProvider, vscode.Disposable { - private codeLensRegistrationHandle: vscode.Disposable | null; private config: interfaces.IExtensionConfiguration; + private tracker: interfaces.IDocumentMergeConflictTracker; - constructor(private context: vscode.ExtensionContext, private tracker: interfaces.IDocumentMergeConflictTracker) { + constructor(private context: vscode.ExtensionContext, trackerService: interfaces.IDocumentMergeConflictTrackerService) { + this.tracker = trackerService.createTracker('codelens'); } begin(config: interfaces.IExtensionConfiguration) { diff --git a/extensions/merge-conflict/src/commandHandler.ts b/extensions/merge-conflict/src/commandHandler.ts index e8441f9221d01d6bb804be3be389d2b4cd2ab608..8b8517063e43b9eb54f705559958d4dbdf348f17 100644 --- a/extensions/merge-conflict/src/commandHandler.ts +++ b/extensions/merge-conflict/src/commandHandler.ts @@ -29,8 +29,10 @@ enum NavigationDirection { export default class CommandHandler implements vscode.Disposable { private disposables: vscode.Disposable[] = []; + private tracker: interfaces.IDocumentMergeConflictTracker; - constructor(private context: vscode.ExtensionContext, private tracker: interfaces.IDocumentMergeConflictTracker) { + constructor(private context: vscode.ExtensionContext, trackerService: interfaces.IDocumentMergeConflictTrackerService) { + this.tracker = trackerService.createTracker('commands'); } begin() { diff --git a/extensions/merge-conflict/src/documentTracker.ts b/extensions/merge-conflict/src/documentTracker.ts index 8030485743866349aaa3e7fd022bd282df57546e..e6b50fec1a8321d75d2239b950a7a9f50e9e155a 100644 --- a/extensions/merge-conflict/src/documentTracker.ts +++ b/extensions/merge-conflict/src/documentTracker.ts @@ -8,29 +8,70 @@ import { MergeConflictParser } from './mergeConflictParser'; import * as interfaces from './interfaces'; import { Delayer } from './delayer'; -export default class DocumentMergeConflictTracker implements vscode.Disposable, interfaces.IDocumentMergeConflictTracker { +class ScanTask { + public origins: Set = new Set(); + public delayTask: Delayer; - private cache: Map> = new Map(); - private delayExpireTime: number = 150; + constructor(delayTime: number, initialOrigin: string) { + this.origins.add(initialOrigin); + this.delayTask = new Delayer(delayTime); + } + + public addOrigin(name: string): boolean { + if (this.origins.has(name)) { + return false; + } + + return false; + } + + public hasOrigin(name: string): boolean { + return this.origins.has(name); + } +} + +class OriginDocumentMergeConflictTracker implements interfaces.IDocumentMergeConflictTracker { + constructor(private parent: DocumentMergeConflictTracker, private origin: string) { + } getConflicts(document: vscode.TextDocument): PromiseLike { + return this.parent.getConflicts(document, this.origin); + } + + isPending(document: vscode.TextDocument): boolean { + return this.parent.isPending(document, this.origin); + } + + forget(document: vscode.TextDocument) { + this.parent.forget(document); + } +} + +export default class DocumentMergeConflictTracker implements vscode.Disposable, interfaces.IDocumentMergeConflictTrackerService { + private cache: Map = new Map(); + private delayExpireTime: number = 250; + + getConflicts(document: vscode.TextDocument, origin: string): PromiseLike { // Attempt from cache let key = this.getCacheKey(document); if (!key) { // Document doesnt have a uri, can't cache it, so return - return Promise.resolve(this.getConflictsOrEmpty(document)); + return Promise.resolve(this.getConflictsOrEmpty(document, [origin])); } let cacheItem = this.cache.get(key); if (!cacheItem) { - cacheItem = new Delayer(this.delayExpireTime); + cacheItem = new ScanTask(this.delayExpireTime, origin); this.cache.set(key, cacheItem); } + else { + cacheItem.addOrigin(origin); + } - return cacheItem.trigger(() => { - let conflicts = this.getConflictsOrEmpty(document); + return cacheItem.delayTask.trigger(() => { + let conflicts = this.getConflictsOrEmpty(document, Array.from(cacheItem!.origins)); if (this.cache) { this.cache.delete(key!); @@ -40,6 +81,29 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, }); } + isPending(document: vscode.TextDocument, origin: string): boolean { + if (!document) { + return false; + } + + let key = this.getCacheKey(document); + if (!key) { + return false; + } + + var task = this.cache.get(key); + + if (!task) { + return false; + } + + return task.hasOrigin(origin); + } + + createTracker(origin: string): interfaces.IDocumentMergeConflictTracker { + return new OriginDocumentMergeConflictTracker(this, origin); + } + forget(document: vscode.TextDocument) { let key = this.getCacheKey(document); @@ -52,8 +116,9 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, this.cache.clear(); } - private getConflictsOrEmpty(document: vscode.TextDocument): interfaces.IDocumentMergeConflict[] { + private getConflictsOrEmpty(document: vscode.TextDocument, origins: string[]): interfaces.IDocumentMergeConflict[] { const containsConflict = MergeConflictParser.containsConflict(document); + if (!containsConflict) { return []; } @@ -69,4 +134,5 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, return null; } -} \ No newline at end of file +} + diff --git a/extensions/merge-conflict/src/interfaces.ts b/extensions/merge-conflict/src/interfaces.ts index e13530a3ac77f40ba3d05a9c1fee3c1227a89ff1..9d411befaba441278f921f66358f1eb55ec22bf5 100644 --- a/extensions/merge-conflict/src/interfaces.ts +++ b/extensions/merge-conflict/src/interfaces.ts @@ -37,5 +37,11 @@ export interface IDocumentMergeConflictDescriptor { export interface IDocumentMergeConflictTracker { getConflicts(document: vscode.TextDocument): PromiseLike; + isPending(document: vscode.TextDocument): boolean; + forget(document: vscode.TextDocument); +} + +export interface IDocumentMergeConflictTrackerService { + createTracker(origin: string): IDocumentMergeConflictTracker; forget(document: vscode.TextDocument); } diff --git a/extensions/merge-conflict/src/mergeDecorator.ts b/extensions/merge-conflict/src/mergeDecorator.ts index 3124fd5d262b5d9f8c199bcdaa2337f749b2a8ad..f6d0135aad9a45f417a1a3f882ed45eb91f68e59 100644 --- a/extensions/merge-conflict/src/mergeDecorator.ts +++ b/extensions/merge-conflict/src/mergeDecorator.ts @@ -17,8 +17,10 @@ export default class MergeDectorator implements vscode.Disposable { private currentColorRgb = `32,200,94`; private incomingColorRgb = `24,134,255`; private config: interfaces.IExtensionConfiguration; + private tracker: interfaces.IDocumentMergeConflictTracker; - constructor(private context: vscode.ExtensionContext, private tracker: interfaces.IDocumentMergeConflictTracker) { + constructor(private context: vscode.ExtensionContext, trackerService: interfaces.IDocumentMergeConflictTrackerService) { + this.tracker = trackerService.createTracker('decorator'); } begin(config: interfaces.IExtensionConfiguration) { @@ -149,10 +151,14 @@ export default class MergeDectorator implements vscode.Disposable { return; } + // If we have a pending scan from the same origin, exit early. + if (this.tracker.isPending(editor.document)) { + return; + } + let conflicts = await this.tracker.getConflicts(editor.document); if (conflicts.length === 0) { - // TODO: Remove decorations this.removeDecorations(editor); return; }