mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 13:46:13 +00:00
scm: folder context actions
This commit is contained in:
parent
bcd7bcedff
commit
ced9fdfa4f
|
@ -55,7 +55,7 @@
|
|||
"command.syncRebase": "Sync (Rebase)",
|
||||
"command.publish": "Publish Branch",
|
||||
"command.showOutput": "Show Git Output",
|
||||
"command.ignore": "Add File to .gitignore",
|
||||
"command.ignore": "Add to .gitignore",
|
||||
"command.stashIncludeUntracked": "Stash (Include Untracked)",
|
||||
"command.stash": "Stash",
|
||||
"command.stashPop": "Pop Stash...",
|
||||
|
|
|
@ -6,57 +6,61 @@
|
|||
import { memoize } from 'vs/base/common/decorators';
|
||||
import * as paths from 'vs/base/common/path';
|
||||
import { Iterator } from 'vs/base/common/iterator';
|
||||
import { relativePath } from 'vs/base/common/resources';
|
||||
import { relativePath, joinPath } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export interface ILeafNode<T> {
|
||||
readonly path: string;
|
||||
export interface ILeafNode<T, C = void> {
|
||||
readonly uri: URI;
|
||||
readonly relativePath: string;
|
||||
readonly name: string;
|
||||
readonly element: T;
|
||||
readonly context: C;
|
||||
}
|
||||
|
||||
export interface IBranchNode<T> {
|
||||
readonly path: string;
|
||||
export interface IBranchNode<T, C = void> {
|
||||
readonly uri: URI;
|
||||
readonly relativePath: string;
|
||||
readonly name: string;
|
||||
readonly size: number;
|
||||
readonly children: Iterator<INode<T>>;
|
||||
readonly parent: IBranchNode<T> | undefined;
|
||||
get(childName: string): INode<T> | undefined;
|
||||
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> = IBranchNode<T> | ILeafNode<T>;
|
||||
export type INode<T, C> = IBranchNode<T, C> | ILeafNode<T, C>;
|
||||
|
||||
// Internals
|
||||
|
||||
class Node {
|
||||
class Node<C> {
|
||||
|
||||
@memoize
|
||||
get name(): string { return paths.posix.basename(this.path); }
|
||||
get name(): string { return paths.posix.basename(this.relativePath); }
|
||||
|
||||
constructor(readonly path: string) { }
|
||||
constructor(readonly uri: URI, readonly relativePath: string, readonly context: C) { }
|
||||
}
|
||||
|
||||
class BranchNode<T> extends Node implements IBranchNode<T> {
|
||||
class BranchNode<T, C> extends Node<C> implements IBranchNode<T, C> {
|
||||
|
||||
private _children = new Map<string, BranchNode<T> | LeafNode<T>>();
|
||||
private _children = new Map<string, BranchNode<T, C> | LeafNode<T, C>>();
|
||||
|
||||
get size(): number {
|
||||
return this._children.size;
|
||||
}
|
||||
|
||||
get children(): Iterator<BranchNode<T> | LeafNode<T>> {
|
||||
get children(): Iterator<BranchNode<T, C> | LeafNode<T, C>> {
|
||||
return Iterator.fromIterableIterator(this._children.values());
|
||||
}
|
||||
|
||||
constructor(path: string, readonly parent: IBranchNode<T> | undefined = undefined) {
|
||||
super(path);
|
||||
constructor(uri: URI, relativePath: string, context: C, readonly parent: IBranchNode<T, C> | undefined = undefined) {
|
||||
super(uri, relativePath, context);
|
||||
}
|
||||
|
||||
get(path: string): BranchNode<T> | LeafNode<T> | undefined {
|
||||
get(path: string): BranchNode<T, C> | LeafNode<T, C> | undefined {
|
||||
return this._children.get(path);
|
||||
}
|
||||
|
||||
set(path: string, child: BranchNode<T> | LeafNode<T>): void {
|
||||
set(path: string, child: BranchNode<T, C> | LeafNode<T, C>): void {
|
||||
this._children.set(path, child);
|
||||
}
|
||||
|
||||
|
@ -65,22 +69,32 @@ class BranchNode<T> extends Node implements IBranchNode<T> {
|
|||
}
|
||||
}
|
||||
|
||||
class LeafNode<T> extends Node implements ILeafNode<T> {
|
||||
class LeafNode<T, C> extends Node<C> implements ILeafNode<T, C> {
|
||||
|
||||
constructor(path: string, readonly element: T) {
|
||||
super(path);
|
||||
constructor(uri: URI, path: string, context: C, readonly element: T) {
|
||||
super(uri, path, context);
|
||||
}
|
||||
}
|
||||
|
||||
export class ResourceTree<T extends NonNullable<any>> {
|
||||
function collect<T, C>(node: INode<T, C>, result: T[]): T[] {
|
||||
if (ResourceTree.isBranchNode(node)) {
|
||||
Iterator.forEach(node.children, child => collect(child, result));
|
||||
} else {
|
||||
result.push(node.element);
|
||||
}
|
||||
|
||||
readonly root = new BranchNode<T>('');
|
||||
return result;
|
||||
}
|
||||
|
||||
static isBranchNode<T>(obj: any): obj is IBranchNode<T> {
|
||||
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;
|
||||
}
|
||||
|
||||
static getRoot<T>(node: IBranchNode<T>): IBranchNode<T> {
|
||||
static getRoot<T, C>(node: IBranchNode<T, C>): IBranchNode<T, C> {
|
||||
while (node.parent) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
@ -88,13 +102,19 @@ export class ResourceTree<T extends NonNullable<any>> {
|
|||
return node;
|
||||
}
|
||||
|
||||
constructor(private rootURI: URI = URI.file('/')) { }
|
||||
static collect<T, C>(node: INode<T, C>): T[] {
|
||||
return collect(node, []);
|
||||
}
|
||||
|
||||
constructor(context: C, rootURI: URI = URI.file('/')) {
|
||||
this.root = new BranchNode(rootURI, '', context);
|
||||
}
|
||||
|
||||
add(uri: URI, element: T): void {
|
||||
const key = relativePath(this.rootURI, uri) || uri.fsPath;
|
||||
const key = relativePath(this.root.uri, uri) || uri.fsPath;
|
||||
const parts = key.split(/[\\\/]/).filter(p => !!p);
|
||||
let node = this.root;
|
||||
let path = this.root.path;
|
||||
let path = '';
|
||||
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const name = parts[i];
|
||||
|
@ -104,10 +124,10 @@ export class ResourceTree<T extends NonNullable<any>> {
|
|||
|
||||
if (!child) {
|
||||
if (i < parts.length - 1) {
|
||||
child = new BranchNode(path, node);
|
||||
child = new BranchNode(joinPath(this.root.uri, path), path, this.root.context, node);
|
||||
node.set(name, child);
|
||||
} else {
|
||||
child = new LeafNode(path, element);
|
||||
child = new LeafNode(uri, path, this.root.context, element);
|
||||
node.set(name, child);
|
||||
return;
|
||||
}
|
||||
|
@ -119,7 +139,7 @@ export class ResourceTree<T extends NonNullable<any>> {
|
|||
}
|
||||
|
||||
// replace
|
||||
node.set(name, new LeafNode(path, element));
|
||||
node.set(name, new LeafNode(uri, path, this.root.context, element));
|
||||
return;
|
||||
} else if (i === parts.length - 1) {
|
||||
throw new Error('Inconsistent tree: can\'t override branch with leaf.');
|
||||
|
@ -130,12 +150,12 @@ export class ResourceTree<T extends NonNullable<any>> {
|
|||
}
|
||||
|
||||
delete(uri: URI): T | undefined {
|
||||
const key = relativePath(this.rootURI, uri) || uri.fsPath;
|
||||
const key = relativePath(this.root.uri, uri) || uri.fsPath;
|
||||
const parts = key.split(/[\\\/]/).filter(p => !!p);
|
||||
return this._delete(this.root, parts, 0);
|
||||
}
|
||||
|
||||
private _delete(node: BranchNode<T>, parts: string[], index: number): T | undefined {
|
||||
private _delete(node: BranchNode<T, C>, parts: string[], index: number): T | undefined {
|
||||
const name = parts[index];
|
||||
const child = node.get(name);
|
||||
|
||||
|
|
|
@ -9,24 +9,24 @@ import { URI } from 'vs/base/common/uri';
|
|||
|
||||
suite('ResourceTree', function () {
|
||||
test('ctor', function () {
|
||||
const tree = new ResourceTree<string>();
|
||||
const tree = new ResourceTree<string, null>(null);
|
||||
assert(ResourceTree.isBranchNode(tree.root));
|
||||
assert.equal(tree.root.size, 0);
|
||||
});
|
||||
|
||||
test('simple', function () {
|
||||
const tree = new ResourceTree<string>();
|
||||
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);
|
||||
|
||||
let foo = tree.root.get('foo') as IBranchNode<string>;
|
||||
let foo = tree.root.get('foo') as IBranchNode<string, null>;
|
||||
assert(foo);
|
||||
assert(ResourceTree.isBranchNode(foo));
|
||||
assert.equal(foo.size, 1);
|
||||
|
||||
let bar = foo.get('bar.txt') as ILeafNode<string>;
|
||||
let bar = foo.get('bar.txt') as ILeafNode<string, null>;
|
||||
assert(bar);
|
||||
assert(!ResourceTree.isBranchNode(bar));
|
||||
assert.equal(bar.element, 'bar contents');
|
||||
|
@ -34,14 +34,14 @@ suite('ResourceTree', function () {
|
|||
tree.add(URI.file('/hello.txt'), 'hello contents');
|
||||
assert.equal(tree.root.size, 2);
|
||||
|
||||
let hello = tree.root.get('hello.txt') as ILeafNode<string>;
|
||||
let hello = tree.root.get('hello.txt') as ILeafNode<string, null>;
|
||||
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>;
|
||||
hello = tree.root.get('hello.txt') as ILeafNode<string, null>;
|
||||
assert(hello);
|
||||
assert(!ResourceTree.isBranchNode(hello));
|
||||
assert.equal(hello.element, 'hello contents');
|
||||
|
|
|
@ -91,6 +91,7 @@
|
|||
.scm-viewlet .monaco-list-row .resource-group {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row .resource-group > .name {
|
||||
|
@ -99,6 +100,7 @@
|
|||
font-weight: bold;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row .resource {
|
||||
|
@ -125,6 +127,7 @@
|
|||
|
||||
.scm-viewlet .monaco-list-row .resource-group > .count {
|
||||
padding: 0 8px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.scm-viewlet .monaco-list-row .resource > .decoration-icon {
|
||||
|
|
|
@ -48,14 +48,14 @@ import { FuzzyScore, createMatches } from 'vs/base/common/filters';
|
|||
import { IViewDescriptor } from 'vs/workbench/common/views';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
type TreeElement = ISCMResourceGroup | IBranchNode<ISCMResource> | ISCMResource;
|
||||
type TreeElement = ISCMResourceGroup | IBranchNode<ISCMResource, ISCMResourceGroup> | ISCMResource;
|
||||
|
||||
interface ResourceGroupTemplate {
|
||||
name: HTMLElement;
|
||||
count: CountBadge;
|
||||
actionBar: ActionBar;
|
||||
readonly name: HTMLElement;
|
||||
readonly count: CountBadge;
|
||||
readonly actionBar: ActionBar;
|
||||
elementDisposables: IDisposable;
|
||||
disposables: IDisposable;
|
||||
readonly disposables: IDisposable;
|
||||
}
|
||||
|
||||
class ResourceGroupRenderer implements ICompressibleTreeRenderer<ISCMResourceGroup, FuzzyScore, ResourceGroupTemplate> {
|
||||
|
@ -130,9 +130,25 @@ class MultipleSelectionActionRunner extends ActionRunner {
|
|||
super();
|
||||
}
|
||||
|
||||
runAction(action: IAction, context: ISCMResource): Promise<any> {
|
||||
if (action instanceof MenuItemAction) {
|
||||
runAction(action: IAction, context: ISCMResource | IBranchNode<ISCMResource, ISCMResourceGroup>): Promise<any> {
|
||||
if (!(action instanceof MenuItemAction)) {
|
||||
return super.runAction(action, context);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// const resources = ResourceTree.isBranchNode(context)
|
||||
// ? ResourceTree.collect(context)
|
||||
// : [context];
|
||||
|
||||
const selection = this.getSelectedResources();
|
||||
|
||||
if (ResourceTree.isBranchNode(context)) {
|
||||
const selection = this.getSelectedResources();
|
||||
const resources = ResourceTree.collect(context);
|
||||
|
||||
return action.run(...resources, ...selection);
|
||||
}
|
||||
|
||||
const filteredSelection = selection.filter(s => s !== context);
|
||||
|
||||
if (selection.length === filteredSelection.length || selection.length === 1) {
|
||||
|
@ -141,12 +157,9 @@ class MultipleSelectionActionRunner extends ActionRunner {
|
|||
|
||||
return action.run(context, ...filteredSelection);
|
||||
}
|
||||
|
||||
return super.runAction(action, context);
|
||||
}
|
||||
}
|
||||
|
||||
class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IBranchNode<ISCMResource>, FuzzyScore, ResourceTemplate> {
|
||||
class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IBranchNode<ISCMResource, ISCMResourceGroup>, FuzzyScore, ResourceTemplate> {
|
||||
|
||||
static TEMPLATE_ID = 'resource';
|
||||
get templateId(): string { return ResourceRenderer.TEMPLATE_ID; }
|
||||
|
@ -176,16 +189,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>, FuzzyScore>, index: number, template: ResourceTemplate): void {
|
||||
renderElement(node: ITreeNode<ISCMResource, FuzzyScore> | ITreeNode<IBranchNode<ISCMResource, ISCMResourceGroup>, FuzzyScore>, index: number, template: ResourceTemplate): void {
|
||||
template.elementDisposables.dispose();
|
||||
|
||||
const elementDisposables = new DisposableStore();
|
||||
const resource = node.element;
|
||||
const resourceOrFolder = node.element;
|
||||
const theme = this.themeService.getTheme();
|
||||
const icon = ResourceTree.isBranchNode(resource) ? undefined : (theme.type === LIGHT ? resource.decorations.icon : resource.decorations.iconDark);
|
||||
const icon = !ResourceTree.isBranchNode(resourceOrFolder) && (theme.type === LIGHT ? resourceOrFolder.decorations.icon : resourceOrFolder.decorations.iconDark);
|
||||
|
||||
const uri = ResourceTree.isBranchNode(resource) ? URI.file(resource.path) : resource.sourceUri;
|
||||
const fileKind = ResourceTree.isBranchNode(resource) ? FileKind.FOLDER : FileKind.FILE;
|
||||
const uri = ResourceTree.isBranchNode(resourceOrFolder) ? resourceOrFolder.uri : resourceOrFolder.sourceUri;
|
||||
const fileKind = ResourceTree.isBranchNode(resourceOrFolder) ? FileKind.FOLDER : FileKind.FILE;
|
||||
const viewModel = this.viewModelProvider();
|
||||
|
||||
template.fileLabel.setFile(uri, {
|
||||
|
@ -196,21 +209,19 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IBran
|
|||
});
|
||||
|
||||
template.actionBar.clear();
|
||||
template.actionBar.context = resource;
|
||||
template.actionBar.context = resourceOrFolder;
|
||||
|
||||
if (ResourceTree.isBranchNode(resource)) {
|
||||
const group = viewModel.getResourceGroupOf(resource);
|
||||
|
||||
if (group) {
|
||||
elementDisposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceFolderMenu(group), template.actionBar));
|
||||
}
|
||||
if (ResourceTree.isBranchNode(resourceOrFolder)) {
|
||||
elementDisposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceFolderMenu(resourceOrFolder.context), template.actionBar));
|
||||
removeClass(template.name, 'strike-through');
|
||||
removeClass(template.element, 'faded');
|
||||
} else {
|
||||
elementDisposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceMenu(resource.resourceGroup), template.actionBar));
|
||||
toggleClass(template.name, 'strike-through', resource.decorations.strikeThrough);
|
||||
toggleClass(template.element, 'faded', resource.decorations.faded);
|
||||
elementDisposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceMenu(resourceOrFolder.resourceGroup), template.actionBar));
|
||||
toggleClass(template.name, 'strike-through', resourceOrFolder.decorations.strikeThrough);
|
||||
toggleClass(template.element, 'faded', resourceOrFolder.decorations.faded);
|
||||
}
|
||||
|
||||
const tooltip = (ResourceTree.isBranchNode(resource) ? resource.path : resource.decorations.tooltip) || '';
|
||||
const tooltip = !ResourceTree.isBranchNode(resourceOrFolder) && resourceOrFolder.decorations.tooltip || '';
|
||||
|
||||
if (icon) {
|
||||
template.decorationIcon.style.display = '';
|
||||
|
@ -219,47 +230,48 @@ class ResourceRenderer implements ICompressibleTreeRenderer<ISCMResource | IBran
|
|||
} else {
|
||||
template.decorationIcon.style.display = 'none';
|
||||
template.decorationIcon.style.backgroundImage = '';
|
||||
template.decorationIcon.title = '';
|
||||
}
|
||||
|
||||
template.element.setAttribute('data-tooltip', tooltip);
|
||||
template.elementDisposables = elementDisposables;
|
||||
}
|
||||
|
||||
renderCompressedElements(node: ITreeNode<ICompressedTreeNode<ISCMResource> | ICompressedTreeNode<IBranchNode<ISCMResource>>, FuzzyScore>, index: number, template: ResourceTemplate, height: number | undefined): void {
|
||||
disposeElement(resource: ITreeNode<ISCMResource, FuzzyScore> | ITreeNode<IBranchNode<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 {
|
||||
template.elementDisposables.dispose();
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
const compressed = node.element as ICompressedTreeNode<IBranchNode<ISCMResource>>;
|
||||
const branchNode = compressed.elements[compressed.elements.length - 1];
|
||||
const elementDisposables = new DisposableStore();
|
||||
const compressed = node.element as ICompressedTreeNode<IBranchNode<ISCMResource, ISCMResourceGroup>>;
|
||||
const folder = compressed.elements[compressed.elements.length - 1];
|
||||
|
||||
const label = compressed.elements.map(e => e.name).join('/');
|
||||
const uri = URI.file(branchNode.path);
|
||||
const fileKind = FileKind.FOLDER;
|
||||
|
||||
template.fileLabel.setResource({ resource: uri, name: label }, {
|
||||
template.fileLabel.setResource({ resource: folder.uri, name: label }, {
|
||||
fileDecorations: { colors: false, badges: true },
|
||||
fileKind,
|
||||
matches: createMatches(node.filterData)
|
||||
});
|
||||
|
||||
template.actionBar.clear();
|
||||
template.actionBar.context = 'what'; // TODO
|
||||
template.actionBar.context = folder;
|
||||
|
||||
const viewModel = this.viewModelProvider();
|
||||
const group = viewModel.getResourceGroupOf(branchNode);
|
||||
|
||||
if (group) {
|
||||
disposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceFolderMenu(group), template.actionBar));
|
||||
}
|
||||
elementDisposables.add(connectPrimaryMenuToInlineActionBar(this.menus.getResourceFolderMenu(folder.context), template.actionBar));
|
||||
|
||||
removeClass(template.name, 'strike-through');
|
||||
removeClass(template.element, 'faded');
|
||||
template.decorationIcon.style.display = 'none';
|
||||
template.decorationIcon.style.backgroundImage = '';
|
||||
|
||||
template.element.setAttribute('data-tooltip', branchNode.path);
|
||||
template.elementDisposables = disposables;
|
||||
template.element.setAttribute('data-tooltip', '');
|
||||
template.elementDisposables = elementDisposables;
|
||||
}
|
||||
|
||||
disposeElement(resource: ITreeNode<ISCMResource, FuzzyScore> | ITreeNode<IBranchNode<ISCMResource>, FuzzyScore>, index: number, template: ResourceTemplate): void {
|
||||
disposeCompressedElements(node: ITreeNode<ICompressedTreeNode<ISCMResource> | ICompressedTreeNode<IBranchNode<ISCMResource, ISCMResourceGroup>>, FuzzyScore>, index: number, template: ResourceTemplate, height: number | undefined): void {
|
||||
template.elementDisposables.dispose();
|
||||
}
|
||||
|
||||
|
@ -331,25 +343,27 @@ export class SCMTreeKeyboardNavigationLabelProvider implements IKeyboardNavigati
|
|||
}
|
||||
}
|
||||
|
||||
const scmResourceIdentityProvider = new class implements IIdentityProvider<TreeElement> {
|
||||
getId(e: TreeElement): string {
|
||||
if (ResourceTree.isBranchNode(e)) {
|
||||
return e.path;
|
||||
} else if (isSCMResource(e)) {
|
||||
const group = e.resourceGroup;
|
||||
class SCMResourceIdentityProvider implements IIdentityProvider<TreeElement> {
|
||||
|
||||
getId(element: TreeElement): string {
|
||||
if (ResourceTree.isBranchNode(element)) {
|
||||
const group = element.context;
|
||||
return `${group.provider.contextValue}/${group.id}/$FOLDER/${element.uri.toString()}`;
|
||||
} else if (isSCMResource(element)) {
|
||||
const group = element.resourceGroup;
|
||||
const provider = group.provider;
|
||||
return `${provider.contextValue}/${group.id}/${e.sourceUri.toString()}`;
|
||||
return `${provider.contextValue}/${group.id}/${element.sourceUri.toString()}`;
|
||||
} else {
|
||||
const provider = e.provider;
|
||||
return `${provider.contextValue}/${e.id}`;
|
||||
const provider = element.provider;
|
||||
return `${provider.contextValue}/${element.id}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
interface IGroupItem {
|
||||
readonly group: ISCMResourceGroup;
|
||||
readonly resources: ISCMResource[];
|
||||
readonly tree: ResourceTree<ISCMResource>;
|
||||
readonly tree: ResourceTree<ISCMResource, ISCMResourceGroup>;
|
||||
readonly disposable: IDisposable;
|
||||
}
|
||||
|
||||
|
@ -361,7 +375,7 @@ function groupItemAsTreeElement(item: IGroupItem, mode: ViewModelMode): ICompres
|
|||
return { element: item.group, children, incompressible: true };
|
||||
}
|
||||
|
||||
function asTreeElement(node: INode<ISCMResource>, incompressible: boolean): ICompressedTreeElement<TreeElement> {
|
||||
function asTreeElement(node: INode<ISCMResource, ISCMResourceGroup>, incompressible: boolean): ICompressedTreeElement<TreeElement> {
|
||||
if (ResourceTree.isBranchNode(node)) {
|
||||
return {
|
||||
element: node,
|
||||
|
@ -406,7 +420,7 @@ class ViewModel {
|
|||
const itemsToInsert: IGroupItem[] = [];
|
||||
|
||||
for (const group of toInsert) {
|
||||
const tree = new ResourceTree<ISCMResource>(group.provider.rootUri || URI.file('/'));
|
||||
const tree = new ResourceTree<ISCMResource, ISCMResourceGroup>(group, group.provider.rootUri || URI.file('/'));
|
||||
const resources: ISCMResource[] = [...group.elements];
|
||||
const disposable = combinedDisposable(
|
||||
group.onDidChange(() => this.tree.refilter()),
|
||||
|
@ -464,18 +478,6 @@ class ViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
getResourceGroupOf(node: IBranchNode<ISCMResource>): ISCMResourceGroup | undefined {
|
||||
const root = ResourceTree.getRoot(node);
|
||||
|
||||
for (const item of this.items) {
|
||||
if (item.tree.root === root) {
|
||||
return item.group;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.visibilityDisposables.dispose();
|
||||
this.disposables.dispose();
|
||||
|
@ -656,6 +658,7 @@ export class RepositoryPanel extends ViewletPanel {
|
|||
const filter = new SCMTreeFilter();
|
||||
const sorter = new SCMTreeSorter();
|
||||
const keyboardNavigationLabelProvider = new SCMTreeKeyboardNavigationLabelProvider();
|
||||
const identityProvider = new SCMResourceIdentityProvider();
|
||||
|
||||
this.tree = this.instantiationService.createInstance(
|
||||
WorkbenchCompressibleObjectTree,
|
||||
|
@ -664,7 +667,7 @@ export class RepositoryPanel extends ViewletPanel {
|
|||
delegate,
|
||||
renderers,
|
||||
{
|
||||
identityProvider: scmResourceIdentityProvider,
|
||||
identityProvider,
|
||||
horizontalScrolling: false,
|
||||
filter,
|
||||
sorter,
|
||||
|
@ -786,11 +789,7 @@ export class RepositoryPanel extends ViewletPanel {
|
|||
let actions: IAction[] = [];
|
||||
|
||||
if (ResourceTree.isBranchNode(element)) {
|
||||
const group = this.viewModel.getResourceGroupOf(element);
|
||||
|
||||
if (group) {
|
||||
actions = this.menus.getResourceFolderContextActions(group);
|
||||
}
|
||||
actions = this.menus.getResourceFolderContextActions(element.context);
|
||||
} else if (isSCMResource(element)) {
|
||||
actions = this.menus.getResourceContextActions(element);
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue