Skip to content

Commit 90df2d0

Browse files
author
Eric Amodio
committed
Adds Open Timeline command to explorer files
Adds follow/unfollow actions to the timeline view Reworks timeline view commands
1 parent fb2ea11 commit 90df2d0

4 files changed

Lines changed: 143 additions & 108 deletions

File tree

src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts

Lines changed: 16 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,19 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
99
import { Registry } from 'vs/platform/registry/common/platform';
1010
import { IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions } from 'vs/workbench/common/views';
1111
import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/browser/explorerViewlet';
12-
import { ITimelineService } from 'vs/workbench/contrib/timeline/common/timeline';
12+
import { ITimelineService, TimelinePaneId } from 'vs/workbench/contrib/timeline/common/timeline';
1313
import { TimelineService } from 'vs/workbench/contrib/timeline/common/timelineService';
1414
import { TimelinePane } from './timelinePane';
1515
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
1616
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
1717
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
1818
import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
1919
import product from 'vs/platform/product/common/product';
20+
import { ExplorerFolderContext } from 'vs/workbench/contrib/files/common/files';
21+
import { ResourceContextKey } from 'vs/workbench/common/resources';
2022

2123
export class TimelinePaneDescriptor implements IViewDescriptor {
22-
readonly id = TimelinePane.ID;
24+
readonly id = TimelinePaneId;
2325
readonly name = TimelinePane.TITLE;
2426
readonly ctorDescriptor = new SyncDescriptor(TimelinePane);
2527
readonly when = ContextKeyExpr.equals('config.timeline.showView', true);
@@ -56,90 +58,30 @@ configurationRegistry.registerConfiguration({
5658

5759
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews([new TimelinePaneDescriptor()], VIEW_CONTAINER);
5860

59-
namespace TimelineViewRefreshAction {
61+
namespace OpenTimelineAction {
6062

61-
export const ID = 'timeline.refresh';
62-
export const LABEL = localize('timeline.refreshView', "Refresh");
63+
export const ID = 'files.openTimeline';
64+
export const LABEL = localize('files.openTimeline', "Open Timeline");
6365

6466
export function handler(): ICommandHandler {
6567
return (accessor, arg) => {
6668
const service = accessor.get(ITimelineService);
67-
return service.reset();
69+
return service.setUri(arg);
6870
};
6971
}
7072
}
7173

72-
CommandsRegistry.registerCommand(TimelineViewRefreshAction.ID, TimelineViewRefreshAction.handler());
74+
CommandsRegistry.registerCommand(OpenTimelineAction.ID, OpenTimelineAction.handler());
7375

74-
// namespace TimelineViewRefreshHardAction {
75-
76-
// export const ID = 'timeline.refreshHard';
77-
// export const LABEL = localize('timeline.refreshHard', "Refresh (Hard)");
78-
79-
// export function handler(fetch?: 'all' | 'more'): ICommandHandler {
80-
// return (accessor, arg) => {
81-
// const service = accessor.get(ITimelineService);
82-
// return service.refresh(fetch);
83-
// };
84-
// }
85-
// }
86-
87-
// CommandsRegistry.registerCommand(TimelineViewRefreshAction.ID, TimelineViewRefreshAction.handler());
88-
89-
// namespace TimelineViewLoadMoreAction {
90-
91-
// export const ID = 'timeline.loadMore';
92-
// export const LABEL = localize('timeline.loadMoreInView', "Load More");
93-
94-
// export function handler(): ICommandHandler {
95-
// return (accessor, arg) => {
96-
// const service = accessor.get(ITimelineService);
97-
// return service.refresh('more');
98-
// };
99-
// }
100-
// }
101-
102-
// CommandsRegistry.registerCommand(TimelineViewLoadMoreAction.ID, TimelineViewLoadMoreAction.handler());
103-
104-
// namespace TimelineViewLoadAllAction {
105-
106-
// export const ID = 'timeline.loadAll';
107-
// export const LABEL = localize('timeline.loadAllInView', "Load All");
108-
109-
// export function handler(): ICommandHandler {
110-
// return (accessor, arg) => {
111-
// const service = accessor.get(ITimelineService);
112-
// return service.refresh('all');
113-
// };
114-
// }
115-
// }
116-
117-
// CommandsRegistry.registerCommand(TimelineViewLoadAllAction.ID, TimelineViewLoadAllAction.handler());
118-
119-
MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({
120-
group: 'navigation',
76+
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, ({
77+
group: '4_timeline',
12178
order: 1,
12279
command: {
123-
id: TimelineViewRefreshAction.ID,
124-
title: TimelineViewRefreshAction.LABEL,
125-
icon: { id: 'codicon/refresh' }
126-
}
80+
id: OpenTimelineAction.ID,
81+
title: localize(OpenTimelineAction.LABEL, "Open Timeline"),
82+
icon: { id: 'codicon/history' }
83+
},
84+
when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.HasResource)
12785
}));
12886

129-
// MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({
130-
// group: 'navigation',
131-
// order: 2,
132-
// command: {
133-
// id: TimelineViewLoadMoreAction.ID,
134-
// title: TimelineViewLoadMoreAction.LABEL,
135-
// icon: { id: 'codicon/unfold' }
136-
// },
137-
// alt: {
138-
// id: TimelineViewLoadAllAction.ID,
139-
// title: TimelineViewLoadAllAction.LABEL,
140-
// icon: { id: 'codicon/unfold' }
141-
142-
// }
143-
// }));
144-
14587
registerSingleton(ITimelineService, TimelineService, true);

src/vs/workbench/contrib/timeline/browser/timelinePane.ts

Lines changed: 112 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/vie
1818
import { TreeResourceNavigator, WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
1919
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
2020
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
21-
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
21+
import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
2222
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
2323
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
24-
import { ITimelineService, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvidersChangeEvent, TimelineRequest, Timeline } from 'vs/workbench/contrib/timeline/common/timeline';
24+
import { ITimelineService, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvidersChangeEvent, TimelineRequest, Timeline, TimelinePaneId } from 'vs/workbench/contrib/timeline/common/timeline';
2525
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
2626
import { SideBySideEditor, toResource } from 'vs/workbench/common/editor';
27-
import { ICommandService } from 'vs/platform/commands/common/commands';
27+
import { ICommandService, CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
2828
import { IThemeService, LIGHT, ThemeIcon } from 'vs/platform/theme/common/themeService';
2929
import { IViewDescriptorService } from 'vs/workbench/common/views';
3030
import { basename } from 'vs/base/common/path';
@@ -34,7 +34,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
3434
import { IActionViewItemProvider, ActionBar, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
3535
import { IAction, ActionRunner } from 'vs/base/common/actions';
3636
import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
37-
import { MenuItemAction, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
37+
import { MenuItemAction, IMenuService, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
3838
import { fromNow } from 'vs/base/common/date';
3939
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
4040

@@ -85,8 +85,9 @@ interface TimelineCursors {
8585
more: boolean;
8686
}
8787

88+
export const TimelineFollowActiveEditorContext = new RawContextKey<boolean>('timelineFollowActiveEditor', true);
89+
8890
export class TimelinePane extends ViewPane {
89-
static readonly ID = 'timeline';
9091
static readonly TITLE = localize('timeline', 'Timeline');
9192

9293
private _$container!: HTMLElement;
@@ -95,9 +96,11 @@ export class TimelinePane extends ViewPane {
9596
private _$tree!: HTMLDivElement;
9697
private _tree!: WorkbenchObjectTree<TreeElement, FuzzyScore>;
9798
private _treeRenderer: TimelineTreeRenderer | undefined;
98-
private _menus: TimelineMenus;
99+
private _menus: TimelinePaneMenus;
99100
private _visibilityDisposables: DisposableStore | undefined;
100101

102+
private _followActiveEditorContext: IContextKey<boolean>;
103+
101104
private _excludedSources: Set<string>;
102105
private _cursorsByProvider: Map<string, TimelineCursors> = new Map();
103106
private _items: { element: TreeElement }[] = [];
@@ -122,13 +125,53 @@ export class TimelinePane extends ViewPane {
122125
) {
123126
super({ ...options, titleMenuId: MenuId.TimelineTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
124127

125-
this._menus = this._register(this.instantiationService.createInstance(TimelineMenus, this.id));
128+
this._menus = this._register(this.instantiationService.createInstance(TimelinePaneMenus, this.id));
129+
this._register(this.instantiationService.createInstance(TimelinePaneCommands, this));
126130

127131
const scopedContextKeyService = this._register(this.contextKeyService.createScoped());
128-
scopedContextKeyService.createKey('view', TimelinePane.ID);
132+
scopedContextKeyService.createKey('view', TimelinePaneId);
133+
134+
this._followActiveEditorContext = TimelineFollowActiveEditorContext.bindTo(this.contextKeyService);
129135

130136
this._excludedSources = new Set(configurationService.getValue('timeline.excludeSources'));
131137
configurationService.onDidChangeConfiguration(this.onConfigurationChanged, this);
138+
139+
this._register(timelineService.onDidChangeUri(uri => this.setUri(uri), this));
140+
}
141+
142+
private _followActiveEditor: boolean = true;
143+
get followActiveEditor(): boolean {
144+
return this._followActiveEditor;
145+
}
146+
set followActiveEditor(value: boolean) {
147+
if (this._followActiveEditor === value) {
148+
return;
149+
}
150+
151+
this._followActiveEditor = value;
152+
this._followActiveEditorContext.set(value);
153+
154+
if (value) {
155+
this.onActiveEditorChanged();
156+
}
157+
}
158+
159+
reset() {
160+
this.loadTimeline(true);
161+
}
162+
163+
setUri(uri: URI) {
164+
this.setUriCore(uri, true);
165+
}
166+
167+
private setUriCore(uri: URI | undefined, disableFollowing: boolean) {
168+
if (disableFollowing) {
169+
this.followActiveEditor = false;
170+
}
171+
172+
this._uri = uri;
173+
this._treeRenderer?.setUri(uri);
174+
this.loadTimeline(true);
132175
}
133176

134177
private onConfigurationChanged(e: IConfigurationChangeEvent) {
@@ -141,6 +184,10 @@ export class TimelinePane extends ViewPane {
141184
}
142185

143186
private onActiveEditorChanged() {
187+
if (!this.followActiveEditor) {
188+
return;
189+
}
190+
144191
let uri;
145192

146193
const editor = this.editorService.activeEditor;
@@ -154,9 +201,7 @@ export class TimelinePane extends ViewPane {
154201
return;
155202
}
156203

157-
this._uri = uri;
158-
this._treeRenderer?.setUri(uri);
159-
this.loadTimeline(true);
204+
this.setUriCore(uri, false);
160205
}
161206

162207
private onProvidersChanged(e: TimelineProvidersChangeEvent) {
@@ -177,11 +222,6 @@ export class TimelinePane extends ViewPane {
177222
}
178223
}
179224

180-
private onReset() {
181-
this.loadTimeline(true);
182-
}
183-
184-
185225
private _titleDescription: string | undefined;
186226
get titleDescription(): string | undefined {
187227
return this._titleDescription;
@@ -574,13 +614,14 @@ export class TimelinePane extends ViewPane {
574614

575615
this.timelineService.onDidChangeProviders(this.onProvidersChanged, this, this._visibilityDisposables);
576616
this.timelineService.onDidChangeTimeline(this.onTimelineChanged, this, this._visibilityDisposables);
577-
this.timelineService.onDidReset(this.onReset, this, this._visibilityDisposables);
578617
this.editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._visibilityDisposables);
579618

580619
this.onActiveEditorChanged();
581620
} else {
582621
this._visibilityDisposables?.dispose();
583622
}
623+
624+
super.setVisible(visible);
584625
}
585626

586627
protected layoutBody(height: number, width: number): void {
@@ -671,7 +712,7 @@ export class TimelinePane extends ViewPane {
671712
this.message = file ? localize('timeline.loading', 'Loading timeline for {0}...', file) : '';
672713
}
673714

674-
private onContextMenu(menus: TimelineMenus, treeEvent: ITreeContextMenuEvent<TreeElement | null>): void {
715+
private onContextMenu(menus: TimelinePaneMenus, treeEvent: ITreeContextMenuEvent<TreeElement | null>): void {
675716
const item = treeEvent.element;
676717
if (item === null) {
677718
return;
@@ -795,7 +836,7 @@ class TimelineTreeRenderer implements ITreeRenderer<TreeElement, FuzzyScore, Tim
795836
private _actionViewItemProvider: IActionViewItemProvider;
796837

797838
constructor(
798-
private readonly _menus: TimelineMenus,
839+
private readonly _menus: TimelinePaneMenus,
799840
@IInstantiationService protected readonly instantiationService: IInstantiationService,
800841
@IThemeService private _themeService: IThemeService
801842
) {
@@ -854,7 +895,58 @@ class TimelineTreeRenderer implements ITreeRenderer<TreeElement, FuzzyScore, Tim
854895
}
855896
}
856897

857-
class TimelineMenus extends Disposable {
898+
class TimelinePaneCommands extends Disposable {
899+
900+
static RefreshCommand = 'timeline.refresh';
901+
static ToggleFollowActiveEditorCommand = 'timeline.toggleFollowActiveEditor';
902+
903+
constructor(private _pane: TimelinePane) {
904+
super();
905+
906+
this._register(CommandsRegistry.registerCommand(TimelinePaneCommands.RefreshCommand, this.refreshCommand()));
907+
this._register(MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({
908+
group: 'navigation',
909+
order: 99,
910+
command: {
911+
id: TimelinePaneCommands.RefreshCommand,
912+
title: localize(TimelinePaneCommands.RefreshCommand, "Refresh"),
913+
icon: { id: 'codicon/refresh' }
914+
}
915+
})));
916+
917+
this._register(CommandsRegistry.registerCommand(TimelinePaneCommands.ToggleFollowActiveEditorCommand, this.toggleFollowActiveEditorCommand()));
918+
this._register(MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({
919+
group: 'navigation',
920+
order: 2,
921+
command: {
922+
id: TimelinePaneCommands.ToggleFollowActiveEditorCommand,
923+
title: localize(`${TimelinePaneCommands.ToggleFollowActiveEditorCommand}.stop`, "Stop following the Active Editor"),
924+
icon: { id: 'codicon/eye' }
925+
},
926+
when: TimelineFollowActiveEditorContext
927+
})));
928+
this._register(MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({
929+
group: 'navigation',
930+
order: 2,
931+
command: {
932+
id: TimelinePaneCommands.ToggleFollowActiveEditorCommand,
933+
title: localize(`${TimelinePaneCommands.ToggleFollowActiveEditorCommand}.follow`, "Follow the Active Editor"),
934+
icon: { id: 'codicon/eye-closed' }
935+
},
936+
when: TimelineFollowActiveEditorContext.toNegated()
937+
})));
938+
}
939+
940+
refreshCommand(): ICommandHandler {
941+
return (accessor, arg) => this._pane.reset();
942+
}
943+
944+
toggleFollowActiveEditorCommand(): ICommandHandler {
945+
return (accessor, arg) => this._pane.followActiveEditor = !this._pane.followActiveEditor;
946+
}
947+
}
948+
949+
class TimelinePaneMenus extends Disposable {
858950

859951
constructor(
860952
private id: string,

src/vs/workbench/contrib/timeline/common/timeline.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export function toKey(extension: ExtensionIdentifier | string, source: string) {
1515
return `${typeof extension === 'string' ? extension : ExtensionIdentifier.toKey(extension)}|${source}`;
1616
}
1717

18+
export const TimelinePaneId = 'timeline';
19+
1820
export interface TimelineItem {
1921
handle: string;
2022
source: string;
@@ -91,7 +93,7 @@ export interface ITimelineService {
9193

9294
onDidChangeProviders: Event<TimelineProvidersChangeEvent>;
9395
onDidChangeTimeline: Event<TimelineChangeEvent>;
94-
onDidReset: Event<void>;
96+
onDidChangeUri: Event<URI>;
9597

9698
registerTimelineProvider(provider: TimelineProvider): IDisposable;
9799
unregisterTimelineProvider(id: string): void;
@@ -100,8 +102,7 @@ export interface ITimelineService {
100102

101103
getTimeline(id: string, uri: URI, options: TimelineOptions, tokenSource: CancellationTokenSource, internalOptions?: InternalTimelineOptions): TimelineRequest | undefined;
102104

103-
// refresh(fetch?: 'all' | 'more'): void;
104-
reset(): void;
105+
setUri(uri: URI): void;
105106
}
106107

107108
const TIMELINE_SERVICE_ID = 'timeline';

0 commit comments

Comments
 (0)