mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 21:55:38 +00:00
Track document changes per callee
It is possible in the open / activate event cycle that we end up doing a double push of decorator changes to the editor, as both events pending on to the same delayed operation.
This commit is contained in:
commit
7966de7ac9
|
@ -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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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<string> = new Set<string>();
|
||||
public delayTask: Delayer<interfaces.IDocumentMergeConflict[]>;
|
||||
|
||||
private cache: Map<string, Delayer<interfaces.IDocumentMergeConflict[]>> = new Map();
|
||||
private delayExpireTime: number = 150;
|
||||
constructor(delayTime: number, initialOrigin: string) {
|
||||
this.origins.add(initialOrigin);
|
||||
this.delayTask = new Delayer<interfaces.IDocumentMergeConflict[]>(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<interfaces.IDocumentMergeConflict[]> {
|
||||
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<string, ScanTask> = new Map();
|
||||
private delayExpireTime: number = 250;
|
||||
|
||||
getConflicts(document: vscode.TextDocument, origin: string): PromiseLike<interfaces.IDocumentMergeConflict[]> {
|
||||
// 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<interfaces.IDocumentMergeConflict[]>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,5 +37,11 @@ export interface IDocumentMergeConflictDescriptor {
|
|||
|
||||
export interface IDocumentMergeConflictTracker {
|
||||
getConflicts(document: vscode.TextDocument): PromiseLike<IDocumentMergeConflict[]>;
|
||||
isPending(document: vscode.TextDocument): boolean;
|
||||
forget(document: vscode.TextDocument);
|
||||
}
|
||||
|
||||
export interface IDocumentMergeConflictTrackerService {
|
||||
createTracker(origin: string): IDocumentMergeConflictTracker;
|
||||
forget(document: vscode.TextDocument);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue