提交 bede31d0 编写于 作者: J Joao Moreno

resource tree: support data in branches

上级 a6981175
......@@ -11,58 +11,48 @@ import { URI } from 'vs/base/common/uri';
import { mapValues } from 'vs/base/common/collections';
import { PathIterator } from 'vs/base/common/map';
export interface ILeafNode<T, C = void> {
export interface IResourceNode<T, C = void> {
readonly uri: URI;
readonly relativePath: string;
readonly name: string;
readonly element: T;
readonly element: T | undefined;
readonly children: Iterator<IResourceNode<T, C>>;
readonly childrenCount: number;
readonly parent: IResourceNode<T, C> | undefined;
readonly context: C;
get(childName: string): IResourceNode<T, C> | undefined;
}
export interface IBranchNode<T, C = void> {
readonly uri: URI;
readonly relativePath: string;
readonly name: string;
readonly size: number;
readonly children: Iterator<INode<T, C>>;
readonly parent: IBranchNode<T, C> | undefined;
readonly context: C;
get(childName: string): INode<T, C> | undefined;
}
export type INode<T, C> = IBranchNode<T, C> | ILeafNode<T, C>;
// Internals
class Node<C> {
@memoize
get name(): string { return paths.posix.basename(this.relativePath); }
constructor(readonly uri: URI, readonly relativePath: string, readonly context: C) { }
}
class BranchNode<T, C> extends Node<C> implements IBranchNode<T, C> {
class Node<T, C> implements IResourceNode<T, C> {
private _children = new Map<string, BranchNode<T, C> | LeafNode<T, C>>();
private _children = new Map<string, Node<T, C>>();
get size(): number {
get childrenCount(): number {
return this._children.size;
}
get children(): Iterator<BranchNode<T, C> | LeafNode<T, C>> {
get children(): Iterator<Node<T, C>> {
return Iterator.fromArray(mapValues(this._children));
}
constructor(uri: URI, relativePath: string, context: C, readonly parent: IBranchNode<T, C> | undefined = undefined) {
super(uri, relativePath, context);
@memoize
get name(): string {
return paths.posix.basename(this.relativePath);
}
get(path: string): BranchNode<T, C> | LeafNode<T, C> | undefined {
constructor(
readonly uri: URI,
readonly relativePath: string,
readonly context: C,
public element: T | undefined = undefined,
readonly parent: IResourceNode<T, C> | undefined = undefined
) { }
get(path: string): Node<T, C> | undefined {
return this._children.get(path);
}
set(path: string, child: BranchNode<T, C> | LeafNode<T, C>): void {
set(path: string, child: Node<T, C>): void {
this._children.set(path, child);
}
......@@ -75,32 +65,21 @@ class BranchNode<T, C> extends Node<C> implements IBranchNode<T, C> {
}
}
class LeafNode<T, C> extends Node<C> implements ILeafNode<T, C> {
constructor(uri: URI, path: string, context: C, readonly element: T) {
super(uri, path, context);
}
}
function collect<T, C>(node: INode<T, C>, result: T[]): T[] {
if (ResourceTree.isBranchNode(node)) {
Iterator.forEach(node.children, child => collect(child, result));
} else {
function collect<T, C>(node: IResourceNode<T, C>, result: T[]): T[] {
if (typeof node.element !== 'undefined') {
result.push(node.element);
}
Iterator.forEach(node.children, child => collect(child, result));
return result;
}
export class ResourceTree<T extends NonNullable<any>, C> {
readonly root: BranchNode<T, C>;
static isBranchNode<T, C>(obj: any): obj is IBranchNode<T, C> {
return obj instanceof BranchNode;
}
readonly root: Node<T, C>;
static getRoot<T, C>(node: IBranchNode<T, C>): IBranchNode<T, C> {
static getRoot<T, C>(node: IResourceNode<T, C>): IResourceNode<T, C> {
while (node.parent) {
node = node.parent;
}
......@@ -108,12 +87,16 @@ export class ResourceTree<T extends NonNullable<any>, C> {
return node;
}
static collect<T, C>(node: INode<T, C>): T[] {
static collect<T, C>(node: IResourceNode<T, C>): T[] {
return collect(node, []);
}
static isResourceNode<T, C>(obj: any): obj is IResourceNode<T, C> {
return obj instanceof Node;
}
constructor(context: C, rootURI: URI = URI.file('/')) {
this.root = new BranchNode(rootURI, '', context);
this.root = new Node(rootURI, '', context);
}
add(uri: URI, element: T): void {
......@@ -129,26 +112,17 @@ export class ResourceTree<T extends NonNullable<any>, C> {
let child = node.get(name);
if (!child) {
if (iterator.hasNext()) {
child = new BranchNode(joinPath(this.root.uri, path), path, this.root.context, node);
node.set(name, child);
} else {
child = new LeafNode(uri, path, this.root.context, element);
node.set(name, child);
return;
}
}
if (!(child instanceof BranchNode)) {
if (iterator.hasNext()) {
throw new Error('Inconsistent tree: can\'t override leaf with branch.');
}
// replace
node.set(name, new LeafNode(uri, path, this.root.context, element));
return;
child = new Node(
joinPath(this.root.uri, path),
path,
this.root.context,
iterator.hasNext() ? undefined : element,
node
);
node.set(name, child);
} else if (!iterator.hasNext()) {
throw new Error('Inconsistent tree: can\'t override branch with leaf.');
child.element = element;
}
node = child;
......@@ -167,7 +141,7 @@ export class ResourceTree<T extends NonNullable<any>, C> {
return this._delete(this.root, iterator);
}
private _delete(node: BranchNode<T, C>, iterator: PathIterator): T | undefined {
private _delete(node: Node<T, C>, iterator: PathIterator): T | undefined {
const name = iterator.value();
const child = node.get(name);
......@@ -175,25 +149,14 @@ export class ResourceTree<T extends NonNullable<any>, C> {
return undefined;
}
// not at end
if (iterator.hasNext()) {
if (child instanceof BranchNode) {
const result = this._delete(child, iterator.next());
const result = this._delete(child, iterator.next());
if (typeof result !== 'undefined' && child.size === 0) {
node.delete(name);
}
return result;
} else {
throw new Error('Inconsistent tree: Expected a branch, found a leaf instead.');
if (typeof result !== 'undefined' && child.childrenCount === 0) {
node.delete(name);
}
}
//at end
if (child instanceof BranchNode) {
// TODO: maybe we can allow this
throw new Error('Inconsistent tree: Expected a leaf, found a branch instead.');
return result;
}
node.delete(name);
......@@ -203,4 +166,22 @@ export class ResourceTree<T extends NonNullable<any>, C> {
clear(): void {
this.root.clear();
}
getNode(uri: URI): IResourceNode<T, C> | undefined {
const key = relativePath(this.root.uri, uri) || uri.fsPath;
const iterator = new PathIterator(false).reset(key);
let node = this.root;
while (true) {
const name = iterator.value();
const child = node.get(name);
if (!child || !iterator.hasNext()) {
return child;
}
node = child;
iterator.next();
}
}
}
......@@ -4,46 +4,70 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { ResourceTree, IBranchNode, ILeafNode } from 'vs/base/common/resourceTree';
import { ResourceTree } from 'vs/base/common/resourceTree';
import { URI } from 'vs/base/common/uri';
suite('ResourceTree', function () {
suite.only('ResourceTree', function () {
test('ctor', function () {
const tree = new ResourceTree<string, null>(null);
assert(ResourceTree.isBranchNode(tree.root));
assert.equal(tree.root.size, 0);
assert.equal(tree.root.childrenCount, 0);
});
test('simple', function () {
const tree = new ResourceTree<string, null>(null);
tree.add(URI.file('/foo/bar.txt'), 'bar contents');
assert(ResourceTree.isBranchNode(tree.root));
assert.equal(tree.root.size, 1);
assert.equal(tree.root.childrenCount, 1);
let foo = tree.root.get('foo') as IBranchNode<string, null>;
let foo = tree.root.get('foo')!;
assert(foo);
assert(ResourceTree.isBranchNode(foo));
assert.equal(foo.size, 1);
assert.equal(foo.childrenCount, 1);
let bar = foo.get('bar.txt') as ILeafNode<string, null>;
let bar = foo.get('bar.txt')!;
assert(bar);
assert(!ResourceTree.isBranchNode(bar));
assert.equal(bar.element, 'bar contents');
tree.add(URI.file('/hello.txt'), 'hello contents');
assert.equal(tree.root.size, 2);
assert.equal(tree.root.childrenCount, 2);
let hello = tree.root.get('hello.txt') as ILeafNode<string, null>;
let hello = tree.root.get('hello.txt')!;
assert(hello);
assert(!ResourceTree.isBranchNode(hello));
assert.equal(hello.element, 'hello contents');
tree.delete(URI.file('/foo/bar.txt'));
assert.equal(tree.root.size, 1);
hello = tree.root.get('hello.txt') as ILeafNode<string, null>;
assert.equal(tree.root.childrenCount, 1);
hello = tree.root.get('hello.txt')!;
assert(hello);
assert(!ResourceTree.isBranchNode(hello));
assert.equal(hello.element, 'hello contents');
});
test('folders with data', function () {
const tree = new ResourceTree<string, null>(null);
assert.equal(tree.root.childrenCount, 0);
tree.add(URI.file('/foo'), 'foo');
assert.equal(tree.root.childrenCount, 1);
assert.equal(tree.root.get('foo')!.element, 'foo');
tree.add(URI.file('/bar'), 'bar');
assert.equal(tree.root.childrenCount, 2);
assert.equal(tree.root.get('bar')!.element, 'bar');
tree.add(URI.file('/foo/file.txt'), 'file');
assert.equal(tree.root.childrenCount, 2);
assert.equal(tree.root.get('foo')!.element, 'foo');
assert.equal(tree.root.get('bar')!.element, 'bar');
assert.equal(tree.root.get('foo')!.get('file.txt')!.element, 'file');
tree.delete(URI.file('/foo'));
assert.equal(tree.root.childrenCount, 1);
assert(!tree.root.get('foo'));
assert.equal(tree.root.get('bar')!.element, 'bar');
tree.delete(URI.file('/bar'));
assert.equal(tree.root.childrenCount, 0);
assert(!tree.root.get('foo'));
assert(!tree.root.get('bar'));
});
});
......@@ -6,7 +6,7 @@
import 'vs/css!./media/scmViewlet';
import { Event, Emitter } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { basename, isEqual } from 'vs/base/common/resources';
import { basename } from 'vs/base/common/resources';
import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle';
import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet';
import { append, $, addClass, toggleClass, trackFocus, removeClass } from 'vs/base/browser/dom';
......@@ -37,7 +37,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import * as platform from 'vs/base/common/platform';
import { ITreeNode, ITreeFilter, ITreeSorter, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
import { ISplice } from 'vs/base/common/sequence';
import { ResourceTree, IBranchNode, INode } from 'vs/base/common/resourceTree';
import { ResourceTree, IResourceNode } from 'vs/base/common/resourceTree';
import { ObjectTree, ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/tree/objectTree';
import { Iterator } from 'vs/base/common/iterator';
import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
......@@ -53,7 +53,7 @@ import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/th
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { toResource, SideBySideEditor } from 'vs/workbench/common/editor';
type TreeElement = ISCMResourceGroup | IBranchNode<ISCMResource, ISCMResourceGroup> | ISCMResource;
type TreeElement = ISCMResourceGroup | IResourceNode<ISCMResource, ISCMResourceGroup> | ISCMResource;
interface ResourceGroupTemplate {
readonly name: HTMLElement;
......@@ -132,11 +132,11 @@ interface ResourceTemplate {
class MultipleSelectionActionRunner extends ActionRunner {
constructor(private getSelectedResources: () => (ISCMResource | IBranchNode<ISCMResource, ISCMResourceGroup>)[]) {
constructor(private getSelectedResources: () => (ISCMResource | IResourceNode<ISCMResource, ISCMResourceGroup>)[]) {
super();
}
runAction(action: IAction, context: ISCMResource | IBranchNode<ISCMResource, ISCMResourceGroup>): Promise<any> {
runAction(action: IAction, context: ISCMResource | IResourceNode<ISCMResource, ISCMResourceGroup>): Promise<any> {
if (!(action instanceof MenuItemAction)) {
return super.runAction(action, context);
}
......@@ -144,12 +144,12 @@ class MultipleSelectionActionRunner extends ActionRunner {
const selection = this.getSelectedResources();
const contextIsSelected = selection.some(s => s === context);
const actualContext = contextIsSelected ? selection : [context];
const args = flatten(actualContext.map(e => ResourceTree.isBranchNode(e) ? ResourceTree.collect(e) : [e]));
const args = flatten(actualContext.map(e => ResourceTree.isResourceNode(e) ? ResourceTree.collect(e) : [e]));
return action.run(...args);
}
}
class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IBranchNode<ISCMResource, ISCMResourceGroup>, FuzzyScore, ResourceTemplate> {
class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IResourceNode<ISCMResource, ISCMResourceGroup>, FuzzyScore, ResourceTemplate> {
static readonly TEMPLATE_ID = 'resource';
get templateId(): string { return ResourceRenderer.TEMPLATE_ID; }
......@@ -158,7 +158,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IBran
private viewModelProvider: () => ViewModel,
private labels: ResourceLabels,
private actionViewItemProvider: IActionViewItemProvider,
private getSelectedResources: () => (ISCMResource | IBranchNode<ISCMResource, ISCMResourceGroup>)[],
private getSelectedResources: () => (ISCMResource | IResourceNode<ISCMResource, ISCMResourceGroup>)[],
private themeService: IThemeService,
private menus: SCMMenus
) { }
......@@ -179,16 +179,16 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IBran
return { element, name, fileLabel, decorationIcon, actionBar, elementDisposables: Disposable.None, disposables };
}
renderElement(node: ITreeNode<ISCMResource, FuzzyScore> | ITreeNode<IBranchNode<ISCMResource, ISCMResourceGroup>, FuzzyScore>, index: number, template: ResourceTemplate): void {
renderElement(node: ITreeNode<ISCMResource, FuzzyScore> | ITreeNode<ISCMResource | IResourceNode<ISCMResource, ISCMResourceGroup>, FuzzyScore>, index: number, template: ResourceTemplate): void {
template.elementDisposables.dispose();
const elementDisposables = new DisposableStore();
const resourceOrFolder = node.element;
const theme = this.themeService.getTheme();
const icon = !ResourceTree.isBranchNode(resourceOrFolder) && (theme.type === LIGHT ? resourceOrFolder.decorations.icon : resourceOrFolder.decorations.iconDark);
const icon = !ResourceTree.isResourceNode(resourceOrFolder) && (theme.type === LIGHT ? resourceOrFolder.decorations.icon : resourceOrFolder.decorations.iconDark);
const uri = ResourceTree.isBranchNode(resourceOrFolder) ? resourceOrFolder.uri : resourceOrFolder.sourceUri;
const fileKind = ResourceTree.isBranchNode(resourceOrFolder) ? FileKind.FOLDER : FileKind.FILE;
const uri = ResourceTree.isResourceNode(resourceOrFolder) ? resourceOrFolder.uri : resourceOrFolder.sourceUri;
const fileKind = ResourceTree.isResourceNode(resourceOrFolder) ? FileKind.FOLDER : FileKind.FILE;
const viewModel = this.viewModelProvider();
template.fileLabel.setFile(uri, {
......@@ -201,7 +201,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IBran
template.actionBar.clear();
template.actionBar.context = resourceOrFolder;
if (ResourceTree.isBranchNode(resourceOrFolder)) {
if (ResourceTree.isResourceNode(resourceOrFolder)) {
elementDisposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceFolderMenu(resourceOrFolder.context), template.actionBar));
removeClass(template.name, 'strike-through');
removeClass(template.element, 'faded');
......@@ -211,7 +211,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IBran
toggleClass(template.element, 'faded', resourceOrFolder.decorations.faded);
}
const tooltip = !ResourceTree.isBranchNode(resourceOrFolder) && resourceOrFolder.decorations.tooltip || '';
const tooltip = !ResourceTree.isResourceNode(resourceOrFolder) && resourceOrFolder.decorations.tooltip || '';
if (icon) {
template.decorationIcon.style.display = '';
......@@ -227,15 +227,15 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IBran
template.elementDisposables = elementDisposables;
}
disposeElement(resource: ITreeNode<ISCMResource, FuzzyScore> | ITreeNode<IBranchNode<ISCMResource, ISCMResourceGroup>, FuzzyScore>, index: number, template: ResourceTemplate): void {
disposeElement(resource: ITreeNode<ISCMResource, FuzzyScore> | ITreeNode<IResourceNode<ISCMResource, ISCMResourceGroup>, FuzzyScore>, index: number, template: ResourceTemplate): void {
template.elementDisposables.dispose();
}
renderCompressedElements(node: ITreeNode<ICompressedTreeNode<ISCMResource> | ICompressedTreeNode<IBranchNode<ISCMResource, ISCMResourceGroup>>, FuzzyScore>, index: number, template: ResourceTemplate, height: number | undefined): void {
renderCompressedElements(node: ITreeNode<ICompressedTreeNode<ISCMResource> | ICompressedTreeNode<IResourceNode<ISCMResource, ISCMResourceGroup>>, FuzzyScore>, index: number, template: ResourceTemplate, height: number | undefined): void {
template.elementDisposables.dispose();
const elementDisposables = new DisposableStore();
const compressed = node.element as ICompressedTreeNode<IBranchNode<ISCMResource, ISCMResourceGroup>>;
const compressed = node.element as ICompressedTreeNode<IResourceNode<ISCMResource, ISCMResourceGroup>>;
const folder = compressed.elements[compressed.elements.length - 1];
const label = compressed.elements.map(e => e.name).join('/');
......@@ -261,7 +261,7 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IBran
template.elementDisposables = elementDisposables;
}
disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<ISCMResource> | ICompressedTreeNode<IBranchNode<ISCMResource, ISCMResourceGroup>>, FuzzyScore>, index: number, template: ResourceTemplate, height: number | undefined): void {
disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<ISCMResource> | ICompressedTreeNode<IResourceNode<ISCMResource, ISCMResourceGroup>>, FuzzyScore>, index: number, template: ResourceTemplate, height: number | undefined): void {
template.elementDisposables.dispose();
}
......@@ -276,7 +276,7 @@ class ProviderListDelegate implements IListVirtualDelegate<TreeElement> {
getHeight() { return 22; }
getTemplateId(element: TreeElement) {
if (ResourceTree.isBranchNode(element) || isSCMResource(element)) {
if (ResourceTree.isResourceNode(element) || isSCMResource(element)) {
return ResourceRenderer.TEMPLATE_ID;
} else {
return ResourceGroupRenderer.TEMPLATE_ID;
......@@ -287,7 +287,7 @@ class ProviderListDelegate implements IListVirtualDelegate<TreeElement> {
class SCMTreeFilter implements ITreeFilter<TreeElement> {
filter(element: TreeElement): boolean {
if (ResourceTree.isBranchNode(element)) {
if (ResourceTree.isResourceNode(element)) {
return true;
} else if (isSCMResourceGroup(element)) {
return element.elements.length > 0 || !element.hideWhenEmpty;
......@@ -313,15 +313,15 @@ export class SCMTreeSorter implements ITreeSorter<TreeElement> {
return 0;
}
const oneIsDirectory = ResourceTree.isBranchNode(one);
const otherIsDirectory = ResourceTree.isBranchNode(other);
const oneIsDirectory = ResourceTree.isResourceNode(one);
const otherIsDirectory = ResourceTree.isResourceNode(other);
if (oneIsDirectory !== otherIsDirectory) {
return oneIsDirectory ? -1 : 1;
}
const oneName = ResourceTree.isBranchNode(one) ? one.name : basename((one as ISCMResource).sourceUri);
const otherName = ResourceTree.isBranchNode(other) ? other.name : basename((other as ISCMResource).sourceUri);
const oneName = ResourceTree.isResourceNode(one) ? one.name : basename((one as ISCMResource).sourceUri);
const otherName = ResourceTree.isResourceNode(other) ? other.name : basename((other as ISCMResource).sourceUri);
return compareFileNames(oneName, otherName);
}
......@@ -330,7 +330,7 @@ export class SCMTreeSorter implements ITreeSorter<TreeElement> {
export class SCMTreeKeyboardNavigationLabelProvider implements ICompressibleKeyboardNavigationLabelProvider<TreeElement> {
getKeyboardNavigationLabel(element: TreeElement): { toString(): string; } | undefined {
if (ResourceTree.isBranchNode(element)) {
if (ResourceTree.isResourceNode(element)) {
return element.name;
} else if (isSCMResourceGroup(element)) {
return element.label;
......@@ -340,7 +340,7 @@ export class SCMTreeKeyboardNavigationLabelProvider implements ICompressibleKeyb
}
getCompressedNodeKeyboardNavigationLabel(elements: TreeElement[]): { toString(): string | undefined; } | undefined {
const folders = elements as IBranchNode<ISCMResource, ISCMResourceGroup>[];
const folders = elements as IResourceNode<ISCMResource, ISCMResourceGroup>[];
return folders.map(e => e.name).join('/');
}
}
......@@ -348,7 +348,7 @@ export class SCMTreeKeyboardNavigationLabelProvider implements ICompressibleKeyb
class SCMResourceIdentityProvider implements IIdentityProvider<TreeElement> {
getId(element: TreeElement): string {
if (ResourceTree.isBranchNode(element)) {
if (ResourceTree.isResourceNode(element)) {
const group = element.context;
return `${group.provider.contextValue}/${group.id}/$FOLDER/${element.uri.toString()}`;
} else if (isSCMResource(element)) {
......@@ -377,16 +377,16 @@ function groupItemAsTreeElement(item: IGroupItem, mode: ViewModelMode): ICompres
return { element: item.group, children, incompressible: true, collapsible: true };
}
function asTreeElement(node: INode<ISCMResource, ISCMResourceGroup>, incompressible: boolean): ICompressedTreeElement<TreeElement> {
if (ResourceTree.isBranchNode(node)) {
return {
element: node,
children: Iterator.map(node.children, node => asTreeElement(node, false)),
incompressible
};
function asTreeElement(node: IResourceNode<ISCMResource, ISCMResourceGroup>, forceIncompressible: boolean): ICompressedTreeElement<TreeElement> {
if (node.childrenCount === 0 && node.element) {
return { element: node.element, incompressible: true };
}
return { element: node.element, incompressible: true };
return {
element: node,
children: Iterator.map(node.children, node => asTreeElement(node, false)),
incompressible: forceIncompressible
};
}
const enum ViewModelMode {
......@@ -538,17 +538,14 @@ class ViewModel {
}
// go backwards from last group
for (let i = this.provider.groups.elements.length - 1; i >= 0; i--) {
const group = this.provider.groups.elements[i];
for (const resource of group.elements) {
if (isEqual(uri, resource.sourceUri)) {
this.tree.reveal(resource);
this.tree.setSelection([resource]);
this.tree.setFocus([resource]);
return;
}
for (let i = this.items.length - 1; i >= 0; i--) {
const node = this.items[i].tree.getNode(uri);
if (node && node.element) {
this.tree.reveal(node.element);
this.tree.setSelection([node.element]);
this.tree.setFocus([node.element]);
return;
}
}
}
......@@ -753,12 +750,12 @@ export class RepositoryPanel extends ViewletPanel {
this._register(Event.chain(this.tree.onDidOpen)
.map(e => e.elements[0])
.filter(e => !!e && !ResourceTree.isBranchNode(e) && isSCMResource(e))
.filter(e => !!e && !isSCMResourceGroup(e) && !ResourceTree.isResourceNode(e))
.on(this.open, this));
this._register(Event.chain(this.tree.onDidPin)
.map(e => e.elements[0])
.filter(e => !!e && !ResourceTree.isBranchNode(e) && isSCMResource(e))
.filter(e => !!e && !isSCMResourceGroup(e) && !ResourceTree.isResourceNode(e))
.on(this.pin, this));
this._register(this.tree.onContextMenu(this.onListContextMenu, this));
......@@ -911,12 +908,12 @@ export class RepositoryPanel extends ViewletPanel {
const element = e.element;
let actions: IAction[] = [];
if (ResourceTree.isBranchNode(element)) {
if (isSCMResourceGroup(element)) {
actions = this.menus.getResourceGroupContextActions(element);
} else if (ResourceTree.isResourceNode(element)) {
actions = this.menus.getResourceFolderContextActions(element.context);
} else if (isSCMResource(element)) {
actions = this.menus.getResourceContextActions(element);
} else {
actions = this.menus.getResourceGroupContextActions(element);
actions = this.menus.getResourceContextActions(element);
}
this.contextMenuService.showContextMenu({
......@@ -927,7 +924,7 @@ export class RepositoryPanel extends ViewletPanel {
});
}
private getSelectedResources(): (ISCMResource | IBranchNode<ISCMResource, ISCMResourceGroup>)[] {
private getSelectedResources(): (ISCMResource | IResourceNode<ISCMResource, ISCMResourceGroup>)[] {
return this.tree.getSelection()
.filter(r => !!r && !isSCMResourceGroup(r))! as any;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册