提交 69b30f6b 编写于 作者: E Eric Amodio

Adds paging support (wip)

上级 31eb2b1d
......@@ -6,7 +6,7 @@
import * as dayjs from 'dayjs';
import * as advancedFormat from 'dayjs/plugin/advancedFormat';
import * as relativeTime from 'dayjs/plugin/relativeTime';
import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, TimelineItem, TimelineProvider, Uri, workspace, TimelineChangeEvent } from 'vscode';
import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, Timeline, TimelineChangeEvent, TimelineCursor, TimelineItem, TimelineProvider, Uri, workspace } from 'vscode';
import { Model } from './model';
import { Repository } from './repository';
import { debounce } from './decorators';
......@@ -44,7 +44,7 @@ export class GitTimelineProvider implements TimelineProvider {
this._disposable.dispose();
}
async provideTimeline(uri: Uri, _token: CancellationToken): Promise<TimelineItem[]> {
async provideTimeline(uri: Uri, _cursor: TimelineCursor, _token: CancellationToken): Promise<Timeline> {
// console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`);
const repo = this._model.getRepository(uri);
......@@ -53,7 +53,7 @@ export class GitTimelineProvider implements TimelineProvider {
this._repoStatusDate = undefined;
this._repo = undefined;
return [];
return { items: [] };
}
if (this._repo?.root !== repo.root) {
......@@ -181,7 +181,7 @@ export class GitTimelineProvider implements TimelineProvider {
items.push(item);
}
return items;
return { items: items };
}
private onRepositoriesChanged(_repo: Repository) {
......
......@@ -1611,6 +1611,40 @@ declare module 'vscode' {
uri?: Uri;
}
export interface TimelineCursor {
/**
* A provider-defined cursor specifing the range of timeline items to be returned. Must be serializable.
*/
cursor?: any;
/**
* A flag to specify whether the timeline items requested are before or after (default) the provided cursor.
*/
before?: boolean;
/**
* The maximum number of timeline items that should be returned.
*/
limit?: number;
}
export interface Timeline {
/**
* A provider-defined cursor specifing the range of timeline items returned. Must be serializable.
*/
cursor?: any;
/**
* A flag which indicates whether there are any more items that weren't returned.
*/
more?: boolean;
/**
* An array of [timeline items](#TimelineItem).
*/
items: TimelineItem[];
}
export interface TimelineProvider {
/**
* An optional event to signal that the timeline for a source has changed.
......@@ -1633,10 +1667,11 @@ declare module 'vscode' {
*
* @param uri The [uri](#Uri) of the file to provide the timeline for.
* @param token A cancellation token.
* @return An array of timeline items or a thenable that resolves to such. The lack of a result
* @param cursor TBD
* @return The [timeline result](#TimelineResult) or a thenable that resolves to such. The lack of a result
* can be signaled by returning `undefined`, `null`, or an empty array.
*/
provideTimeline(uri: Uri, token: CancellationToken): ProviderResult<TimelineItem[]>;
provideTimeline(uri: Uri, cursor: TimelineCursor, token: CancellationToken): ProviderResult<Timeline>;
}
export namespace workspace {
......
......@@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { MainContext, MainThreadTimelineShape, IExtHostContext, ExtHostTimelineShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ITimelineService, TimelineItem, TimelineProviderDescriptor, TimelineChangeEvent } from 'vs/workbench/contrib/timeline/common/timeline';
import { TimelineChangeEvent, TimelineCursor, TimelineProviderDescriptor, ITimelineService } from 'vs/workbench/contrib/timeline/common/timeline';
@extHostNamedCustomer(MainContext.MainThreadTimeline)
export class MainThreadTimeline implements MainThreadTimelineShape {
......@@ -24,10 +24,6 @@ export class MainThreadTimeline implements MainThreadTimelineShape {
this._proxy = context.getProxy(ExtHostContext.ExtHostTimeline);
}
$getTimeline(uri: URI, token: CancellationToken): Promise<TimelineItem[]> {
return this._timelineService.getTimeline(uri, token);
}
$registerTimelineProvider(provider: TimelineProviderDescriptor): void {
this.logService.trace(`MainThreadTimeline#registerTimelineProvider: id=${provider.id}`);
......@@ -43,8 +39,8 @@ export class MainThreadTimeline implements MainThreadTimelineShape {
this._timelineService.registerTimelineProvider({
...provider,
onDidChange: onDidChange.event,
provideTimeline(uri: URI, token: CancellationToken) {
return proxy.$getTimeline(provider.id, uri, token);
provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken) {
return proxy.$getTimeline(provider.id, uri, cursor, token);
},
dispose() {
emitters.delete(provider.id);
......
......@@ -49,7 +49,7 @@ import { SaveReason } from 'vs/workbench/common/editor';
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
import { TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { TimelineItem, TimelineProviderDescriptor, TimelineChangeEvent, TimelineItemWithSource } from 'vs/workbench/contrib/timeline/common/timeline';
import { Timeline, TimelineChangeEvent, TimelineCursor, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
......@@ -803,8 +803,6 @@ export interface MainThreadTimelineShape extends IDisposable {
$registerTimelineProvider(provider: TimelineProviderDescriptor): void;
$unregisterTimelineProvider(source: string): void;
$emitTimelineChangeEvent(e: TimelineChangeEvent): void;
$getTimeline(uri: UriComponents, token: CancellationToken): Promise<TimelineItem[]>;
}
// -- extension host
......@@ -1459,7 +1457,7 @@ export interface ExtHostTunnelServiceShape {
}
export interface ExtHostTimelineShape {
$getTimeline(source: string, uri: UriComponents, token: CancellationToken): Promise<TimelineItemWithSource[]>;
$getTimeline(source: string, uri: UriComponents, cursor: TimelineCursor, token: CancellationToken): Promise<Timeline | undefined>;
}
// --- proxy identifiers
......
......@@ -7,7 +7,7 @@ import * as vscode from 'vscode';
import { UriComponents, URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ExtHostTimelineShape, MainThreadTimelineShape, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { TimelineItemWithSource, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline';
import { Timeline, TimelineCursor, TimelineItemWithSource, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline';
import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
......@@ -16,7 +16,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export interface IExtHostTimeline extends ExtHostTimelineShape {
readonly _serviceBrand: undefined;
$getTimeline(id: string, uri: UriComponents, token: vscode.CancellationToken): Promise<TimelineItemWithSource[]>;
$getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken): Promise<Timeline | undefined>;
}
export const IExtHostTimeline = createDecorator<IExtHostTimeline>('IExtHostTimeline');
......@@ -34,9 +34,9 @@ export class ExtHostTimeline implements IExtHostTimeline {
this._proxy = mainContext.getProxy(MainContext.MainThreadTimeline);
}
async $getTimeline(id: string, uri: UriComponents, token: vscode.CancellationToken): Promise<TimelineItemWithSource[]> {
async $getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken): Promise<Timeline | undefined> {
const provider = this._providers.get(id);
return provider?.provideTimeline(URI.revive(uri), token) ?? [];
return provider?.provideTimeline(URI.revive(uri), cursor, token);
}
registerTimelineProvider(scheme: string | string[], provider: vscode.TimelineProvider, extensionId: ExtensionIdentifier, commandConverter: CommandsConverter): IDisposable {
......@@ -53,15 +53,21 @@ export class ExtHostTimeline implements IExtHostTimeline {
...provider,
scheme: scheme,
onDidChange: undefined,
async provideTimeline(uri: URI, token: CancellationToken) {
async provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken) {
timelineDisposables.clear();
const results = await provider.provideTimeline(uri, token);
const result = await provider.provideTimeline(uri, cursor, token);
// Intentional == we don't know how a provider will respond
// eslint-disable-next-line eqeqeq
return results != null
? results.map(item => convertTimelineItem(item))
: [];
if (result == null) {
return undefined;
}
return {
...result,
source: provider.id,
items: result.items.map(convertTimelineItem)
};
},
dispose() {
disposable?.dispose();
......
......@@ -187,7 +187,7 @@ export class TimelinePane extends ViewPane {
let request = this._pendingRequests.get(source);
request?.tokenSource.dispose(true);
request = this.timelineService.getTimelineRequest(source, this._uri, new CancellationTokenSource())!;
request = this.timelineService.getTimeline(source, this._uri, {}, new CancellationTokenSource())!;
this._pendingRequests.set(source, request);
request.tokenSource.token.onCancellationRequested(() => this._pendingRequests.delete(source));
......@@ -199,7 +199,7 @@ export class TimelinePane extends ViewPane {
private async handleRequest(request: TimelineRequest) {
let items;
try {
items = await this.progressService.withProgress({ location: VIEWLET_ID }, () => request.items);
items = await this.progressService.withProgress({ location: VIEWLET_ID }, () => request.result.then(r => r?.items ?? []));
}
catch { }
......
......@@ -37,10 +37,24 @@ export interface TimelineChangeEvent {
uri?: URI;
}
export interface TimelineCursor {
cursor?: any;
before?: boolean;
limit?: number;
}
export interface Timeline {
source: string;
items: TimelineItemWithSource[];
cursor?: any;
more?: boolean;
}
export interface TimelineProvider extends TimelineProviderDescriptor, IDisposable {
onDidChange?: Event<TimelineChangeEvent>;
provideTimeline(uri: URI, token: CancellationToken): Promise<TimelineItemWithSource[]>;
provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken): Promise<Timeline | undefined>;
}
export interface TimelineProviderDescriptor {
......@@ -55,7 +69,7 @@ export interface TimelineProvidersChangeEvent {
}
export interface TimelineRequest {
readonly items: Promise<TimelineItemWithSource[]>;
readonly result: Promise<Timeline | undefined>;
readonly source: string;
readonly tokenSource: CancellationTokenSource;
readonly uri: URI;
......@@ -72,9 +86,7 @@ export interface ITimelineService {
getSources(): string[];
getTimeline(uri: URI, token: CancellationToken): Promise<TimelineItem[]>;
getTimelineRequest(id: string, uri: URI, tokenSource: CancellationTokenSource): TimelineRequest | undefined;
getTimeline(id: string, uri: URI, pagination: TimelineCursor, tokenSource: CancellationTokenSource): TimelineRequest | undefined;
}
const TIMELINE_SERVICE_ID = 'timeline';
......
......@@ -3,13 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
// import { basename } from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { ITimelineService, TimelineProvider, TimelineItem, TimelineChangeEvent, TimelineProvidersChangeEvent } from './timeline';
import { ITimelineService, TimelineChangeEvent, TimelineCursor, TimelineProvidersChangeEvent, TimelineProvider } from './timeline';
export class TimelineService implements ITimelineService {
_serviceBrand: undefined;
......@@ -81,42 +81,7 @@ export class TimelineService implements ITimelineService {
return [...this._providers.keys()];
}
async getTimeline(uri: URI, token: CancellationToken, predicate?: (provider: TimelineProvider) => boolean) {
this.logService.trace(`TimelineService#getTimeline(${uri.toString(true)})`);
const requests: Promise<[string, TimelineItem[]]>[] = [];
for (const provider of this._providers.values()) {
if (typeof provider.scheme === 'string') {
if (provider.scheme !== '*' && provider.scheme !== uri.scheme) {
continue;
}
} else if (!provider.scheme.includes(uri.scheme)) {
continue;
}
if (!(predicate?.(provider) ?? true)) {
continue;
}
requests.push(provider.provideTimeline(uri, token).then(p => [provider.id, p]));
}
const timelines = await Promise.all(requests);
const timeline = [];
for (const [source, items] of timelines) {
if (items.length === 0) {
continue;
}
timeline.push(...items.map(item => ({ ...item, source: source })));
}
timeline.sort((a, b) => b.timestamp - a.timestamp);
return timeline;
}
getTimelineRequest(id: string, uri: URI, tokenSource: CancellationTokenSource) {
getTimeline(id: string, uri: URI, cursor: TimelineCursor, tokenSource: CancellationTokenSource) {
this.logService.trace(`TimelineService#getTimeline(${id}): uri=${uri.toString(true)}`);
const provider = this._providers.get(id);
......@@ -133,12 +98,16 @@ export class TimelineService implements ITimelineService {
}
return {
items: provider.provideTimeline(uri, tokenSource.token)
.then(items => {
items = items.map(item => ({ ...item, source: provider.id }));
items.sort((a, b) => (b.timestamp - a.timestamp) || b.source.localeCompare(a.source, undefined, { numeric: true, sensitivity: 'base' }));
result: provider.provideTimeline(uri, cursor, tokenSource.token)
.then(result => {
if (result === undefined) {
return undefined;
}
result.items = result.items.map(item => ({ ...item, source: provider.id }));
result.items.sort((a, b) => (b.timestamp - a.timestamp) || b.source.localeCompare(a.source, undefined, { numeric: true, sensitivity: 'base' }));
return items;
return result;
}),
source: provider.id,
tokenSource: tokenSource,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册