diff --git a/src/vs/base/common/paging.ts b/src/vs/base/common/paging.ts new file mode 100644 index 0000000000000000000000000000000000000000..a0542e259820fe82981bd7a09cdfab0df867b0b6 --- /dev/null +++ b/src/vs/base/common/paging.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * 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 interface IPager { + firstPage: T[]; + total: number; + pageSize: number; + getPage(pageIndex: number): TPromise; +} + +interface IPage { + isResolved: boolean; + promise: TPromise; + elements: T[]; +} + +export class PagedModel { + + private pages: IPage[] = []; + + constructor(private pager: IPager) { + this.pages = [{ isResolved: true, promise: null, elements: pager.firstPage.slice() }]; + + const totalPages = Math.ceil(pager.total / pager.pageSize); + + for (let i = 0, len = totalPages - 1; i < len; i++) { + this.pages.push({ isResolved: false, promise: null, elements: [] }); + } + } + + isResolved(index: number): boolean { + const pageIndex = Math.floor(index / this.pager.pageSize); + const page = this.pages[pageIndex]; + return !!page.isResolved; + } + + get(index: number): T { + const pageIndex = Math.floor(index / this.pager.pageSize); + const indexInPage = index % this.pager.pageSize; + const page = this.pages[pageIndex]; + + return page.elements[indexInPage]; + } + + resolve(index: number): TPromise { + const pageIndex = Math.floor(index / this.pager.pageSize); + const indexInPage = index % this.pager.pageSize; + const page = this.pages[pageIndex]; + + if (page.isResolved) { + return TPromise.as(page.elements[indexInPage]); + } + + if (!page.promise) { + page.promise = this.pager.getPage(pageIndex).then(elements => { + page.elements = elements; + page.isResolved = true; + page.promise = null; + }); + } + + return page.promise.then(() => page.elements[indexInPage]); + } +} diff --git a/src/vs/base/test/common/paging.test.ts b/src/vs/base/test/common/paging.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..3460264ae83bd79d612141b8452b12ad887bcfa8 --- /dev/null +++ b/src/vs/base/test/common/paging.test.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { IPager, PagedModel } from 'vs/base/common/paging'; +import { TPromise } from 'vs/base/common/winjs.base'; + +suite('PagedModel', () => { + + let model: PagedModel; + + setup(() => { + const pager: IPager = { + firstPage: [0, 1, 2, 3, 4], + pageSize: 5, + total: 100, + getPage: pageIndex => TPromise.as([0, 1, 2, 3, 4].map(i => i + (pageIndex * 5))) + }; + + model = new PagedModel(pager); + }); + + test('isResolved', () => { + assert(model.isResolved(0)); + assert(model.isResolved(1)); + assert(model.isResolved(2)); + assert(model.isResolved(3)); + assert(model.isResolved(4)); + assert(!model.isResolved(5)); + assert(!model.isResolved(6)); + assert(!model.isResolved(7)); + assert(!model.isResolved(8)); + assert(!model.isResolved(9)); + assert(!model.isResolved(10)); + assert(!model.isResolved(99)); + }); + + test('resolve single', () => { + assert(!model.isResolved(5)); + + return model.resolve(5).then(() => { + assert(model.isResolved(5)); + }); + }); + + test('resolve page', () => { + assert(!model.isResolved(5)); + assert(!model.isResolved(6)); + assert(!model.isResolved(7)); + assert(!model.isResolved(8)); + assert(!model.isResolved(9)); + assert(!model.isResolved(10)); + + return model.resolve(5).then(() => { + assert(model.isResolved(5)); + assert(model.isResolved(6)); + assert(model.isResolved(7)); + assert(model.isResolved(8)); + assert(model.isResolved(9)); + assert(!model.isResolved(10)); + }); + }); + + test('resolve page 2', () => { + assert(!model.isResolved(5)); + assert(!model.isResolved(6)); + assert(!model.isResolved(7)); + assert(!model.isResolved(8)); + assert(!model.isResolved(9)); + assert(!model.isResolved(10)); + + return model.resolve(10).then(() => { + assert(!model.isResolved(5)); + assert(!model.isResolved(6)); + assert(!model.isResolved(7)); + assert(!model.isResolved(8)); + assert(!model.isResolved(9)); + assert(model.isResolved(10)); + }); + }); +}); \ No newline at end of file