Repository: vscode
Issue: API support for Timeline view
Labels: feature-request,api,tree-views,scm,api-proposal,timeline

# Goals

Add support for a unified file-based timeline view to be added to the Explorer sidebar which will track the active document (similar to the Outline view). This new view can be contributed by multiple sources, e.g. save/undo points, source control commits, test runs/failures, etc. Events from all sources will be aggregated into a single chronologically-ordered view. 

# Proposal

```ts
	export class TimelineItem {
		/**
		 * A date for when the timeline item occurred
		 */
		date: Date;

		/**
		 * 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;

		/**
		 * Optional method to get the children of the timeline item (if any).
		 *
		 * @return Children of the timeline item (if any).
		 */
		getChildren?(): ProviderResult<TreeItem[]>;

		/**
		 * 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 | boolean;

		/**
		 * 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;

		/**
		 * [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the timeline item.
		 */
		collapsibleState?: TreeItemCollapsibleState;

		/**
		 * 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
		 * @param collapsibleState [TreeItemCollapsibleState](#TreeItemCollapsibleState) of the timeline item. Default is [TreeItemCollapsibleState.None](#TreeItemCollapsibleState.None)
		 */
		constructor(label: string, date: Date, source: string, collapsibleState?: TreeItemCollapsibleState);
	}

	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>;

		/**
		 * 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: Date, 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;
	}
```

A timeline source (e.g. an extension) registers a `TimelineProvider` for a set of documents. VS Code will then call the all the registered `TimelineProvider.provideTimeline` callbacks (in parallel) when the active editor changes and matches those registrations. The results will be merged into a unified set ordered by the `TimelineItem.date` and displayed in a new _File Timeline_ view in the Explorer sidebar.

A timeline provider can signal that new events have occurred via the `onDidAdd` event, providing the set of additional timeline items. A provider can also signal a refresh of its timeline items via the `onDidChange` event.

# Questions & Challenges

### API
- Should we provides a model/signal that a timeline provider should not be cached or more accurately flushed when the file is no longer the active editor? Basically an alternative to sending events, just call `provideTimeline` again
- Caching will be a bit challenging
  - How much do we keep around? And for how long?
- Should the `TimelimeChangeEvent` event provide a way to signal that an individual item(s) should be updated?
- Do we need a throttle on `onDidAdd`?
- Need to be careful with `id`s for the tree items, since they can come from multiple extensions. Probably should ensure a prefix or something per provider for any provided `id`s (or not give control over the `id`s at all) 

### Behavior
- Should tracking the active document be a toggle (like in GitLens where you can turn on/off tracking in the file history view on demand).
- Should there be a way to trigger a specific file/folder/uri timelime to be shown (which would also turn off active file tracking) -- again similar to GitLens
- Should the view support a refresh action that will drop timeline caches and re-request the timeline from all providers? (Hopefully we don't really need this)
- Should we support filtering based on the `source` of the timeline items?
/cc @jrieken 

Refs: https://github.com/microsoft/vscode/issues/83995