diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index 12883fe46d4..2ed893ed766 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -8,15 +8,15 @@ import * as path from 'path'; import { Repository, GitResourceGroup } from './repository'; import { Model } from './model'; import { debounce } from './decorators'; -import { filterEvent, dispose, anyEvent, fireEvent } from './util'; +import { filterEvent, dispose, anyEvent, fireEvent, PromiseSource } from './util'; import { GitErrorCodes, Status } from './api/git'; -type Callback = { resolve: (status: boolean) => void, reject: (err: any) => void }; - class GitIgnoreDecorationProvider implements DecorationProvider { + private static Decoration: Decoration = { priority: 3, color: new ThemeColor('gitDecoration.ignoredResourceForeground') }; + readonly onDidChangeDecorations: Event; - private queue = new Map; }>(); + private queue = new Map>; }>(); private disposables: Disposable[] = []; constructor(private model: Model) { @@ -29,32 +29,29 @@ class GitIgnoreDecorationProvider implements DecorationProvider { this.disposables.push(window.registerDecorationProvider(this)); } - provideDecoration(uri: Uri): Promise { + async provideDecoration(uri: Uri): Promise { const repository = this.model.getRepository(uri); if (!repository) { - return Promise.resolve(undefined); + return; } let queueItem = this.queue.get(repository.root); if (!queueItem) { - queueItem = { repository, queue: new Map() }; + queueItem = { repository, queue: new Map>() }; this.queue.set(repository.root, queueItem); } - return new Promise((resolve, reject) => { - queueItem!.queue.set(uri.fsPath, { resolve, reject }); + let promiseSource = queueItem.queue.get(uri.fsPath); + + if (!promiseSource) { + promiseSource = new PromiseSource(); + queueItem!.queue.set(uri.fsPath, promiseSource); this.checkIgnoreSoon(); - }).then(ignored => { - if (ignored) { - return { - priority: 3, - color: new ThemeColor('gitDecoration.ignoredResourceForeground') - }; - } - return undefined; - }); + } + + return await promiseSource.promise; } @debounce(500) @@ -66,16 +63,16 @@ class GitIgnoreDecorationProvider implements DecorationProvider { const paths = [...item.queue.keys()]; item.repository.checkIgnore(paths).then(ignoreSet => { - for (const [key, value] of item.queue.entries()) { - value.resolve(ignoreSet.has(key)); + for (const [path, promiseSource] of item.queue.entries()) { + promiseSource.resolve(ignoreSet.has(path) ? GitIgnoreDecorationProvider.Decoration : undefined); } }, err => { if (err.gitErrorCode !== GitErrorCodes.IsInSubmodule) { console.error(err); } - for (const [, value] of item.queue.entries()) { - value.reject(err); + for (const [, promiseSource] of item.queue.entries()) { + promiseSource.reject(err); } }); } diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 74b353f518a..e2ac91ebb19 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Disposable } from 'vscode'; +import { Event, Disposable, EventEmitter } from 'vscode'; import { dirname, sep } from 'path'; import { Readable } from 'stream'; import { promises as fs, createReadStream } from 'fs'; @@ -400,3 +400,39 @@ export class Limiter { } } } + +type Completion = { success: true, value: T } | { success: false, err: any }; + +export class PromiseSource { + + private _onDidComplete = new EventEmitter>(); + + private _promise: Promise | undefined; + get promise(): Promise { + if (this._promise) { + return this._promise; + } + + return eventToPromise(this._onDidComplete.event).then(completion => { + if (completion.success) { + return completion.value; + } else { + throw completion.err; + } + }); + } + + resolve(value: T): void { + if (!this._promise) { + this._promise = Promise.resolve(value); + this._onDidComplete.fire({ success: true, value }); + } + } + + reject(err: any): void { + if (!this._promise) { + this._promise = Promise.reject(err); + this._onDidComplete.fire({ success: false, err }); + } + } +}