diff --git a/extensions/git/src/contentProvider.ts b/extensions/git/src/contentProvider.ts index 3ef952c231d..1c3283aa102 100644 --- a/extensions/git/src/contentProvider.ts +++ b/extensions/git/src/contentProvider.ts @@ -6,7 +6,7 @@ 'use strict'; import { workspace, Uri, Disposable, Event, EventEmitter } from 'vscode'; -import { debounce } from './decorators'; +import { debounce, throttle } from './decorators'; import { Model } from './model'; export class GitContentProvider { @@ -20,13 +20,20 @@ export class GitContentProvider { constructor(private model: Model) { this.disposables.push( - model.onDidChangeRepository(this.fireChangeEvents, this), + model.onDidChangeRepository(this.eventuallyFireChangeEvents, this), workspace.registerTextDocumentContentProvider('git', this) ); } - @debounce(300) - private fireChangeEvents(): void { + @debounce(1100) + private eventuallyFireChangeEvents(): void { + this.fireChangeEvents(); + } + + @throttle + private async fireChangeEvents(): Promise { + await this.model.whenIdle(); + Object.keys(this.uris).forEach(key => { this.onDidChangeEmitter.fire(this.uris[key]); }); diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 4cc6eb1e72f..24315fcf232 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -188,6 +188,38 @@ export enum Operation { GetCommitTemplate = 1 << 15 } +// function getOperationName(operation: Operation): string { +// switch (operation) { +// case Operation.Status: return 'Status'; +// case Operation.Add: return 'Add'; +// case Operation.RevertFiles: return 'RevertFiles'; +// case Operation.Commit: return 'Commit'; +// case Operation.Clean: return 'Clean'; +// case Operation.Branch: return 'Branch'; +// case Operation.Checkout: return 'Checkout'; +// case Operation.Reset: return 'Reset'; +// case Operation.Fetch: return 'Fetch'; +// case Operation.Pull: return 'Pull'; +// case Operation.Push: return 'Push'; +// case Operation.Sync: return 'Sync'; +// case Operation.Init: return 'Init'; +// case Operation.Show: return 'Show'; +// case Operation.Stage: return 'Stage'; +// case Operation.GetCommitTemplate: return 'GetCommitTemplate'; +// default: return 'unknown'; +// } +// } + +function isReadOnly(operation: Operation): boolean { + switch (operation) { + case Operation.Show: + case Operation.GetCommitTemplate: + return true; + default: + return false; + } +} + export interface Operations { isIdle(): boolean; isRunning(operation: Operation): boolean; @@ -329,6 +361,12 @@ export class Model implements Disposable { this.status(); } + async whenIdle(): Promise { + while (!this.operations.isIdle()) { + await eventToPromise(this.onDidRunOperation); + } + } + @throttle async init(): Promise { if (this.state !== State.NotAGitRepository) { @@ -445,6 +483,9 @@ export class Model implements Disposable { } async show(ref: string, uri: Uri): Promise { + // TODO@Joao: should we make this a general concept? + await this.whenIdle(); + return await this.run(Operation.Show, async () => { const relativePath = path.relative(this.repository.root, uri.fsPath).replace(/\\/g, '/'); const result = await this.repository.git.exec(this.repository.root, ['show', `${ref}:${relativePath}`]); @@ -472,7 +513,11 @@ export class Model implements Disposable { try { await this.assertIdleState(); const result = await runOperation(); - await this.update(); + + if (!isReadOnly(operation)) { + await this.update(); + } + return result; } catch (err) { if (err.gitErrorCode === GitErrorCodes.NotAGitRepository) { @@ -615,12 +660,6 @@ export class Model implements Disposable { await new Promise(c => setTimeout(c, 5000)); } - private async whenIdle(): Promise { - while (!this.operations.isIdle()) { - await eventToPromise(this.onDidRunOperation); - } - } - dispose(): void { this.repositoryDisposable.dispose(); this.disposables = dispose(this.disposables);