mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-31 20:46:24 +01:00
Adds Open Timeline command to explorer files
Adds follow/unfollow actions to the timeline view Reworks timeline view commands
This commit is contained in:
@@ -9,7 +9,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions } from 'vs/workbench/common/views';
|
||||
import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/browser/explorerViewlet';
|
||||
import { ITimelineService } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { ITimelineService, TimelinePaneId } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { TimelineService } from 'vs/workbench/contrib/timeline/common/timelineService';
|
||||
import { TimelinePane } from './timelinePane';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
@@ -17,9 +17,11 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { ExplorerFolderContext } from 'vs/workbench/contrib/files/common/files';
|
||||
import { ResourceContextKey } from 'vs/workbench/common/resources';
|
||||
|
||||
export class TimelinePaneDescriptor implements IViewDescriptor {
|
||||
readonly id = TimelinePane.ID;
|
||||
readonly id = TimelinePaneId;
|
||||
readonly name = TimelinePane.TITLE;
|
||||
readonly ctorDescriptor = new SyncDescriptor(TimelinePane);
|
||||
readonly when = ContextKeyExpr.equals('config.timeline.showView', true);
|
||||
@@ -56,90 +58,30 @@ configurationRegistry.registerConfiguration({
|
||||
|
||||
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews([new TimelinePaneDescriptor()], VIEW_CONTAINER);
|
||||
|
||||
namespace TimelineViewRefreshAction {
|
||||
namespace OpenTimelineAction {
|
||||
|
||||
export const ID = 'timeline.refresh';
|
||||
export const LABEL = localize('timeline.refreshView', "Refresh");
|
||||
export const ID = 'files.openTimeline';
|
||||
export const LABEL = localize('files.openTimeline', "Open Timeline");
|
||||
|
||||
export function handler(): ICommandHandler {
|
||||
return (accessor, arg) => {
|
||||
const service = accessor.get(ITimelineService);
|
||||
return service.reset();
|
||||
return service.setUri(arg);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand(TimelineViewRefreshAction.ID, TimelineViewRefreshAction.handler());
|
||||
CommandsRegistry.registerCommand(OpenTimelineAction.ID, OpenTimelineAction.handler());
|
||||
|
||||
// namespace TimelineViewRefreshHardAction {
|
||||
|
||||
// export const ID = 'timeline.refreshHard';
|
||||
// export const LABEL = localize('timeline.refreshHard', "Refresh (Hard)");
|
||||
|
||||
// export function handler(fetch?: 'all' | 'more'): ICommandHandler {
|
||||
// return (accessor, arg) => {
|
||||
// const service = accessor.get(ITimelineService);
|
||||
// return service.refresh(fetch);
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
||||
// CommandsRegistry.registerCommand(TimelineViewRefreshAction.ID, TimelineViewRefreshAction.handler());
|
||||
|
||||
// namespace TimelineViewLoadMoreAction {
|
||||
|
||||
// export const ID = 'timeline.loadMore';
|
||||
// export const LABEL = localize('timeline.loadMoreInView', "Load More");
|
||||
|
||||
// export function handler(): ICommandHandler {
|
||||
// return (accessor, arg) => {
|
||||
// const service = accessor.get(ITimelineService);
|
||||
// return service.refresh('more');
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
||||
// CommandsRegistry.registerCommand(TimelineViewLoadMoreAction.ID, TimelineViewLoadMoreAction.handler());
|
||||
|
||||
// namespace TimelineViewLoadAllAction {
|
||||
|
||||
// export const ID = 'timeline.loadAll';
|
||||
// export const LABEL = localize('timeline.loadAllInView', "Load All");
|
||||
|
||||
// export function handler(): ICommandHandler {
|
||||
// return (accessor, arg) => {
|
||||
// const service = accessor.get(ITimelineService);
|
||||
// return service.refresh('all');
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
||||
// CommandsRegistry.registerCommand(TimelineViewLoadAllAction.ID, TimelineViewLoadAllAction.handler());
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({
|
||||
group: 'navigation',
|
||||
MenuRegistry.appendMenuItem(MenuId.ExplorerContext, ({
|
||||
group: '4_timeline',
|
||||
order: 1,
|
||||
command: {
|
||||
id: TimelineViewRefreshAction.ID,
|
||||
title: TimelineViewRefreshAction.LABEL,
|
||||
icon: { id: 'codicon/refresh' }
|
||||
}
|
||||
id: OpenTimelineAction.ID,
|
||||
title: localize(OpenTimelineAction.LABEL, "Open Timeline"),
|
||||
icon: { id: 'codicon/history' }
|
||||
},
|
||||
when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.HasResource)
|
||||
}));
|
||||
|
||||
// MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({
|
||||
// group: 'navigation',
|
||||
// order: 2,
|
||||
// command: {
|
||||
// id: TimelineViewLoadMoreAction.ID,
|
||||
// title: TimelineViewLoadMoreAction.LABEL,
|
||||
// icon: { id: 'codicon/unfold' }
|
||||
// },
|
||||
// alt: {
|
||||
// id: TimelineViewLoadAllAction.ID,
|
||||
// title: TimelineViewLoadAllAction.LABEL,
|
||||
// icon: { id: 'codicon/unfold' }
|
||||
|
||||
// }
|
||||
// }));
|
||||
|
||||
registerSingleton(ITimelineService, TimelineService, true);
|
||||
|
||||
@@ -18,13 +18,13 @@ import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/vie
|
||||
import { TreeResourceNavigator, WorkbenchObjectTree } from 'vs/platform/list/browser/listService';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITimelineService, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvidersChangeEvent, TimelineRequest, Timeline } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { ITimelineService, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvidersChangeEvent, TimelineRequest, Timeline, TimelinePaneId } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { SideBySideEditor, toResource } from 'vs/workbench/common/editor';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ICommandService, CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
|
||||
import { IThemeService, LIGHT, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
@@ -34,7 +34,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IActionViewItemProvider, ActionBar, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IAction, ActionRunner } from 'vs/base/common/actions';
|
||||
import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { MenuItemAction, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { MenuItemAction, IMenuService, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { fromNow } from 'vs/base/common/date';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
@@ -85,8 +85,9 @@ interface TimelineCursors {
|
||||
more: boolean;
|
||||
}
|
||||
|
||||
export const TimelineFollowActiveEditorContext = new RawContextKey<boolean>('timelineFollowActiveEditor', true);
|
||||
|
||||
export class TimelinePane extends ViewPane {
|
||||
static readonly ID = 'timeline';
|
||||
static readonly TITLE = localize('timeline', 'Timeline');
|
||||
|
||||
private _$container!: HTMLElement;
|
||||
@@ -95,9 +96,11 @@ export class TimelinePane extends ViewPane {
|
||||
private _$tree!: HTMLDivElement;
|
||||
private _tree!: WorkbenchObjectTree<TreeElement, FuzzyScore>;
|
||||
private _treeRenderer: TimelineTreeRenderer | undefined;
|
||||
private _menus: TimelineMenus;
|
||||
private _menus: TimelinePaneMenus;
|
||||
private _visibilityDisposables: DisposableStore | undefined;
|
||||
|
||||
private _followActiveEditorContext: IContextKey<boolean>;
|
||||
|
||||
private _excludedSources: Set<string>;
|
||||
private _cursorsByProvider: Map<string, TimelineCursors> = new Map();
|
||||
private _items: { element: TreeElement }[] = [];
|
||||
@@ -122,13 +125,53 @@ export class TimelinePane extends ViewPane {
|
||||
) {
|
||||
super({ ...options, titleMenuId: MenuId.TimelineTitle }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService);
|
||||
|
||||
this._menus = this._register(this.instantiationService.createInstance(TimelineMenus, this.id));
|
||||
this._menus = this._register(this.instantiationService.createInstance(TimelinePaneMenus, this.id));
|
||||
this._register(this.instantiationService.createInstance(TimelinePaneCommands, this));
|
||||
|
||||
const scopedContextKeyService = this._register(this.contextKeyService.createScoped());
|
||||
scopedContextKeyService.createKey('view', TimelinePane.ID);
|
||||
scopedContextKeyService.createKey('view', TimelinePaneId);
|
||||
|
||||
this._followActiveEditorContext = TimelineFollowActiveEditorContext.bindTo(this.contextKeyService);
|
||||
|
||||
this._excludedSources = new Set(configurationService.getValue('timeline.excludeSources'));
|
||||
configurationService.onDidChangeConfiguration(this.onConfigurationChanged, this);
|
||||
|
||||
this._register(timelineService.onDidChangeUri(uri => this.setUri(uri), this));
|
||||
}
|
||||
|
||||
private _followActiveEditor: boolean = true;
|
||||
get followActiveEditor(): boolean {
|
||||
return this._followActiveEditor;
|
||||
}
|
||||
set followActiveEditor(value: boolean) {
|
||||
if (this._followActiveEditor === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._followActiveEditor = value;
|
||||
this._followActiveEditorContext.set(value);
|
||||
|
||||
if (value) {
|
||||
this.onActiveEditorChanged();
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.loadTimeline(true);
|
||||
}
|
||||
|
||||
setUri(uri: URI) {
|
||||
this.setUriCore(uri, true);
|
||||
}
|
||||
|
||||
private setUriCore(uri: URI | undefined, disableFollowing: boolean) {
|
||||
if (disableFollowing) {
|
||||
this.followActiveEditor = false;
|
||||
}
|
||||
|
||||
this._uri = uri;
|
||||
this._treeRenderer?.setUri(uri);
|
||||
this.loadTimeline(true);
|
||||
}
|
||||
|
||||
private onConfigurationChanged(e: IConfigurationChangeEvent) {
|
||||
@@ -141,6 +184,10 @@ export class TimelinePane extends ViewPane {
|
||||
}
|
||||
|
||||
private onActiveEditorChanged() {
|
||||
if (!this.followActiveEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
let uri;
|
||||
|
||||
const editor = this.editorService.activeEditor;
|
||||
@@ -154,9 +201,7 @@ export class TimelinePane extends ViewPane {
|
||||
return;
|
||||
}
|
||||
|
||||
this._uri = uri;
|
||||
this._treeRenderer?.setUri(uri);
|
||||
this.loadTimeline(true);
|
||||
this.setUriCore(uri, false);
|
||||
}
|
||||
|
||||
private onProvidersChanged(e: TimelineProvidersChangeEvent) {
|
||||
@@ -177,11 +222,6 @@ export class TimelinePane extends ViewPane {
|
||||
}
|
||||
}
|
||||
|
||||
private onReset() {
|
||||
this.loadTimeline(true);
|
||||
}
|
||||
|
||||
|
||||
private _titleDescription: string | undefined;
|
||||
get titleDescription(): string | undefined {
|
||||
return this._titleDescription;
|
||||
@@ -574,13 +614,14 @@ export class TimelinePane extends ViewPane {
|
||||
|
||||
this.timelineService.onDidChangeProviders(this.onProvidersChanged, this, this._visibilityDisposables);
|
||||
this.timelineService.onDidChangeTimeline(this.onTimelineChanged, this, this._visibilityDisposables);
|
||||
this.timelineService.onDidReset(this.onReset, this, this._visibilityDisposables);
|
||||
this.editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._visibilityDisposables);
|
||||
|
||||
this.onActiveEditorChanged();
|
||||
} else {
|
||||
this._visibilityDisposables?.dispose();
|
||||
}
|
||||
|
||||
super.setVisible(visible);
|
||||
}
|
||||
|
||||
protected layoutBody(height: number, width: number): void {
|
||||
@@ -671,7 +712,7 @@ export class TimelinePane extends ViewPane {
|
||||
this.message = file ? localize('timeline.loading', 'Loading timeline for {0}...', file) : '';
|
||||
}
|
||||
|
||||
private onContextMenu(menus: TimelineMenus, treeEvent: ITreeContextMenuEvent<TreeElement | null>): void {
|
||||
private onContextMenu(menus: TimelinePaneMenus, treeEvent: ITreeContextMenuEvent<TreeElement | null>): void {
|
||||
const item = treeEvent.element;
|
||||
if (item === null) {
|
||||
return;
|
||||
@@ -795,7 +836,7 @@ class TimelineTreeRenderer implements ITreeRenderer<TreeElement, FuzzyScore, Tim
|
||||
private _actionViewItemProvider: IActionViewItemProvider;
|
||||
|
||||
constructor(
|
||||
private readonly _menus: TimelineMenus,
|
||||
private readonly _menus: TimelinePaneMenus,
|
||||
@IInstantiationService protected readonly instantiationService: IInstantiationService,
|
||||
@IThemeService private _themeService: IThemeService
|
||||
) {
|
||||
@@ -854,7 +895,58 @@ class TimelineTreeRenderer implements ITreeRenderer<TreeElement, FuzzyScore, Tim
|
||||
}
|
||||
}
|
||||
|
||||
class TimelineMenus extends Disposable {
|
||||
class TimelinePaneCommands extends Disposable {
|
||||
|
||||
static RefreshCommand = 'timeline.refresh';
|
||||
static ToggleFollowActiveEditorCommand = 'timeline.toggleFollowActiveEditor';
|
||||
|
||||
constructor(private _pane: TimelinePane) {
|
||||
super();
|
||||
|
||||
this._register(CommandsRegistry.registerCommand(TimelinePaneCommands.RefreshCommand, this.refreshCommand()));
|
||||
this._register(MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({
|
||||
group: 'navigation',
|
||||
order: 99,
|
||||
command: {
|
||||
id: TimelinePaneCommands.RefreshCommand,
|
||||
title: localize(TimelinePaneCommands.RefreshCommand, "Refresh"),
|
||||
icon: { id: 'codicon/refresh' }
|
||||
}
|
||||
})));
|
||||
|
||||
this._register(CommandsRegistry.registerCommand(TimelinePaneCommands.ToggleFollowActiveEditorCommand, this.toggleFollowActiveEditorCommand()));
|
||||
this._register(MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({
|
||||
group: 'navigation',
|
||||
order: 2,
|
||||
command: {
|
||||
id: TimelinePaneCommands.ToggleFollowActiveEditorCommand,
|
||||
title: localize(`${TimelinePaneCommands.ToggleFollowActiveEditorCommand}.stop`, "Stop following the Active Editor"),
|
||||
icon: { id: 'codicon/eye' }
|
||||
},
|
||||
when: TimelineFollowActiveEditorContext
|
||||
})));
|
||||
this._register(MenuRegistry.appendMenuItem(MenuId.TimelineTitle, ({
|
||||
group: 'navigation',
|
||||
order: 2,
|
||||
command: {
|
||||
id: TimelinePaneCommands.ToggleFollowActiveEditorCommand,
|
||||
title: localize(`${TimelinePaneCommands.ToggleFollowActiveEditorCommand}.follow`, "Follow the Active Editor"),
|
||||
icon: { id: 'codicon/eye-closed' }
|
||||
},
|
||||
when: TimelineFollowActiveEditorContext.toNegated()
|
||||
})));
|
||||
}
|
||||
|
||||
refreshCommand(): ICommandHandler {
|
||||
return (accessor, arg) => this._pane.reset();
|
||||
}
|
||||
|
||||
toggleFollowActiveEditorCommand(): ICommandHandler {
|
||||
return (accessor, arg) => this._pane.followActiveEditor = !this._pane.followActiveEditor;
|
||||
}
|
||||
}
|
||||
|
||||
class TimelinePaneMenus extends Disposable {
|
||||
|
||||
constructor(
|
||||
private id: string,
|
||||
|
||||
@@ -15,6 +15,8 @@ export function toKey(extension: ExtensionIdentifier | string, source: string) {
|
||||
return `${typeof extension === 'string' ? extension : ExtensionIdentifier.toKey(extension)}|${source}`;
|
||||
}
|
||||
|
||||
export const TimelinePaneId = 'timeline';
|
||||
|
||||
export interface TimelineItem {
|
||||
handle: string;
|
||||
source: string;
|
||||
@@ -91,7 +93,7 @@ export interface ITimelineService {
|
||||
|
||||
onDidChangeProviders: Event<TimelineProvidersChangeEvent>;
|
||||
onDidChangeTimeline: Event<TimelineChangeEvent>;
|
||||
onDidReset: Event<void>;
|
||||
onDidChangeUri: Event<URI>;
|
||||
|
||||
registerTimelineProvider(provider: TimelineProvider): IDisposable;
|
||||
unregisterTimelineProvider(id: string): void;
|
||||
@@ -100,8 +102,7 @@ export interface ITimelineService {
|
||||
|
||||
getTimeline(id: string, uri: URI, options: TimelineOptions, tokenSource: CancellationTokenSource, internalOptions?: InternalTimelineOptions): TimelineRequest | undefined;
|
||||
|
||||
// refresh(fetch?: 'all' | 'more'): void;
|
||||
reset(): void;
|
||||
setUri(uri: URI): void;
|
||||
}
|
||||
|
||||
const TIMELINE_SERVICE_ID = 'timeline';
|
||||
|
||||
@@ -9,7 +9,8 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
// import { basename } from 'vs/base/common/path';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ITimelineService, TimelineChangeEvent, TimelineOptions, TimelineProvidersChangeEvent, TimelineProvider, InternalTimelineOptions } from './timeline';
|
||||
import { ITimelineService, TimelineChangeEvent, TimelineOptions, TimelineProvidersChangeEvent, TimelineProvider, InternalTimelineOptions, TimelinePaneId } from './timeline';
|
||||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
|
||||
export class TimelineService implements ITimelineService {
|
||||
_serviceBrand: undefined;
|
||||
@@ -19,14 +20,16 @@ export class TimelineService implements ITimelineService {
|
||||
|
||||
private readonly _onDidChangeTimeline = new Emitter<TimelineChangeEvent>();
|
||||
readonly onDidChangeTimeline: Event<TimelineChangeEvent> = this._onDidChangeTimeline.event;
|
||||
|
||||
private readonly _onDidReset = new Emitter<void>();
|
||||
readonly onDidReset: Event<void> = this._onDidReset.event;
|
||||
private readonly _onDidChangeUri = new Emitter<URI>();
|
||||
readonly onDidChangeUri: Event<URI> = this._onDidChangeUri.event;
|
||||
|
||||
private readonly _providers = new Map<string, TimelineProvider>();
|
||||
private readonly _providerSubscriptions = new Map<string, IDisposable>();
|
||||
|
||||
constructor(@ILogService private readonly logService: ILogService) {
|
||||
constructor(
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IViewsService protected viewsService: IViewsService,
|
||||
) {
|
||||
// let source = 'slow-source';
|
||||
// this.registerTimelineProvider({
|
||||
// scheme: '*',
|
||||
@@ -175,11 +178,8 @@ export class TimelineService implements ITimelineService {
|
||||
this._onDidChangeProviders.fire({ removed: [id] });
|
||||
}
|
||||
|
||||
// refresh(fetch?: 'all' | 'more') {
|
||||
// this._onDidChangeTimeline.fire({ fetch: fetch });
|
||||
// }
|
||||
|
||||
reset() {
|
||||
this._onDidReset.fire();
|
||||
setUri(uri: URI) {
|
||||
this.viewsService.openView(TimelinePaneId, true);
|
||||
this._onDidChangeUri.fire(uri);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user