From 361348287e3837a81cbf1e03ac437ea0daa8590b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 23 Aug 2016 16:05:17 +0200 Subject: [PATCH] extract Cache class --- src/vs/base/common/cache.ts | 29 ++++++++ src/vs/base/test/common/cache.test.ts | 66 +++++++++++++++++++ .../electron-browser/extensionEditor.ts | 18 +++-- 3 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 src/vs/base/common/cache.ts create mode 100644 src/vs/base/test/common/cache.test.ts diff --git a/src/vs/base/common/cache.ts b/src/vs/base/common/cache.ts new file mode 100644 index 00000000000..9ed07ab56c2 --- /dev/null +++ b/src/vs/base/common/cache.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; + +export default class Cache { + + private promise: TPromise = null; + constructor(private task: ()=>TPromise) {} + + get(): TPromise { + if (this.promise) { + return this.promise; + } + + const promise = this.task(); + + this.promise = new TPromise((c, e) => promise.done(c, e), () => { + this.promise = null; + promise.cancel(); + }); + + return this.promise; + } +} diff --git a/src/vs/base/test/common/cache.test.ts b/src/vs/base/test/common/cache.test.ts new file mode 100644 index 00000000000..8a8f1515db2 --- /dev/null +++ b/src/vs/base/test/common/cache.test.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as assert from 'assert'; +import Cache from 'vs/base/common/cache'; +import { TPromise } from 'vs/base/common/winjs.base'; + +suite('Cache', () => { + + test('simple value', () => { + let counter = 0; + const cache = new Cache(() => TPromise.as(counter++)); + + return cache.get() + .then(c => assert.equal(c, 0), () => assert.fail()) + .then(() => cache.get()) + .then(c => assert.equal(c, 0), () => assert.fail()); + }); + + test('simple error', () => { + let counter = 0; + const cache = new Cache(() => TPromise.wrapError(counter++)); + + return cache.get() + .then(() => assert.fail(), err => assert.equal(err, 0)) + .then(() => cache.get()) + .then(() => assert.fail(), err => assert.equal(err, 0)); + }); + + test('should retry cancellations', () => { + let counter1 = 0, counter2 = 0; + + const cache = new Cache(() => { + counter1++; + return TPromise.timeout(1).then(() => counter2++); + }); + + assert.equal(counter1, 0); + assert.equal(counter2, 0); + let promise = cache.get(); + assert.equal(counter1, 1); + assert.equal(counter2, 0); + promise.cancel(); + assert.equal(counter1, 1); + assert.equal(counter2, 0); + + promise = cache.get(); + assert.equal(counter1, 2); + assert.equal(counter2, 0); + + return promise + .then(c => { + assert.equal(counter1, 2); + assert.equal(counter2, 1); + }) + .then(() => cache.get()) + .then(c => { + assert.equal(counter1, 2); + assert.equal(counter2, 1); + }); + }); +}); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 0060d7039ae..1023caf24b9 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -11,6 +11,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { marked } from 'vs/base/common/marked/marked'; import { always } from 'vs/base/common/async'; import Event, { Emitter, once } from 'vs/base/common/event'; +import Cache from 'vs/base/common/cache'; import { Action } from 'vs/base/common/actions'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, empty, dispose, toDisposable } from 'vs/base/common/lifecycle'; @@ -22,7 +23,7 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/common/viewletService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionGalleryService, IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; import { ExtensionsInput } from './extensionsInput'; import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension } from './extensions'; @@ -112,6 +113,9 @@ export class ExtensionEditor extends BaseEditor { private _highlight: ITemplateData; private highlightDisposable: IDisposable; + private extensionReadme: Cache; + private extensionManifest: Cache; + private contentDisposables: IDisposable[] = []; private transientDisposables: IDisposable[] = []; private disposables: IDisposable[]; @@ -130,6 +134,8 @@ export class ExtensionEditor extends BaseEditor { this._highlight = null; this.highlightDisposable = empty; this.disposables = []; + this.extensionReadme = null; + this.extensionManifest = null; this.disposables.push(viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables)); } @@ -179,6 +185,9 @@ export class ExtensionEditor extends BaseEditor { const extension = input.extension; this.telemetryService.publicLog('extensionGallery:openExtension', extension.telemetryData); + this.extensionReadme = new Cache(() => extension.getReadme()); + this.extensionManifest = new Cache(() => extension.getManifest()); + const onError = once(domEvent(this.icon, 'error')); onError(() => this.icon.src = extension.iconUrlFallback, null, this.transientDisposables); this.icon.src = extension.iconUrl; @@ -243,8 +252,9 @@ export class ExtensionEditor extends BaseEditor { } private openReadme(extension: IExtension) { - return this.loadContents(() => extension.getReadme() + return this.loadContents(() => this.extensionReadme.get() .then(marked.parse) + .then(renderBody) .then(body => { const webview = new WebView( this.content, @@ -252,7 +262,7 @@ export class ExtensionEditor extends BaseEditor { ); webview.style(this.themeService.getColorTheme()); - webview.contents = [renderBody(body)]; + webview.contents = [body]; const linkListener = webview.onDidClickLink(link => shell.openExternal(link.toString())); const themeListener = this.themeService.onDidColorThemeChange(themeId => webview.style(themeId)); @@ -265,7 +275,7 @@ export class ExtensionEditor extends BaseEditor { } private openContributions(extension: IExtension) { - return this.loadContents(() => extension.getManifest() + return this.loadContents(() => this.extensionManifest.get() .then(manifest => { this.content.innerHTML = ''; const content = append(this.content, $('div', { class: 'subcontent' })); -- GitLab