提交 7f8d9c9b 编写于 作者: J Joao Moreno

scm viewlet context menus

上级 174dde2e
......@@ -34,21 +34,57 @@
"command": "git.open-change",
"title": "Open Change",
"category": "Git"
},
{
"command": "git.stage",
"title": "Stage",
"category": "Git"
},
{
"command": "git.stage-all",
"title": "Stage All",
"category": "Git"
},
{
"command": "git.unstage",
"title": "Unstage",
"category": "Git"
},
{
"command": "git.unstage-all",
"title": "Unstage All",
"category": "Git"
}
],
"menus": {
"scm/title": [
"scm/git/title": [
{
"command": "git.refresh",
"group": "navigation",
"when": "scm.provider == git"
"group": "navigation"
}
],
"scm/git/index/group/context": [
{
"command": "git.unstage-all",
"group": "navigation"
}
],
"scm/git/index/resource/context": [
{
"command": "git.unstage",
"group": "navigation"
}
],
"scm/git/workingTree/group/context": [
{
"command": "git.stage-all",
"group": "navigation"
}
],
"scm/git/workingTree/context": [
"scm/git/workingTree/resource/context": [
{
"command": "git.open-change",
"group": "navigation",
"when": "scm.provider == git"
"command": "git.stage",
"group": "navigation"
}
]
},
......
......@@ -7,6 +7,14 @@
import { commands, Disposable } from 'vscode';
import { Model } from './model';
import { log } from './util';
function refresh(model: Model): () => void {
return () => {
log('refresh');
model.update();
};
}
function openChange(...args: any[]): void {
console.log('openChange', args);
......@@ -14,7 +22,7 @@ function openChange(...args: any[]): void {
export function registerCommands(model: Model): Disposable {
const disposables = [
commands.registerCommand('git.refresh', () => model.update()),
commands.registerCommand('git.refresh', refresh(model)),
commands.registerCommand('git.open-change', openChange)
];
......
......@@ -9,16 +9,13 @@ import { scm, ExtensionContext, workspace, Uri, window, Disposable } from 'vscod
import * as path from 'path';
import { findGit, Git } from './git';
import { Model } from './model';
import { log } from './util';
import { GitSCMProvider } from './scmProvider';
import { registerCommands } from './commands';
import * as nls from 'vscode-nls';
nls.config();
export function log(...args: any[]): void {
console.log.apply(console, ['git:', ...args]);
}
class TextDocumentContentProvider {
constructor(private git: Git, private rootPath: string) { }
......
......@@ -7,6 +7,10 @@
import { Event } from 'vscode';
export function log(...args: any[]): void {
console.log.apply(console, ['git:', ...args]);
}
export interface IDisposable {
dispose(): void;
}
......
......@@ -13,7 +13,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { forEach } from 'vs/base/common/collections';
import { IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { MenuId, SCMMenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { MenuId, SCMTitleMenuId, SCMMenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
namespace schema {
......@@ -26,23 +26,17 @@ namespace schema {
group?: string;
}
const SCMMenuRegex = /^scm\/([^/]+)\/([^/]+)(\/context)?$/;
export function parseMenuId(value: string): MenuId {
switch (value) {
case 'editor/title': return MenuId.EditorTitle;
case 'editor/context': return MenuId.EditorContext;
case 'explorer/context': return MenuId.ExplorerContext;
case 'editor/title/context': return MenuId.EditorTitleContext;
case 'scm/title': return MenuId.SCMTitle;
}
const match = SCMMenuRegex.exec(value);
if (match) {
const [, providerId, resourceGroupId, isContext] = match;
return new SCMMenuId(providerId, resourceGroupId, !!isContext);
}
return SCMTitleMenuId.parse(value)
|| SCMMenuId.parse(value)
|| void 0;
}
export function isValidMenuItems(menu: IUserFriendlyMenuItem[], collector: ExtensionMessageCollector): boolean {
......
......@@ -41,7 +41,6 @@ export class MenuId {
static readonly EditorContext = new MenuId('3');
static readonly ExplorerContext = new MenuId('4');
static readonly ProblemsPanelContext = new MenuId('5');
static readonly SCMTitle = new MenuId('scm/title');
constructor(private _id: string) {
......@@ -52,14 +51,38 @@ export class MenuId {
}
}
export class SCMTitleMenuId extends MenuId {
static parse(value: string): MenuId | null {
const match = /^scm\/([^/]+)\/title$/.exec(value);
return match ? new SCMTitleMenuId(match[1]) : null;
}
constructor(private _providerId: string) {
super(`scm/${_providerId}/title`);
}
}
export const enum SCMMenuType {
ResourceGroup,
Resource
}
export class SCMMenuId extends MenuId {
get providerId(): string { return this._providerId; }
get resourceGroupId(): string { return this._resourceGroupId; }
get isContext(): boolean { return this._isContext; }
static parse(value: string): MenuId | null {
const match = /^scm\/([^/]+)\/([^/]+)\/(group|resource)(\/context)?$/.exec(value);
if (match) {
const [, providerId, resourceGroupId, typeStr, isContext] = match;
const type = typeStr === 'group' ? SCMMenuType.ResourceGroup : SCMMenuType.Resource;
return new SCMMenuId(providerId, resourceGroupId, type, !!isContext);
}
}
constructor(private _providerId: string, private _resourceGroupId: string, private _isContext: boolean) {
super(`scm/${_providerId}/${_resourceGroupId}${_isContext ? '/context' : ''}`);
constructor(private providerId: string, private resourceGroupId: string, private type: SCMMenuType, private isContext: boolean) {
super(`scm/${providerId}/${resourceGroupId}/${type === SCMMenuType.ResourceGroup ? 'group' : 'resource'}${isContext ? '/context' : ''}`);
}
}
......
......@@ -6,60 +6,84 @@
'use strict';
import 'vs/css!./media/scmViewlet';
import Event, { mapEvent } from 'vs/base/common/event';
import { memoize } from 'vs/base/common/decorators';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, empty as EmptyDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMenuService, MenuId, IMenu, SCMMenuId } from 'vs/platform/actions/common/actions';
import { IMenuService, SCMTitleMenuId, SCMMenuId, SCMMenuType, MenuId } from 'vs/platform/actions/common/actions';
import { IAction } from 'vs/base/common/actions';
import { fillInActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { ISCMService, ISCMProvider } from 'vs/workbench/services/scm/common/scm';
export class SCMMenus implements IDisposable {
private titleMenu: IMenu;
private disposables: IDisposable[] = [];
private _titleMenuActions: { primary: IAction[]; secondary: IAction[] };
private get cachedTitleMenuActions() {
if (!this._titleMenuActions) {
this._titleMenuActions = { primary: [], secondary: [] };
fillInActions(this.titleMenu, null, this._titleMenuActions);
}
return this._titleMenuActions;
}
private titleDisposable: IDisposable = EmptyDisposable;
private titleActions: IAction[] = [];
private titleSecondaryActions: IAction[] = [];
constructor(
@IContextKeyService private contextKeyService: IContextKeyService,
@ISCMService private scmService: ISCMService,
@IMenuService private menuService: IMenuService
) {
this.titleMenu = menuService.createMenu(MenuId.SCMTitle, contextKeyService);
this.disposables.push(this.titleMenu);
this.setActiveProvider(this.scmService.activeProvider);
this.scmService.onDidChangeProvider(this.setActiveProvider, this, this.disposables);
}
private setActiveProvider(activeProvider: ISCMProvider | undefined): void {
if (this.titleDisposable) {
this.titleDisposable.dispose();
this.titleDisposable = EmptyDisposable;
}
if (!activeProvider) {
return;
}
const titleMenuId = new SCMTitleMenuId(activeProvider.id);
const titleMenu = this.menuService.createMenu(titleMenuId, this.contextKeyService);
const updateActions = () => fillInActions(titleMenu, null, { primary: this.titleActions, secondary: this.titleSecondaryActions });
const listener = titleMenu.onDidChange(updateActions);
updateActions();
this.titleDisposable = toDisposable(() => {
listener.dispose();
titleMenu.dispose();
this.titleActions = [];
this.titleSecondaryActions = [];
});
}
@memoize
get onDidChangeTitleMenu(): Event<any> {
return mapEvent(this.titleMenu.onDidChange, () => this._titleMenuActions = void 0);
getTitleActions(): IAction[] {
return this.titleActions;
}
get title(): IAction[] {
return this.cachedTitleMenuActions.primary;
getTitleSecondaryActions(): IAction[] {
return this.titleSecondaryActions;
}
get titleSecondary(): IAction[] {
return this.cachedTitleMenuActions.secondary;
getResourceGroupActions(providerId: string, resourceGroupId: string): IAction[] {
const menuId = new SCMMenuId(providerId, resourceGroupId, SCMMenuType.ResourceGroup, false);
return this.getActions(menuId);
}
getResourceGroupContextActions(providerId: string, resourceGroupId: string): IAction[] {
const menuId = new SCMMenuId(providerId, resourceGroupId, SCMMenuType.ResourceGroup, true);
return this.getActions(menuId);
}
getResourceActions(providerId: string, resourceGroupId: string): IAction[] {
const menuId = new SCMMenuId(providerId, resourceGroupId, false);
const menu = this.menuService.createMenu(menuId, this.contextKeyService);
const result = [];
fillInActions(menu, null, result);
menu.dispose();
return result;
const menuId = new SCMMenuId(providerId, resourceGroupId, SCMMenuType.Resource, false);
return this.getActions(menuId);
}
getResourceContextActions(providerId: string, resourceGroupId: string): IAction[] {
const menuId = new SCMMenuId(providerId, resourceGroupId, true);
const menuId = new SCMMenuId(providerId, resourceGroupId, SCMMenuType.Resource, true);
return this.getActions(menuId);
}
private getActions(menuId: MenuId): IAction[] {
const menu = this.menuService.createMenu(menuId, this.contextKeyService);
const result = [];
fillInActions(menu, null, result);
......
......@@ -136,8 +136,6 @@ export class SCMViewlet extends Viewlet {
this.menus = this.instantiationService.createInstance(SCMMenus);
this.disposables.push(this.menus);
this.menus.onDidChangeTitleMenu(this.updateTitleArea, this, this.disposables);
}
private setActiveProvider(activeProvider: ISCMProvider | undefined): void {
......@@ -149,6 +147,7 @@ export class SCMViewlet extends Viewlet {
this.providerChangeDisposable = EmptyDisposable;
}
this.updateTitleArea();
this.update();
}
......@@ -243,11 +242,11 @@ export class SCMViewlet extends Viewlet {
}
getActions(): IAction[] {
return this.menus.title;
return this.menus.getTitleActions();
}
getSecondaryActions(): IAction[] {
return this.menus.titleSecondary;
return this.menus.getTitleSecondaryActions();
}
getActionItem(action: IAction): IActionItem {
......@@ -262,18 +261,16 @@ export class SCMViewlet extends Viewlet {
}
const element = e.element;
let resourceGroupId: string;
let actions: IAction[];
if ((element as ISCMResource).uri) {
const resource = element as ISCMResource;
resourceGroupId = resource.resourceGroupId;
actions = this.menus.getResourceContextActions(provider.id, resource.resourceGroupId);
} else {
const resourceGroup = element as ISCMResourceGroup;
resourceGroupId = resourceGroup.id;
actions = this.menus.getResourceGroupContextActions(provider.id, resourceGroup.id);
}
const actions = this.menus.getResourceContextActions(provider.id, resourceGroupId);
this.contextMenuService.showContextMenu({
getAnchor: () => ({ x: e.clientX + 1, y: e.clientY }),
getActions: () => TPromise.as(actions)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册