Skip to content

Commit 70e1e9b

Browse files
Eric Amodioeamodio
Eric Amodio
authored andcommitted
Adds timeline view & api providers (wip) — #84297
1 parent a592b87 commit 70e1e9b

13 files changed

+770
-4
lines changed

src/vs/vscode.proposed.d.ts

+122
Original file line numberDiff line numberDiff line change
@@ -1469,4 +1469,126 @@ declare module 'vscode' {
14691469
}
14701470

14711471
//#endregion
1472+
1473+
//#region eamodio - timeline: https://github.com/microsoft/vscode/issues/84297
1474+
1475+
export class TimelineItem {
1476+
/**
1477+
* A date for when the timeline item occurred
1478+
*/
1479+
date: number;
1480+
1481+
/**
1482+
* 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.
1483+
*/
1484+
source: string;
1485+
1486+
1487+
/**
1488+
* A human-readable string describing the timeline item. When `falsy`, it is derived from [resourceUri](#TreeItem.resourceUri).
1489+
*/
1490+
label: string;
1491+
1492+
/**
1493+
* Optional id for the timeline item. See [TreeItem.id](#TreeItem.id) for more details.
1494+
*/
1495+
id?: string;
1496+
1497+
/**
1498+
* The icon path or [ThemeIcon](#ThemeIcon) for the timeline item. See [TreeItem.iconPath](#TreeItem.iconPath) for more details.
1499+
*/
1500+
iconPath?: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon;
1501+
1502+
/**
1503+
* A human readable string describing less prominent details of the timeline item. See [TreeItem.description](#TreeItem.description) for more details.
1504+
*/
1505+
description?: string;
1506+
1507+
/**
1508+
* The [uri](#Uri) of the resource representing the timeline item (if any). See [TreeItem.resourceUri](#TreeItem.resourceUri) for more details.
1509+
*/
1510+
resourceUri?: Uri;
1511+
1512+
/**
1513+
* The tooltip text when you hover over the timeline item.
1514+
*/
1515+
tooltip?: string | undefined;
1516+
1517+
/**
1518+
* The [command](#Command) that should be executed when the timeline item is selected.
1519+
*/
1520+
command?: Command;
1521+
1522+
/**
1523+
* Context value of the timeline item. See [TreeItem.contextValue](#TreeItem.contextValue) for more details.
1524+
*/
1525+
contextValue?: string;
1526+
1527+
/**
1528+
* @param label A human-readable string describing the timeline item
1529+
* @param date A date for when the timeline item occurred
1530+
* @param source A human-readable string describing the source of the timeline item
1531+
*/
1532+
constructor(label: string, date: number, source: string);
1533+
}
1534+
1535+
export interface TimelimeAddEvent {
1536+
1537+
/**
1538+
* An array of timeline items which have been added.
1539+
*/
1540+
readonly items: readonly TimelineItem[];
1541+
1542+
/**
1543+
* The uri of the file to which the timeline items belong.
1544+
*/
1545+
readonly uri: Uri;
1546+
}
1547+
1548+
export interface TimelimeChangeEvent {
1549+
1550+
/**
1551+
* The date after which the timeline has changed. If `undefined` the entire timeline will be reset.
1552+
*/
1553+
readonly since?: Date;
1554+
1555+
/**
1556+
* The uri of the file to which the timeline changed.
1557+
*/
1558+
readonly uri: Uri;
1559+
}
1560+
1561+
export interface TimelineProvider {
1562+
// onDidAdd?: Event<TimelimeAddEvent>;
1563+
// onDidChange?: Event<TimelimeChangeEvent>;
1564+
id: string;
1565+
1566+
/**
1567+
* Provide [timeline items](#TimelineItem) for a [Uri](#Uri) after a particular date.
1568+
*
1569+
* @param uri The uri of the file to provide the timeline for.
1570+
* @param since A date after which timeline items should be provided.
1571+
* @param token A cancellation token.
1572+
* @return An array of timeline items or a thenable that resolves to such. The lack of a result
1573+
* can be signaled by returning `undefined`, `null`, or an empty array.
1574+
*/
1575+
provideTimeline(uri: Uri, since: number, token: CancellationToken): ProviderResult<TimelineItem[]>;
1576+
}
1577+
1578+
export namespace workspace {
1579+
/**
1580+
* Register a timeline provider.
1581+
*
1582+
* Multiple providers can be registered. In that case, providers are asked in
1583+
* parallel and the results are merged. A failing provider (rejected promise or exception) will
1584+
* not cause a failure of the whole operation.
1585+
*
1586+
* @param selector A selector that defines the documents this provider is applicable to.
1587+
* @param provider A timeline provider.
1588+
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
1589+
*/
1590+
export function registerTimelineProvider(selector: DocumentSelector, provider: TimelineProvider): Disposable;
1591+
}
1592+
1593+
//#endregion
14721594
}

src/vs/workbench/api/browser/extensionHost.contribution.ts

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import './mainThreadTask';
6060
import './mainThreadLabelService';
6161
import './mainThreadTunnelService';
6262
import './mainThreadAuthentication';
63+
import './mainThreadTimeline';
6364
import 'vs/workbench/api/common/apiCommands';
6465

6566
export class ExtensionPoints implements IWorkbenchContribution {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { MainContext, MainThreadTimelineShape, IExtHostContext, ExtHostTimelineShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
7+
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
8+
import { ITimelineService, TimelineItem } from 'vs/workbench/contrib/timeline/common/timeline';
9+
import { URI } from 'vs/base/common/uri';
10+
import { CancellationToken } from 'vs/base/common/cancellation';
11+
12+
@extHostNamedCustomer(MainContext.MainThreadTimeline)
13+
export class MainThreadTimeline implements MainThreadTimelineShape {
14+
15+
private readonly _proxy: ExtHostTimelineShape;
16+
17+
constructor(
18+
context: IExtHostContext,
19+
@ITimelineService private readonly _timelineService: ITimelineService
20+
) {
21+
this._proxy = context.getProxy(ExtHostContext.ExtHostTimeline);
22+
}
23+
24+
$getTimeline(uri: URI, since: number, token: CancellationToken): Promise<TimelineItem[]> {
25+
return this._timelineService.getTimeline(uri, since, token);
26+
}
27+
28+
$registerTimelineProvider(key: string, id: string): void {
29+
console.log(`MainThreadTimeline#registerTimelineProvider: key=${key}`);
30+
31+
const proxy = this._proxy;
32+
this._timelineService.registerTimelineProvider(key, {
33+
id: id,
34+
provideTimeline(uri: URI, since: number, token: CancellationToken) {
35+
return proxy.$getTimeline(key, uri, since, token);
36+
}
37+
});
38+
}
39+
40+
$unregisterTimelineProvider(key: string): void {
41+
console.log(`MainThreadTimeline#unregisterTimelineProvider: key=${key}`);
42+
this._timelineService.unregisterTimelineProvider(key);
43+
}
44+
45+
dispose(): void {
46+
// noop
47+
}
48+
}

src/vs/workbench/api/common/extHost.api.impl.ts

+16-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as nls from 'vs/nls';
7-
import { CancellationTokenSource } from 'vs/base/common/cancellation';
7+
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
88
import * as errors from 'vs/base/common/errors';
99
import { Emitter, Event } from 'vs/base/common/event';
1010
import * as path from 'vs/base/common/path';
@@ -71,6 +71,7 @@ import { ExtHostTheming } from 'vs/workbench/api/common/extHostTheming';
7171
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
7272
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
7373
import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication';
74+
import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline';
7475

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

136138
// Check that no named customers are missing
137139
const expected: ProxyIdentifier<any>[] = values(ExtHostContext);
@@ -761,6 +763,17 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
761763
onDidTunnelsChange: (listener, thisArg?, disposables?) => {
762764
checkProposedApiEnabled(extension);
763765
return extHostTunnelService.onDidTunnelsChange(listener, thisArg, disposables);
766+
767+
},
768+
registerTimelineProvider: (scheme: string, provider: vscode.TimelineProvider) => {
769+
checkProposedApiEnabled(extension);
770+
return extHostTimelineService.registerTimelineProvider(extension.identifier, {
771+
id: provider.id,
772+
async provideTimeline(uri: URI, since: number, token: CancellationToken) {
773+
const results = await provider.provideTimeline(uri, since, token);
774+
return results ?? [];
775+
}
776+
});
764777
}
765778
};
766779

@@ -984,7 +997,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
984997
Decoration: extHostTypes.Decoration,
985998
WebviewContentState: extHostTypes.WebviewContentState,
986999
UIKind: UIKind,
987-
ColorThemeKind: extHostTypes.ColorThemeKind
1000+
ColorThemeKind: extHostTypes.ColorThemeKind,
1001+
TimelineItem: extHostTypes.TimelineItem
9881002
};
9891003
};
9901004
}

src/vs/workbench/api/common/extHost.protocol.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import { SaveReason } from 'vs/workbench/common/editor';
4949
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
5050
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
5151
import { TunnelOptions } from 'vs/platform/remote/common/tunnel';
52+
import { TimelineItem } from 'vs/workbench/contrib/timeline/common/timeline';
5253

5354
export interface IEnvironment {
5455
isExtensionDevelopmentDebug: boolean;
@@ -797,6 +798,13 @@ export interface MainThreadTunnelServiceShape extends IDisposable {
797798
$setCandidateFilter(): Promise<void>;
798799
}
799800

801+
export interface MainThreadTimelineShape extends IDisposable {
802+
$registerTimelineProvider(key: string, id: string): void;
803+
$unregisterTimelineProvider(key: string): void;
804+
805+
$getTimeline(resource: UriComponents, since: number, token: CancellationToken): Promise<TimelineItem[]>;
806+
}
807+
800808
// -- extension host
801809

802810
export interface ExtHostCommandsShape {
@@ -1441,6 +1449,12 @@ export interface ExtHostTunnelServiceShape {
14411449
$onDidTunnelsChange(): Promise<void>;
14421450
}
14431451

1452+
export interface ExtHostTimelineShape {
1453+
// $registerTimelineProvider(handle: number, provider: TimelineProvider): void;
1454+
1455+
$getTimeline(key: string, uri: UriComponents, since: number, token: CancellationToken): Promise<TimelineItem[]>;
1456+
}
1457+
14441458
// --- proxy identifiers
14451459

14461460
export const MainContext = {
@@ -1484,7 +1498,8 @@ export const MainContext = {
14841498
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
14851499
MainThreadLabelService: createMainId<MainThreadLabelServiceShape>('MainThreadLabelService'),
14861500
MainThreadTheming: createMainId<MainThreadThemingShape>('MainThreadTheming'),
1487-
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService')
1501+
MainThreadTunnelService: createMainId<MainThreadTunnelServiceShape>('MainThreadTunnelService'),
1502+
MainThreadTimeline: createMainId<MainThreadTimelineShape>('MainThreadTimeline')
14881503
};
14891504

14901505
export const ExtHostContext = {
@@ -1521,5 +1536,6 @@ export const ExtHostContext = {
15211536
ExtHostLabelService: createMainId<ExtHostLabelServiceShape>('ExtHostLabelService'),
15221537
ExtHostTheming: createMainId<ExtHostThemingShape>('ExtHostTheming'),
15231538
ExtHostTunnelService: createMainId<ExtHostTunnelServiceShape>('ExtHostTunnelService'),
1524-
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication')
1539+
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication'),
1540+
ExtHostTimeline: createMainId<ExtHostTimelineShape>('ExtHostTimeline')
15251541
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
import { UriComponents, URI } from 'vs/base/common/uri';
8+
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
9+
import { ExtHostTimelineShape, MainThreadTimelineShape, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
10+
import { TimelineItem, TimelineProvider, toKey } from 'vs/workbench/contrib/timeline/common/timeline';
11+
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
12+
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
13+
14+
export interface IExtHostTimeline extends ExtHostTimelineShape {
15+
readonly _serviceBrand: undefined;
16+
$getTimeline(key: string, uri: UriComponents, since: number, token: vscode.CancellationToken): Promise<TimelineItem[]>;
17+
}
18+
19+
export const IExtHostTimeline = createDecorator<IExtHostTimeline>('IExtHostTimeline');
20+
21+
export class ExtHostTimeline implements IExtHostTimeline {
22+
_serviceBrand: undefined;
23+
24+
private _proxy: MainThreadTimelineShape;
25+
26+
private _providers = new Map<string, TimelineProvider>();
27+
28+
constructor(
29+
mainContext: IMainContext,
30+
31+
) {
32+
this._proxy = mainContext.getProxy(MainContext.MainThreadTimeline);
33+
34+
this.registerTimelineProvider('bar', {
35+
id: 'baz',
36+
async provideTimeline(uri: URI, since: number, token: vscode.CancellationToken) {
37+
return [
38+
{
39+
id: '1',
40+
label: 'Bar Timeline1',
41+
description: uri.toString(true),
42+
detail: new Date().toString(),
43+
date: Date.now(),
44+
source: 'log'
45+
},
46+
{
47+
id: '2',
48+
label: 'Bar Timeline2',
49+
description: uri.toString(true),
50+
detail: new Date(Date.now() - 100).toString(),
51+
date: Date.now() - 100,
52+
source: 'log'
53+
}
54+
];
55+
}
56+
});
57+
}
58+
59+
async $getTimeline(key: string, uri: UriComponents, since: number, token: vscode.CancellationToken): Promise<TimelineItem[]> {
60+
const provider = this._providers.get(key);
61+
return provider?.provideTimeline(URI.revive(uri), since, token) ?? [];
62+
}
63+
64+
registerTimelineProvider(extension: ExtensionIdentifier | string, provider: TimelineProvider): IDisposable {
65+
console.log(`ExtHostTimeline#registerTimelineProvider: extension=${extension.toString()}, provider=${provider.id}`);
66+
67+
const key = toKey(extension, provider.id);
68+
if (this._providers.has(key)) {
69+
throw new Error(`Timeline Provider ${key} already exists.`);
70+
}
71+
72+
this._proxy.$registerTimelineProvider(key, provider.id);
73+
this._providers.set(key, provider);
74+
75+
return toDisposable(() => {
76+
this._providers.delete(key);
77+
this._proxy.$unregisterTimelineProvider(key);
78+
});
79+
}
80+
}

src/vs/workbench/api/common/extHostTypes.ts

+9
Original file line numberDiff line numberDiff line change
@@ -2544,3 +2544,12 @@ export enum ColorThemeKind {
25442544
}
25452545

25462546
//#endregion Theming
2547+
2548+
//#region Timeline
2549+
2550+
@es5ClassCompat
2551+
export class TimelineItem implements vscode.TimelineItem {
2552+
constructor(public label: string, public date: number, public source: string) { }
2553+
}
2554+
2555+
//#endregion Timeline
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/

0 commit comments

Comments
 (0)