Skip to content

Commit

Permalink
Adds timeline view & api providers (wip) — #84297
Browse files Browse the repository at this point in the history
  • Loading branch information
Eric Amodio authored and eamodio committed Jan 27, 2020
1 parent a592b87 commit 70e1e9b
Show file tree
Hide file tree
Showing 13 changed files with 770 additions and 4 deletions.
122 changes: 122 additions & 0 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1469,4 +1469,126 @@ declare module 'vscode' {
}

//#endregion

//#region eamodio - timeline: https://github.com/microsoft/vscode/issues/84297

export class TimelineItem {
/**
* A date for when the timeline item occurred
*/
date: number;

/**
* A human-readable string describing the source of the timeline item. This can be used for filtering by sources so keep it consistent across timeline item types.
*/
source: string;


/**
* A human-readable string describing the timeline item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri).
*/
label: string;

/**
* Optional id for the timeline item. See [TreeItem.id](#TreeItem.id) for more details.
*/
id?: string;

/**
* The icon path or [ThemeIcon](#ThemeIcon) for the timeline item. See [TreeItem.iconPath](#TreeItem.iconPath) for more details.
*/
iconPath?: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon;

/**
* A human readable string describing less prominent details of the timeline item. See [TreeItem.description](#TreeItem.description) for more details.
*/
description?: string;

/**
* The [uri](#Uri) of the resource representing the timeline item (if any). See [TreeItem.resourceUri](#TreeItem.resourceUri) for more details.
*/
resourceUri?: Uri;

/**
* The tooltip text when you hover over the timeline item.
*/
tooltip?: string | undefined;

/**
* The [command](#Command) that should be executed when the timeline item is selected.
*/
command?: Command;

/**
* Context value of the timeline item. See [TreeItem.contextValue](#TreeItem.contextValue) for more details.
*/
contextValue?: string;

/**
* @param label A human-readable string describing the timeline item
* @param date A date for when the timeline item occurred
* @param source A human-readable string describing the source of the timeline item
*/
constructor(label: string, date: number, source: string);
}

export interface TimelimeAddEvent {

/**
* An array of timeline items which have been added.
*/
readonly items: readonly TimelineItem[];

/**
* The uri of the file to which the timeline items belong.
*/
readonly uri: Uri;
}

export interface TimelimeChangeEvent {

/**
* The date after which the timeline has changed. If `undefined` the entire timeline will be reset.
*/
readonly since?: Date;

/**
* The uri of the file to which the timeline changed.
*/
readonly uri: Uri;
}

export interface TimelineProvider {
// onDidAdd?: Event<TimelimeAddEvent>;
// onDidChange?: Event<TimelimeChangeEvent>;
id: string;

/**
* Provide [timeline items](#TimelineItem) for a [Uri](#Uri) after a particular date.
*
* @param uri The uri of the file to provide the timeline for.
* @param since A date after which timeline items should be provided.
* @param token A cancellation token.
* @return An array of timeline items 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, since: number, token: CancellationToken): ProviderResult<TimelineItem[]>;
}

export namespace workspace {
/**
* Register a timeline provider.
*
* Multiple providers can be registered. In that case, providers are asked in
* parallel and the results are merged. A failing provider (rejected promise or exception) will
* not cause a failure of the whole operation.
*
* @param selector A selector that defines the documents this provider is applicable to.
* @param provider A timeline provider.
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
*/
export function registerTimelineProvider(selector: DocumentSelector, provider: TimelineProvider): Disposable;
}

//#endregion
}
1 change: 1 addition & 0 deletions src/vs/workbench/api/browser/extensionHost.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import './mainThreadTask';
import './mainThreadLabelService';
import './mainThreadTunnelService';
import './mainThreadAuthentication';
import './mainThreadTimeline';
import 'vs/workbench/api/common/apiCommands';

export class ExtensionPoints implements IWorkbenchContribution {
Expand Down
48 changes: 48 additions & 0 deletions src/vs/workbench/api/browser/mainThreadTimeline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { MainContext, MainThreadTimelineShape, IExtHostContext, ExtHostTimelineShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ITimelineService, TimelineItem } from 'vs/workbench/contrib/timeline/common/timeline';
import { URI } from 'vs/base/common/uri';
import { CancellationToken } from 'vs/base/common/cancellation';

@extHostNamedCustomer(MainContext.MainThreadTimeline)
export class MainThreadTimeline implements MainThreadTimelineShape {

private readonly _proxy: ExtHostTimelineShape;

constructor(
context: IExtHostContext,
@ITimelineService private readonly _timelineService: ITimelineService
) {
this._proxy = context.getProxy(ExtHostContext.ExtHostTimeline);
}

$getTimeline(uri: URI, since: number, token: CancellationToken): Promise<TimelineItem[]> {
return this._timelineService.getTimeline(uri, since, token);
}

$registerTimelineProvider(key: string, id: string): void {
console.log(`MainThreadTimeline#registerTimelineProvider: key=${key}`);

const proxy = this._proxy;
this._timelineService.registerTimelineProvider(key, {
id: id,
provideTimeline(uri: URI, since: number, token: CancellationToken) {
return proxy.$getTimeline(key, uri, since, token);
}
});
}

$unregisterTimelineProvider(key: string): void {
console.log(`MainThreadTimeline#unregisterTimelineProvider: key=${key}`);
this._timelineService.unregisterTimelineProvider(key);
}

dispose(): void {
// noop
}
}
18 changes: 16 additions & 2 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import * as path from 'vs/base/common/path';
Expand Down Expand Up @@ -71,6 +71,7 @@ import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming';
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';

export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
Expand Down Expand Up @@ -132,6 +133,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHostLabelService, new ExtHostLabelService(rpcProtocol));
const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol));
const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol));
const extHostTimelineService = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol));

// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = values(ExtHostContext);
Expand Down Expand Up @@ -761,6 +763,17 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
onDidTunnelsChange: (listener, thisArg?, disposables?) => {
checkProposedApiEnabled(extension);
return extHostTunnelService.onDidTunnelsChange(listener, thisArg, disposables);

},
registerTimelineProvider: (scheme: string, provider: vscode.TimelineProvider) => {
checkProposedApiEnabled(extension);
return extHostTimelineService.registerTimelineProvider(extension.identifier, {
id: provider.id,
async provideTimeline(uri: URI, since: number, token: CancellationToken) {
const results = await provider.provideTimeline(uri, since, token);
return results ?? [];
}
});
}
};

Expand Down Expand Up @@ -984,7 +997,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
Decoration: extHostTypes.Decoration,
WebviewContentState: extHostTypes.WebviewContentState,
UIKind: UIKind,
ColorThemeKind: extHostTypes.ColorThemeKind
ColorThemeKind: extHostTypes.ColorThemeKind,
TimelineItem: extHostTypes.TimelineItem
};
};
}
Expand Down
20 changes: 18 additions & 2 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +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 } from 'vs/workbench/contrib/timeline/common/timeline';

export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
Expand Down Expand Up @@ -797,6 +798,13 @@ export interface MainThreadTunnelServiceShape extends IDisposable {
$setCandidateFilter(): Promise<void>;
}

export interface MainThreadTimelineShape extends IDisposable {
$registerTimelineProvider(key: string, id: string): void;
$unregisterTimelineProvider(key: string): void;

$getTimeline(resource: UriComponents, since: number, token: CancellationToken): Promise<TimelineItem[]>;
}

// -- extension host

export interface ExtHostCommandsShape {
Expand Down Expand Up @@ -1441,6 +1449,12 @@ export interface ExtHostTunnelServiceShape {
$onDidTunnelsChange(): Promise<void>;
}

export interface ExtHostTimelineShape {
// $registerTimelineProvider(handle: number, provider: TimelineProvider): void;

$getTimeline(key: string, uri: UriComponents, since: number, token: CancellationToken): Promise<TimelineItem[]>;
}

// --- proxy identifiers

export const MainContext = {
Expand Down Expand Up @@ -1484,7 +1498,8 @@ export const MainContext = {
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
MainThreadLabelService: createMainId<MainThreadLabelServiceShape>('MainThreadLabelService'),
MainThreadTheming: createMainId<MainThreadThemingShape>('MainThreadTheming'),
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService')
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService'),
MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline')
};

export const ExtHostContext = {
Expand Down Expand Up @@ -1521,5 +1536,6 @@ export const ExtHostContext = {
ExtHostLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService'),
ExtHostTheming: createMainId<ExtHostThemingShape>('ExtHostTheming'),
ExtHostTunnelService: createMainId<ExtHostTunnelServiceShape>('ExtHostTunnelService'),
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication')
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication'),
ExtHostTimeline: createMainId<ExtHostTimelineShape>('ExtHostTimeline')
};
80 changes: 80 additions & 0 deletions src/vs/workbench/api/common/extHostTimeline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

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 { TimelineItem, TimelineProvider, toKey } from 'vs/workbench/contrib/timeline/common/timeline';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';

export interface IExtHostTimeline extends ExtHostTimelineShape {
readonly _serviceBrand: undefined;
$getTimeline(key: string, uri: UriComponents, since: number, token: vscode.CancellationToken): Promise<TimelineItem[]>;
}

export const IExtHostTimeline = createDecorator<IExtHostTimeline>('IExtHostTimeline');

export class ExtHostTimeline implements IExtHostTimeline {
_serviceBrand: undefined;

private _proxy: MainThreadTimelineShape;

private _providers = new Map<string, TimelineProvider>();

constructor(
mainContext: IMainContext,

) {
this._proxy = mainContext.getProxy(MainContext.MainThreadTimeline);

this.registerTimelineProvider('bar', {
id: 'baz',
async provideTimeline(uri: URI, since: number, token: vscode.CancellationToken) {
return [
{
id: '1',
label: 'Bar Timeline1',
description: uri.toString(true),
detail: new Date().toString(),
date: Date.now(),
source: 'log'
},
{
id: '2',
label: 'Bar Timeline2',
description: uri.toString(true),
detail: new Date(Date.now() - 100).toString(),
date: Date.now() - 100,
source: 'log'
}
];
}
});
}

async $getTimeline(key: string, uri: UriComponents, since: number, token: vscode.CancellationToken): Promise<TimelineItem[]> {
const provider = this._providers.get(key);
return provider?.provideTimeline(URI.revive(uri), since, token) ?? [];
}

registerTimelineProvider(extension: ExtensionIdentifier | string, provider: TimelineProvider): IDisposable {
console.log(`ExtHostTimeline#registerTimelineProvider: extension=${extension.toString()}, provider=${provider.id}`);

const key = toKey(extension, provider.id);
if (this._providers.has(key)) {
throw new Error(`Timeline Provider ${key} already exists.`);
}

this._proxy.$registerTimelineProvider(key, provider.id);
this._providers.set(key, provider);

return toDisposable(() => {
this._providers.delete(key);
this._proxy.$unregisterTimelineProvider(key);
});
}
}
9 changes: 9 additions & 0 deletions src/vs/workbench/api/common/extHostTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2544,3 +2544,12 @@ export enum ColorThemeKind {
}

//#endregion Theming

//#region Timeline

@es5ClassCompat
export class TimelineItem implements vscode.TimelineItem {
constructor(public label: string, public date: number, public source: string) { }
}

//#endregion Timeline
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Loading

0 comments on commit 70e1e9b

Please sign in to comment.