API support for Timeline view
See original GitHub issueGoals
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
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 providedid
s (or not give control over theid
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
Issue Analytics
- State:
- Created 4 years ago
- Reactions:92
- Comments:26 (17 by maintainers)
Top GitHub Comments
Thanks @eamodio - What would you say is the main argument against supporting history trees in the Timeline view? is it the complexity and effort that it may require to do so while properly delivering a great / easy / intuitive UX for the user? Or are there any other reasons?
In my mind, there are good UX designs that could accommodate tree views and make them optional if needed. In case it helps, below is a screenshot and animation of a similar feature (for navigating the full undo history of a file) in Emacs:
Screenshot:
Video example
Description of the animation: The video tries to be self-explanatory, but just in case, here’s a detailed description. The user starts typing text on a buffer (editor in VScode), then undoing some of the typing, and then typing text again. At a specific point in time the user invokes a command called “undo-tree” (equivalent to our Timeline view) which splits the screen in half and shows:
Once the undo tree is visible, the user switches cursor focus to the tree in the lower half, and the user navigates up and down the tree (i.e. history of the original file) via keyboard shortcuts. As the user is visiting different nodes, the original file (at the top) is updating its contents to reflect the state of the file corresponding to that node, and at any point in time, the user can switch focus to the original file in the top half, and continue editing from that state.
Note also that when the focus is on the tree, the user can switch navigation branches in the tree from a node with multiple children. Doing so doesn’t change the “current node” where the user is sitting, it just simply enables a different path the user can take via up/down.
Something that is not shown in this video is that the undo tree is not persisted anywhere (it’s actually lost when a file is closed). But that’s ok. The undo history of a file in most editors (VSCode included?) isn’t really saved, since it only refers to an “editing session” while the user has both the editor and file open.
Good idea for such a feature ! I made my own tree in my extension local-history. I’ll contribute to this timeline view…