提交 90df2d09 编写于 作者: E Eric Amodio

Adds Open Timeline command to explorer files

Adds follow/unfollow actions to the timeline view
Reworks timeline view commands
上级 fb2ea118
......@@ -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);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册