提交 38f69570 编写于 作者: J Joao Moreno

scm viewlet: delta changes

related to #37819
上级 d83b859b
......@@ -5,7 +5,7 @@
'use strict';
import Event from 'vs/base/common/event';
import Event, { Emitter } from 'vs/base/common/event';
export interface ISplice<T> {
readonly start: number;
......@@ -20,4 +20,17 @@ export interface ISpliceable<T> {
export interface ISequence<T> {
readonly elements: T[];
readonly onDidSplice: Event<ISplice<T>>;
}
export class Sequence<T> implements ISequence<T>, ISpliceable<T> {
readonly elements: T[] = [];
private _onDidSplice = new Emitter<ISplice<T>>();
readonly onDidSplice: Event<ISplice<T>> = this._onDidSplice.event;
splice(start: number, deleteCount: number, toInsert: T[] = []): void {
this.elements.splice(start, deleteCount, ...toInsert);
this._onDidSplice.fire({ start, deleteCount, toInsert });
}
}
\ No newline at end of file
......@@ -14,26 +14,20 @@ import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGr
import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { Command } from 'vs/editor/common/modes';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { ISplice, ISequence } from 'vs/base/common/sequence';
import { ISplice, Sequence } from 'vs/base/common/sequence';
class MainThreadSCMResourceCollection implements ISequence<ISCMResource> {
class MainThreadSCMResourceGroup implements ISCMResourceGroup {
readonly elements: ISCMResource[] = [];
private _onDidSplice = new Emitter<ISplice<ISCMResource>>();
readonly onDidSplice = this._onDidSplice.event;
splice(start: number, deleteCount: number, toInsert: ISCMResource[]) {
this.elements.splice(start, deleteCount, ...toInsert);
this._onDidSplice.fire({ start, deleteCount, toInsert });
}
}
class MainThreadSCMResourceGroup implements ISCMResourceGroup {
readonly resources = new MainThreadSCMResourceCollection();
get hideWhenEmpty(): boolean { return this.features.hideWhenEmpty; }
private _onDidChange = new Emitter<void>();
get onDidChange(): Event<void> { return this._onDidChange.event; }
constructor(
private sourceControlHandle: number,
private handle: number,
......@@ -50,6 +44,21 @@ class MainThreadSCMResourceGroup implements ISCMResourceGroup {
groupHandle: this.handle
};
}
splice(start: number, deleteCount: number, toInsert: ISCMResource[]) {
this.elements.splice(start, deleteCount, ...toInsert);
this._onDidSplice.fire({ start, deleteCount, toInsert });
}
$updateGroup(features: SCMGroupFeatures): void {
this.features = assign(this.features, features);
this._onDidChange.fire();
}
$updateGroupLabel(label: string): void {
this.label = label;
this._onDidChange.fire();
}
}
class MainThreadSCMResource implements ISCMResource {
......@@ -84,13 +93,18 @@ class MainThreadSCMProvider implements ISCMProvider {
private _id = `scm${MainThreadSCMProvider.ID_HANDLE++}`;
get id(): string { return this._id; }
private _groups: MainThreadSCMResourceGroup[] = [];
readonly groups = new Sequence<MainThreadSCMResourceGroup>();
private _groupsByHandle: { [handle: number]: MainThreadSCMResourceGroup; } = Object.create(null);
get resources(): ISCMResourceGroup[] {
return this._groups
.filter(g => g.resources.elements.length > 0 || !g.features.hideWhenEmpty);
}
// get groups(): ISequence<ISCMResourceGroup> {
// return {
// elements: this._groups,
// onDidSplice: this._onDidSplice.event
// };
// // return this._groups
// // .filter(g => g.resources.elements.length > 0 || !g.features.hideWhenEmpty);
// }
private _onDidChangeResources = new Emitter<void>();
get onDidChangeResources(): Event<void> { return this._onDidChangeResources.event; }
......@@ -141,8 +155,8 @@ class MainThreadSCMProvider implements ISCMProvider {
id
);
this._groups.push(group);
this._groupsByHandle[handle] = group;
this.groups.splice(this.groups.elements.length, 0, [group]);
}
$updateGroup(handle: number, features: SCMGroupFeatures): void {
......@@ -152,8 +166,7 @@ class MainThreadSCMProvider implements ISCMProvider {
return;
}
group.features = assign(group.features, features);
this._onDidChange.fire();
group.$updateGroup(features);
}
$updateGroupLabel(handle: number, label: string): void {
......@@ -163,8 +176,7 @@ class MainThreadSCMProvider implements ISCMProvider {
return;
}
group.label = label;
this._onDidChange.fire();
group.$updateGroupLabel(label);
}
$spliceGroupResourceStates(splices: SCMRawResourceSplices[]): void {
......@@ -206,7 +218,7 @@ class MainThreadSCMProvider implements ISCMProvider {
);
});
group.resources.splice(start, deleteCount, resources);
group.splice(start, deleteCount, resources);
}
}
......@@ -221,7 +233,7 @@ class MainThreadSCMProvider implements ISCMProvider {
}
delete this._groupsByHandle[handle];
this._groups.splice(this._groups.indexOf(group), 1);
this.groups.splice(this.groups.elements.indexOf(group), 1);
}
getOriginalResource(uri: URI): TPromise<URI> {
......
......@@ -58,7 +58,7 @@ export class StatusUpdater implements IWorkbenchContribution {
if (typeof repository.provider.count === 'number') {
return r + repository.provider.count;
} else {
return r + repository.provider.resources.reduce<number>((r, g) => r + g.resources.elements.length, 0);
return r + repository.provider.groups.elements.reduce<number>((r, g) => r + g.elements.length, 0);
}
}, 0);
......
......@@ -54,6 +54,8 @@ import { render as renderOcticons } from 'vs/base/browser/ui/octiconLabel/octico
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import * as platform from 'vs/base/common/platform';
import { format } from 'vs/base/common/strings';
import { ISpliceable, ISequence, ISplice } from 'vs/base/common/sequence';
import { firstIndex } from 'vs/base/common/arrays';
// TODO@Joao
// Need to subclass MenuItemActionItem in order to respect
......@@ -371,7 +373,7 @@ class ResourceGroupRenderer implements IRenderer<ISCMResourceGroup, ResourceGrou
renderElement(group: ISCMResourceGroup, index: number, template: ResourceGroupTemplate): void {
template.name.textContent = group.label;
template.count.setCount(group.resources.elements.length);
template.count.setCount(group.elements.length);
template.actionBar.clear();
template.actionBar.context = group;
template.actionBar.push(this.scmMenus.getResourceGroupActions(group), { icon: true, label: false });
......@@ -493,6 +495,142 @@ function scmResourceIdentityProvider(r: ISCMResourceGroup | ISCMResource): strin
}
}
function isGroupVisible(group: ISCMResourceGroup) {
return group.elements.length > 0 || !group.hideWhenEmpty;
}
interface IGroupItem {
readonly group: ISCMResourceGroup;
visible: boolean;
readonly disposable: IDisposable;
}
class ResourceGroupSplicer {
private items: IGroupItem[] = [];
private disposables: IDisposable[] = [];
constructor(
groupSequence: ISequence<ISCMResourceGroup>,
private spliceable: ISpliceable<ISCMResourceGroup | ISCMResource>
) {
groupSequence.onDidSplice(this.onDidSpliceGroups, this, this.disposables);
this.onDidSpliceGroups({ start: 0, deleteCount: 0, toInsert: groupSequence.elements });
}
private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice<ISCMResourceGroup>): void {
let absoluteStart = 0;
for (let i = 0; i < start; i++) {
const item = this.items[i];
absoluteStart += (item.visible ? 1 : 0) + item.group.elements.length;
}
let absoluteDeleteCount = 0;
for (let i = 0; i < deleteCount; i++) {
const item = this.items[start + i];
absoluteDeleteCount += (item.visible ? 1 : 0) + item.group.elements.length;
}
const itemsToInsert: IGroupItem[] = [];
const absoluteToInsert: (ISCMResourceGroup | ISCMResource)[] = [];
for (const group of toInsert) {
const visible = isGroupVisible(group);
if (visible) {
absoluteToInsert.push(group);
}
for (const element of group.elements) {
absoluteToInsert.push(element);
}
const disposable = combinedDisposable([
group.onDidChange(() => this.onDidChangeGroup(group)),
group.onDidSplice(splice => this.onDidSpliceGroup(group, splice))
]);
itemsToInsert.push({ group, visible, disposable });
}
const itemsToDispose = this.items.splice(start, deleteCount, ...itemsToInsert);
for (const item of itemsToDispose) {
item.disposable.dispose();
}
this.spliceable.splice(absoluteStart, absoluteDeleteCount, absoluteToInsert);
}
private onDidChangeGroup(group: ISCMResourceGroup): void {
const itemIndex = firstIndex(this.items, item => item.group === group);
if (itemIndex < 0) {
return;
}
const item = this.items[itemIndex];
const visible = isGroupVisible(group);
if (item.visible === visible) {
return;
}
let absoluteStart = 0;
for (let i = 0; i < itemIndex; i++) {
const item = this.items[i];
absoluteStart += (item.visible ? 1 : 0) + item.group.elements.length;
}
if (visible) {
this.spliceable.splice(absoluteStart, 0, [group, ...group.elements]);
} else {
this.spliceable.splice(absoluteStart, 1 + group.elements.length, []);
}
item.visible = visible;
}
private onDidSpliceGroup(group: ISCMResourceGroup, { start, deleteCount, toInsert }: ISplice<ISCMResource>): void {
const itemIndex = firstIndex(this.items, item => item.group === group);
if (itemIndex < 0) {
return;
}
const item = this.items[itemIndex];
const visible = isGroupVisible(group);
if (!item.visible && !visible) {
return;
}
let absoluteStart = start;
for (let i = 0; i < itemIndex; i++) {
const item = this.items[i];
absoluteStart += (item.visible ? 1 : 0) + item.group.elements.length;
}
if (item.visible && !visible) {
this.spliceable.splice(absoluteStart, 1 + deleteCount, toInsert);
} else if (!item.visible && visible) {
this.spliceable.splice(absoluteStart, deleteCount, [group, ...toInsert]);
} else {
this.spliceable.splice(absoluteStart + 1, deleteCount, toInsert);
}
item.visible = visible;
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
export class RepositoryPanel extends ViewletPanel {
private cachedHeight: number | undefined = undefined;
......@@ -629,8 +767,8 @@ export class RepositoryPanel extends ViewletPanel {
this.list.onContextMenu(this.onListContextMenu, this, this.disposables);
this.disposables.push(this.list);
this.repository.provider.onDidChangeResources(this.updateList, this, this.disposables);
this.updateList();
const listSplicer = new ResourceGroupSplicer(this.repository.provider.groups, this.list);
this.disposables.push(listSplicer);
}
layoutBody(height: number = this.cachedHeight): void {
......@@ -678,19 +816,6 @@ export class RepositoryPanel extends ViewletPanel {
return this.repository.provider;
}
private updateList(): void {
const elements = this.repository.provider.resources
.reduce<(ISCMResourceGroup | ISCMResource)[]>((r, g) => {
if (g.resources.elements.length === 0 && g.hideWhenEmpty) {
return r;
}
return [...r, g, ...g.resources.elements];
}, []);
this.list.splice(0, this.list.length, elements);
}
private open(e: ISCMResource): void {
e.open().done(undefined, onUnexpectedError);
}
......
......@@ -39,12 +39,12 @@ export interface ISCMResource {
open(): TPromise<void>;
}
export interface ISCMResourceGroup {
export interface ISCMResourceGroup extends ISequence<ISCMResource> {
readonly provider: ISCMProvider;
readonly label: string;
readonly id: string;
readonly resources: ISequence<ISCMResource>;
readonly hideWhenEmpty: boolean;
readonly onDidChange: Event<void>;
}
export interface ISCMProvider extends IDisposable {
......@@ -52,7 +52,9 @@ export interface ISCMProvider extends IDisposable {
readonly id: string;
readonly contextValue: string;
readonly resources: ISCMResourceGroup[];
readonly groups: ISequence<ISCMResourceGroup>;
// TODO@Joao: remove
readonly onDidChangeResources: Event<void>;
readonly rootUri?: URI;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册