diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index 1ec1cb65c112da1533fa9518260bd3c11defcaf9..dae0e054b008cb43e3cee66c01c8e857abb11d8d 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -5,39 +5,13 @@ import * as paths from 'vs/base/common/paths'; import { URI } from 'vs/base/common/uri'; -import { TPromise, TValueCallback } from 'vs/base/common/winjs.base'; import { canceled } from 'vs/base/common/errors'; -export class DeferredTPromise extends TPromise { - - private completeCallback: TValueCallback; - private errorCallback: (err: any) => void; - - constructor() { - let captured: any; - super((c, e) => { - captured = { c, e }; - }); - this.completeCallback = captured.c; - this.errorCallback = captured.e; - } - - public complete(value: T) { - this.completeCallback(value); - } - - public error(err: any) { - this.errorCallback(err); - } - - public cancel() { - this.errorCallback(canceled()); - } -} +export type ValueCallback = (value: T | Thenable) => void; export class DeferredPromise { - private completeCallback: TValueCallback; + private completeCallback: ValueCallback; private errorCallback: (err: any) => void; public p: Promise; @@ -50,14 +24,20 @@ export class DeferredPromise { } public complete(value: T) { - process.nextTick(() => { - this.completeCallback(value); + return new Promise(resolve => { + process.nextTick(() => { + this.completeCallback(value); + resolve(); + }); }); } public error(err: any) { - process.nextTick(() => { - this.errorCallback(err); + return new Promise(resolve => { + process.nextTick(() => { + this.errorCallback(err); + resolve(); + }); }); } diff --git a/src/vs/platform/quickinput/common/quickInput.ts b/src/vs/platform/quickinput/common/quickInput.ts index 20e13c85c3681eebf0681c82135573baf1b97346..e273c0918a284821910b1b6c59eca99c554a547c 100644 --- a/src/vs/platform/quickinput/common/quickInput.ts +++ b/src/vs/platform/quickinput/common/quickInput.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ResolvedKeybinding } from 'vs/base/common/keyCodes'; import { URI } from 'vs/base/common/uri'; @@ -76,7 +75,7 @@ export interface IPickOptions { /** * an optional property for the item to focus initially. */ - activeItem?: TPromise | T; + activeItem?: Promise | T; onKeyMods?: (keyMods: IKeyMods) => void; onDidFocus?: (entry: T) => void; @@ -231,14 +230,14 @@ export interface IQuickInputService { /** * Opens the quick input box for selecting items and returns a promise with the user selected item(s) if any. */ - pick(picks: TPromise[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: true }, token?: CancellationToken): TPromise; - pick(picks: TPromise[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: false }, token?: CancellationToken): TPromise; - pick(picks: TPromise[]> | QuickPickInput[], options?: Omit, 'canPickMany'>, token?: CancellationToken): TPromise; + pick(picks: Thenable[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: true }, token?: CancellationToken): Promise; + pick(picks: Thenable[]> | QuickPickInput[], options?: IPickOptions & { canPickMany: false }, token?: CancellationToken): Promise; + pick(picks: Thenable[]> | QuickPickInput[], options?: Omit, 'canPickMany'>, token?: CancellationToken): Promise; /** * Opens the quick input box for text input and returns a promise with the user typed value if any. */ - input(options?: IInputOptions, token?: CancellationToken): TPromise; + input(options?: IInputOptions, token?: CancellationToken): Promise; backButton: IQuickInputButton; @@ -251,9 +250,9 @@ export interface IQuickInputService { navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void; - accept(): TPromise; + accept(): Promise; - back(): TPromise; + back(): Promise; - cancel(): TPromise; + cancel(): Promise; } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index b08a87ffb8f8fe0c3e4668dcbef151ca7ea10d47..99b4fe1d5e767af40b172e95edfb7737504b2abe 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -13,7 +13,6 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CancellationToken } from 'vs/base/common/cancellation'; import { QuickInputList } from './quickInputList'; import { QuickInputBox } from './quickInputBox'; @@ -1041,8 +1040,8 @@ export class QuickInputService extends Component implements IQuickInputService { this.updateStyles(); } - pick>(picks: TPromise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): TPromise { - return new TPromise((doResolve, reject) => { + pick>(picks: Promise[]> | QuickPickInput[], options: O = {}, token: CancellationToken = CancellationToken.None): Promise { + return new Promise((doResolve, reject) => { let resolve = (result: any) => { resolve = doResolve; if (options.onKeyMods) { @@ -1117,7 +1116,7 @@ export class QuickInputService extends Component implements IQuickInputService { input.quickNavigate = options.quickNavigate; input.contextKey = options.contextKey; input.busy = true; - TPromise.join([picks, options.activeItem]) + Promise.all([picks, options.activeItem]) .then(([items, _activeItem]) => { activeItem = _activeItem; input.busy = false; @@ -1130,29 +1129,29 @@ export class QuickInputService extends Component implements IQuickInputService { } }); input.show(); - TPromise.wrap(picks).then(null, err => { + Promise.resolve(picks).then(null, err => { reject(err); input.hide(); }); }); } - input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): TPromise { - return new TPromise((resolve, reject) => { + input(options: IInputOptions = {}, token: CancellationToken = CancellationToken.None): Promise { + return new Promise((resolve, reject) => { if (token.isCancellationRequested) { resolve(undefined); return; } const input = this.createInputBox(); - const validateInput = options.validateInput || (() => TPromise.as(undefined)); + const validateInput = options.validateInput || (() => >Promise.resolve(undefined)); const onDidValueChange = debounceEvent(input.onDidChangeValue, (last, cur) => cur, 100); let validationValue = options.value || ''; - let validation = TPromise.wrap(validateInput(validationValue)); + let validation = Promise.resolve(validateInput(validationValue)); const disposables = [ input, onDidValueChange(value => { if (value !== validationValue) { - validation = TPromise.wrap(validateInput(value)); + validation = Promise.resolve(validateInput(value)); validationValue = value; } validation.then(result => { @@ -1164,7 +1163,7 @@ export class QuickInputService extends Component implements IQuickInputService { input.onDidAccept(() => { const value = input.value; if (value !== validationValue) { - validation = TPromise.wrap(validateInput(value)); + validation = Promise.resolve(validateInput(value)); validationValue = value; } validation.then(result => { @@ -1335,17 +1334,17 @@ export class QuickInputService extends Component implements IQuickInputService { accept() { this.onDidAcceptEmitter.fire(); - return TPromise.as(undefined); + return Promise.resolve(undefined); } back() { this.onDidTriggerButtonEmitter.fire(this.backButton); - return TPromise.as(undefined); + return Promise.resolve(undefined); } cancel() { this.hide(); - return TPromise.as(undefined); + return Promise.resolve(undefined); } layout(dimension: dom.Dimension): void { @@ -1411,8 +1410,8 @@ export class BackAction extends Action { super(id, label); } - public run(): TPromise { + public run(): Promise { this.quickInputService.back(); - return TPromise.as(null); + return Promise.resolve(null); } } diff --git a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts index 678a224a1159eb351d55a548beed4895f75bbdb4..fa22428e5b0defde16b73fe8e9ca5c7cae761e2f 100644 --- a/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts +++ b/src/vs/workbench/browser/parts/quickopen/quickOpenController.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/quickopen'; -import { TPromise, ValueCallback } from 'vs/base/common/winjs.base'; import * as nls from 'vs/nls'; import * as browser from 'vs/base/browser/browser'; import * as strings from 'vs/base/common/strings'; @@ -54,6 +53,8 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; const HELP_PREFIX = '?'; +type ValueCallback = (value: T | Thenable) => void; + export class QuickOpenController extends Component implements IQuickOpenService { private static readonly MAX_SHORT_RESPONSE_TIME = 500; @@ -73,7 +74,7 @@ export class QuickOpenController extends Component implements IQuickOpenService private lastSubmittedInputValue: string; private quickOpenWidget: QuickOpenWidget; private dimension: Dimension; - private mapResolvedHandlersToPrefix: { [prefix: string]: TPromise; } = Object.create(null); + private mapResolvedHandlersToPrefix: { [prefix: string]: Promise; } = Object.create(null); private mapContextKeyToContext: { [id: string]: IContextKey; } = Object.create(null); private handlerOnOpenCalled: { [prefix: string]: boolean; } = Object.create(null); private promisesToCompleteOnHide: ValueCallback[] = []; @@ -153,12 +154,12 @@ export class QuickOpenController extends Component implements IQuickOpenService } } - show(prefix?: string, options?: IShowOptions): TPromise { + show(prefix?: string, options?: IShowOptions): Promise { let quickNavigateConfiguration = options ? options.quickNavigateConfiguration : void 0; let inputSelection = options ? options.inputSelection : void 0; let autoFocus = options ? options.autoFocus : void 0; - const promiseCompletedOnHide = new TPromise(c => { + const promiseCompletedOnHide = new Promise(c => { this.promisesToCompleteOnHide.push(c); }); @@ -381,7 +382,7 @@ export class QuickOpenController extends Component implements IQuickOpenService return; } - let resultPromise: TPromise; + let resultPromise: Promise; let resultPromiseDone = false; if (handlerDescriptor) { @@ -426,7 +427,7 @@ export class QuickOpenController extends Component implements IQuickOpenService }); } - private handleDefaultHandler(handler: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): TPromise { + private handleDefaultHandler(handler: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise { // Fill in history results if matching and we are configured to search in history let matchingHistoryEntries: QuickOpenEntry[]; @@ -514,7 +515,7 @@ export class QuickOpenController extends Component implements IQuickOpenService } } - private handleSpecificHandler(handlerDescriptor: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): TPromise { + private handleSpecificHandler(handlerDescriptor: QuickOpenHandlerDescriptor, value: string, token: CancellationToken): Promise { return this.resolveHandler(handlerDescriptor).then((resolvedHandler: QuickOpenHandler) => { // Remove handler prefix from search value @@ -585,7 +586,7 @@ export class QuickOpenController extends Component implements IQuickOpenService return mapEntryToPath; } - private resolveHandler(handler: QuickOpenHandlerDescriptor): TPromise { + private resolveHandler(handler: QuickOpenHandlerDescriptor): Promise { let result = this._resolveHandler(handler); const id = handler.getId(); @@ -603,11 +604,11 @@ export class QuickOpenController extends Component implements IQuickOpenService return result.then(null, (error) => { delete this.mapResolvedHandlersToPrefix[id]; - return TPromise.wrapError(new Error(`Unable to instantiate quick open handler ${handler.getId()}: ${JSON.stringify(error)}`)); + return Promise.reject(new Error(`Unable to instantiate quick open handler ${handler.getId()}: ${JSON.stringify(error)}`)); }); } - private _resolveHandler(handler: QuickOpenHandlerDescriptor): TPromise { + private _resolveHandler(handler: QuickOpenHandlerDescriptor): Promise { const id = handler.getId(); // Return Cached @@ -835,7 +836,7 @@ export class RemoveFromEditorHistoryAction extends Action { super(id, label); } - run(): TPromise { + run(): Thenable { interface IHistoryPickEntry extends IQuickPickItem { input: IEditorInput | IResourceInput; } diff --git a/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts b/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts index 9186a2bcefab2411a4ad7995747637039a93d3bf..ced5dcf21320240a92cc2e27f1e5d69422d57c8a 100644 --- a/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts +++ b/src/vs/workbench/parts/search/test/browser/openFileHandler.test.ts @@ -6,14 +6,13 @@ import * as assert from 'assert'; import * as errors from 'vs/base/common/errors'; import * as objects from 'vs/base/common/objects'; -import { TPromise } from 'vs/base/common/winjs.base'; import { CacheState } from 'vs/workbench/parts/search/browser/openFileHandler'; -import { DeferredTPromise } from 'vs/base/test/common/utils'; +import { DeferredPromise } from 'vs/base/test/common/utils'; import { QueryType, IFileQuery } from 'vs/platform/search/common/search'; suite('CacheState', () => { - test('reuse old cacheKey until new cache is loaded', function () { + test('reuse old cacheKey until new cache is loaded', async function () { const cache = new MockCache(); @@ -26,7 +25,7 @@ suite('CacheState', () => { assert.strictEqual(first.isLoaded, false); assert.strictEqual(first.isUpdating, true); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); assert.strictEqual(first.isLoaded, true); assert.strictEqual(first.isUpdating, false); @@ -34,18 +33,18 @@ suite('CacheState', () => { second.load(); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, true); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); assert.strictEqual(second.cacheKey, firstKey); // still using old cacheKey const secondKey = cache.cacheKeys[1]; - cache.loading[secondKey].complete(null); + await cache.loading[secondKey].complete(null); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); assert.strictEqual(second.cacheKey, secondKey); }); - test('do not spawn additional load if previous is still loading', function () { + test('do not spawn additional load if previous is still loading', async function () { const cache = new MockCache(); @@ -64,29 +63,29 @@ suite('CacheState', () => { assert.strictEqual(Object.keys(cache.loading).length, 1); // still only one loading assert.strictEqual(second.cacheKey, firstKey); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); }); - test('do not use previous cacheKey if query changed', function () { + test('do not use previous cacheKey if query changed', async function () { const cache = new MockCache(); const first = createCacheState(cache); const firstKey = first.cacheKey; first.load(); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); assert.strictEqual(first.isLoaded, true); assert.strictEqual(first.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); cache.baseQuery.excludePattern = { '**/node_modules': true }; const second = createCacheState(cache, first); assert.strictEqual(second.isLoaded, false); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); second.load(); assert.strictEqual(second.isLoaded, false); @@ -95,40 +94,40 @@ suite('CacheState', () => { const secondKey = cache.cacheKeys[1]; assert.strictEqual(second.cacheKey, secondKey); - cache.loading[secondKey].complete(null); + await cache.loading[secondKey].complete(null); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); }); - test('dispose propagates', function () { + test('dispose propagates', async function () { const cache = new MockCache(); const first = createCacheState(cache); const firstKey = first.cacheKey; first.load(); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); const second = createCacheState(cache, first); assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); second.dispose(); assert.strictEqual(second.isLoaded, false); assert.strictEqual(second.isUpdating, false); - assert.strictEqual(Object.keys(cache.disposing).length, 1); + await cache.awaitDisposal(1); assert.ok(cache.disposing[firstKey]); }); - test('keep using old cacheKey when loading fails', function () { + test('keep using old cacheKey when loading fails', async function () { const cache = new MockCache(); const first = createCacheState(cache); const firstKey = first.cacheKey; first.load(); - cache.loading[firstKey].complete(null); + await cache.loading[firstKey].complete(null); const second = createCacheState(cache, first); second.load(); @@ -136,14 +135,14 @@ suite('CacheState', () => { const origErrorHandler = errors.errorHandler.getUnexpectedErrorHandler(); try { errors.setUnexpectedErrorHandler(() => null); - cache.loading[secondKey].error('loading failed'); + await cache.loading[secondKey].error('loading failed'); } finally { errors.setUnexpectedErrorHandler(origErrorHandler); } assert.strictEqual(second.isLoaded, true); assert.strictEqual(second.isUpdating, false); assert.strictEqual(Object.keys(cache.loading).length, 2); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); assert.strictEqual(second.cacheKey, firstKey); // keep using old cacheKey const third = createCacheState(cache, second); @@ -151,15 +150,15 @@ suite('CacheState', () => { assert.strictEqual(third.isLoaded, true); assert.strictEqual(third.isUpdating, true); assert.strictEqual(Object.keys(cache.loading).length, 3); - assert.strictEqual(Object.keys(cache.disposing).length, 0); + await cache.awaitDisposal(0); assert.strictEqual(third.cacheKey, firstKey); const thirdKey = cache.cacheKeys[2]; - cache.loading[thirdKey].complete(null); + await cache.loading[thirdKey].complete(null); assert.strictEqual(third.isLoaded, true); assert.strictEqual(third.isUpdating, false); assert.strictEqual(Object.keys(cache.loading).length, 3); - assert.strictEqual(Object.keys(cache.disposing).length, 2); + await cache.awaitDisposal(2); assert.strictEqual(third.cacheKey, thirdKey); // recover with next successful load }); @@ -175,8 +174,10 @@ suite('CacheState', () => { class MockCache { public cacheKeys: string[] = []; - public loading: { [cacheKey: string]: DeferredTPromise } = {}; - public disposing: { [cacheKey: string]: DeferredTPromise } = {}; + public loading: { [cacheKey: string]: DeferredPromise } = {}; + public disposing: { [cacheKey: string]: DeferredPromise } = {}; + + private _awaitDisposal: (() => void)[][] = []; public baseQuery: IFileQuery = { type: QueryType.File @@ -187,16 +188,31 @@ suite('CacheState', () => { return objects.assign({ cacheKey: cacheKey }, this.baseQuery); } - public load(query: IFileQuery): TPromise { - const promise = new DeferredTPromise(); + public load(query: IFileQuery): Promise { + const promise = new DeferredPromise(); this.loading[query.cacheKey] = promise; - return promise; + return promise.p; } - public dispose(cacheKey: string): TPromise { - const promise = new DeferredTPromise(); + public dispose(cacheKey: string): Promise { + const promise = new DeferredPromise(); this.disposing[cacheKey] = promise; - return promise; + const n = Object.keys(this.disposing).length; + for (const done of this._awaitDisposal[n] || []) { + done(); + } + delete this._awaitDisposal[n]; + return promise.p; + } + + public awaitDisposal(n: number) { + return new Promise(resolve => { + if (n === Object.keys(this.disposing).length) { + resolve(); + } else { + (this._awaitDisposal[n] || (this._awaitDisposal[n] = [])).push(resolve); + } + }); } } }); diff --git a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts index f093b63e4de597f844fe03b9a39c478dd35f02e9..7b4e8721ac6806644a604070a3df9600c266651f 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts +++ b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; import { URI } from 'vs/base/common/uri'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -26,9 +25,9 @@ export class WalkThroughContentProvider implements ITextModelContentProvider, IW this.textModelResolverService.registerTextModelContentProvider(Schemas.walkThrough, this); } - public provideTextContent(resource: URI): TPromise { + public provideTextContent(resource: URI): Thenable { const query = resource.query ? JSON.parse(resource.query) : {}; - const content: TPromise = (query.moduleId ? new TPromise((resolve, reject) => { + const content: Thenable = (query.moduleId ? new Promise((resolve, reject) => { require([query.moduleId], content => { try { resolve(content.default()); @@ -61,7 +60,7 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi this.textModelResolverService.registerTextModelContentProvider(Schemas.walkThroughSnippet, this); } - public provideTextContent(resource: URI): TPromise { + public provideTextContent(resource: URI): Thenable { return this.textFileService.resolveTextContent(URI.file(resource.fsPath)).then(content => { let codeEditorModel = this.modelService.getModel(resource); if (!codeEditorModel) {