未验证 提交 92a86119 编写于 作者: J João Moreno

Merge branch 'joao/scm-perf'

......@@ -150,18 +150,22 @@ class MainThreadSCMProvider implements ISCMProvider {
}
}
$registerGroup(handle: number, id: string, label: string): void {
const group = new MainThreadSCMResourceGroup(
this.handle,
handle,
this,
{},
label,
id
);
this._groupsByHandle[handle] = group;
this.groups.splice(this.groups.elements.length, 0, [group]);
$registerGroups(_groups: [number /*handle*/, string /*id*/, string /*label*/, SCMGroupFeatures][]): void {
const groups = _groups.map(([handle, id, label, features]) => {
const group = new MainThreadSCMResourceGroup(
this.handle,
handle,
this,
features,
label,
id
);
this._groupsByHandle[handle] = group;
return group;
});
this.groups.splice(this.groups.elements.length, 0, groups);
}
$updateGroup(handle: number, features: SCMGroupFeatures): void {
......@@ -326,7 +330,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
this._repositories.delete(handle);
}
$registerGroup(sourceControlHandle: number, groupHandle: number, id: string, label: string): void {
$registerGroups(sourceControlHandle: number, groups: [number /*handle*/, string /*id*/, string /*label*/, SCMGroupFeatures][], splices: SCMRawResourceSplices[]): void {
const repository = this._repositories.get(sourceControlHandle);
if (!repository) {
......@@ -334,7 +338,8 @@ export class MainThreadSCM implements MainThreadSCMShape {
}
const provider = repository.provider as MainThreadSCMProvider;
provider.$registerGroup(groupHandle, id, label);
provider.$registerGroups(groups);
provider.$spliceGroupResourceStates(splices);
}
$updateGroup(sourceControlHandle: number, groupHandle: number, features: SCMGroupFeatures): void {
......
......@@ -837,7 +837,7 @@ export interface MainThreadSCMShape extends IDisposable {
$updateSourceControl(handle: number, features: SCMProviderFeatures): void;
$unregisterSourceControl(handle: number): void;
$registerGroup(sourceControlHandle: number, handle: number, id: string, label: string): void;
$registerGroups(sourceControlHandle: number, groups: [number /*handle*/, string /*id*/, string /*label*/, SCMGroupFeatures][], splices: SCMRawResourceSplices[]): void;
$updateGroup(sourceControlHandle: number, handle: number, features: SCMGroupFeatures): void;
$updateGroupLabel(sourceControlHandle: number, handle: number, label: string): void;
$unregisterGroup(sourceControlHandle: number, handle: number): void;
......
......@@ -6,10 +6,10 @@
import { URI, UriComponents } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { debounce } from 'vs/base/common/decorators';
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { asPromise } from 'vs/base/common/async';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto, MainThreadTelemetryShape } from './extHost.protocol';
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto, MainThreadTelemetryShape, SCMGroupFeatures } from './extHost.protocol';
import { sortedDiff, equals } from 'vs/base/common/arrays';
import { comparePaths } from 'vs/base/common/comparers';
import type * as vscode from 'vscode';
......@@ -228,6 +228,9 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
private readonly _onDidUpdateResourceStates = new Emitter<void>();
readonly onDidUpdateResourceStates = this._onDidUpdateResourceStates.event;
private _disposed = false;
get disposed(): boolean { return this._disposed; }
private readonly _onDidDispose = new Emitter<void>();
readonly onDidDispose = this._onDidDispose.event;
......@@ -246,7 +249,13 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
get hideWhenEmpty(): boolean | undefined { return this._hideWhenEmpty; }
set hideWhenEmpty(hideWhenEmpty: boolean | undefined) {
this._hideWhenEmpty = hideWhenEmpty;
this._proxy.$updateGroup(this._sourceControlHandle, this.handle, { hideWhenEmpty });
this._proxy.$updateGroup(this._sourceControlHandle, this.handle, this.features);
}
get features(): SCMGroupFeatures {
return {
hideWhenEmpty: this.hideWhenEmpty
};
}
get resourceStates(): vscode.SourceControlResourceState[] { return [...this._resourceStates]; }
......@@ -263,9 +272,7 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
private _sourceControlHandle: number,
private _id: string,
private _label: string,
) {
this._proxy.$registerGroup(_sourceControlHandle, this.handle, _id, _label);
}
) { }
getResourceState(handle: number): vscode.SourceControlResourceState | undefined {
return this._resourceStatesMap.get(handle);
......@@ -340,7 +347,7 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
}
dispose(): void {
this._proxy.$unregisterGroup(this._sourceControlHandle, this.handle);
this._disposed = true;
this._onDidDispose.fire();
}
}
......@@ -465,24 +472,49 @@ class ExtHostSourceControl implements vscode.SourceControl {
this._proxy.$registerSourceControl(this.handle, _id, _label, _rootUri);
}
private createdResourceGroups = new Map<ExtHostSourceControlResourceGroup, IDisposable>();
private updatedResourceGroups = new Set<ExtHostSourceControlResourceGroup>();
createResourceGroup(id: string, label: string): ExtHostSourceControlResourceGroup {
const group = new ExtHostSourceControlResourceGroup(this._proxy, this._commands, this.handle, id, label);
const disposable = Event.once(group.onDidDispose)(() => this.createdResourceGroups.delete(group));
this.createdResourceGroups.set(group, disposable);
this.eventuallyAddResourceGroups();
return group;
}
const updateListener = group.onDidUpdateResourceStates(() => {
this.updatedResourceGroups.add(group);
this.eventuallyUpdateResourceStates();
});
@debounce(100)
eventuallyAddResourceGroups(): void {
const groups: [number /*handle*/, string /*id*/, string /*label*/, SCMGroupFeatures][] = [];
const splices: SCMRawResourceSplices[] = [];
Event.once(group.onDidDispose)(() => {
this.updatedResourceGroups.delete(group);
updateListener.dispose();
this._groups.delete(group.handle);
});
for (const [group, disposable] of this.createdResourceGroups) {
disposable.dispose();
this._groups.set(group.handle, group);
return group;
const updateListener = group.onDidUpdateResourceStates(() => {
this.updatedResourceGroups.add(group);
this.eventuallyUpdateResourceStates();
});
Event.once(group.onDidDispose)(() => {
this.updatedResourceGroups.delete(group);
updateListener.dispose();
this._groups.delete(group.handle);
this._proxy.$unregisterGroup(this.handle, group.handle);
});
groups.push([group.handle, group.id, group.label, group.features]);
const snapshot = group._takeResourceStateSnapshot();
if (snapshot.length > 0) {
splices.push([group.handle, snapshot]);
}
this._groups.set(group.handle, group);
}
this._proxy.$registerGroups(this.handle, groups, splices);
}
@debounce(100)
......
......@@ -5,7 +5,7 @@
import 'vs/css!./media/scm';
import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
import { IAction, Action } from 'vs/base/common/actions';
......@@ -23,15 +23,46 @@ function actionEquals(a: IAction, b: IAction): boolean {
return a.id === b.id;
}
interface ISCMResourceGroupMenuEntry {
readonly group: ISCMResourceGroup;
readonly disposable: IDisposable;
}
class SCMMenusItem {
private _resourceGroupMenu: IMenu | undefined;
get resourceGroupMenu(): IMenu {
if (!this._resourceGroupMenu) {
this._resourceGroupMenu = this.menuService.createMenu(MenuId.SCMResourceGroupContext, this.contextKeyService);
}
return this._resourceGroupMenu;
}
private _resourceMenu: IMenu | undefined;
get resourceMenu(): IMenu {
if (!this._resourceMenu) {
this._resourceMenu = this.menuService.createMenu(MenuId.SCMResourceContext, this.contextKeyService);
}
return this._resourceMenu;
}
private _resourceFolderMenu: IMenu | undefined;
get resourceFolderMenu(): IMenu {
if (!this._resourceFolderMenu) {
this._resourceFolderMenu = this.menuService.createMenu(MenuId.SCMResourceFolderContext, this.contextKeyService);
}
return this._resourceFolderMenu;
}
constructor(
private contextKeyService: IContextKeyService,
private menuService: IMenuService
) { }
interface ISCMMenus {
readonly resourceGroupMenu: IMenu;
readonly resourceMenu: IMenu;
readonly resourceFolderMenu: IMenu;
dispose(): void {
this.resourceGroupMenu?.dispose();
this.resourceMenu?.dispose();
this.resourceFolderMenu?.dispose();
this.contextKeyService.dispose();
}
}
export function getSCMResourceContextKey(resource: ISCMResourceGroup | ISCMResource): string {
......@@ -50,8 +81,8 @@ export class SCMRepositoryMenus implements IDisposable {
private readonly _onDidChangeTitle = new Emitter<void>();
readonly onDidChangeTitle: Event<void> = this._onDidChangeTitle.event;
private readonly resourceGroupMenuEntries: ISCMResourceGroupMenuEntry[] = [];
private readonly resourceGroupMenus = new Map<ISCMResourceGroup, ISCMMenus>();
private readonly resourceGroups: ISCMResourceGroup[] = [];
private readonly resourceGroupMenusItems = new Map<ISCMResourceGroup, SCMMenusItem>();
private readonly disposables = new DisposableStore();
......@@ -161,55 +192,45 @@ export class SCMRepositoryMenus implements IDisposable {
}
getResourceGroupMenu(group: ISCMResourceGroup): IMenu {
if (!this.resourceGroupMenus.has(group)) {
throw new Error('SCM Resource Group menu not found');
}
return this.resourceGroupMenus.get(group)!.resourceGroupMenu;
return this.getOrCreateResourceGroupMenusItem(group).resourceGroupMenu;
}
getResourceMenu(group: ISCMResourceGroup): IMenu {
if (!this.resourceGroupMenus.has(group)) {
throw new Error('SCM Resource Group menu not found');
}
return this.resourceGroupMenus.get(group)!.resourceMenu;
return this.getOrCreateResourceGroupMenusItem(group).resourceMenu;
}
getResourceFolderMenu(group: ISCMResourceGroup): IMenu {
if (!this.resourceGroupMenus.has(group)) {
throw new Error('SCM Resource Group menu not found');
}
return this.resourceGroupMenus.get(group)!.resourceFolderMenu;
return this.getOrCreateResourceGroupMenusItem(group).resourceFolderMenu;
}
private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice<ISCMResourceGroup>): void {
const menuEntriesToInsert = toInsert.map<ISCMResourceGroupMenuEntry>(group => {
private getOrCreateResourceGroupMenusItem(group: ISCMResourceGroup): SCMMenusItem {
let result = this.resourceGroupMenusItems.get(group);
if (!result) {
const contextKeyService = this.contextKeyService.createScoped();
contextKeyService.createKey('scmProvider', group.provider.contextValue);
contextKeyService.createKey('scmResourceGroup', getSCMResourceContextKey(group));
const resourceGroupMenu = this.menuService.createMenu(MenuId.SCMResourceGroupContext, contextKeyService);
const resourceMenu = this.menuService.createMenu(MenuId.SCMResourceContext, contextKeyService);
const resourceFolderMenu = this.menuService.createMenu(MenuId.SCMResourceFolderContext, contextKeyService);
const disposable = combinedDisposable(contextKeyService, resourceGroupMenu, resourceMenu, resourceFolderMenu);
result = new SCMMenusItem(contextKeyService, this.menuService);
this.resourceGroupMenusItems.set(group, result);
}
this.resourceGroupMenus.set(group, { resourceGroupMenu, resourceMenu, resourceFolderMenu });
return { group, disposable };
});
return result;
}
const deleted = this.resourceGroupMenuEntries.splice(start, deleteCount, ...menuEntriesToInsert);
private onDidSpliceGroups({ start, deleteCount, toInsert }: ISplice<ISCMResourceGroup>): void {
const deleted = this.resourceGroups.splice(start, deleteCount, ...toInsert);
for (const entry of deleted) {
this.resourceGroupMenus.delete(entry.group);
entry.disposable.dispose();
for (const group of deleted) {
const item = this.resourceGroupMenusItems.get(group);
item?.dispose();
this.resourceGroupMenusItems.delete(group);
}
}
dispose(): void {
this.disposables.dispose();
this.resourceGroupMenuEntries.forEach(e => e.disposable.dispose());
this.resourceGroupMenusItems.forEach(item => item.dispose());
}
}
......
......@@ -878,6 +878,7 @@ class ViewModel {
private alwaysShowRepositories = false;
private firstVisible = true;
private repositoryCollapseStates: Map<ISCMRepository, boolean> | undefined;
private viewSubMenuAction: SCMViewSubMenuAction | undefined;
private disposables = new DisposableStore();
constructor(
......@@ -1134,20 +1135,23 @@ class ViewModel {
return [];
}
const viewAction = new SCMViewSubMenuAction(this);
if (!this.viewSubMenuAction) {
this.viewSubMenuAction = new SCMViewSubMenuAction(this);
this.disposables.add(this.viewSubMenuAction);
}
if (this.alwaysShowRepositories || this.repositories.elements.length !== 1) {
return Array.isArray(viewAction.actions) ? viewAction.actions : viewAction.actions();
return this.viewSubMenuAction.actions;
}
const menus = this.menus.getRepositoryMenus(this.repositories.elements[0].provider);
const secondaryActions = menus.getTitleSecondaryActions();
if (secondaryActions.length === 0) {
return [viewAction];
return [this.viewSubMenuAction];
}
return [viewAction, new Separator(), ...secondaryActions];
return [this.viewSubMenuAction, new Separator(), ...secondaryActions];
}
getViewActionsContext(): any {
......@@ -1207,23 +1211,27 @@ class ViewModel {
}
class SCMViewSubMenuAction extends SubmenuAction {
readonly actions!: IAction[];
constructor(viewModel: ViewModel) {
const listAction = new SCMViewModeListAction(viewModel);
const treeAction = new SCMViewModeTreeAction(viewModel);
const sortByNameAction = new SCMSortByNameAction(viewModel);
const sortByPathAction = new SCMSortByPathAction(viewModel);
const sortByStatusAction = new SCMSortByStatusAction(viewModel);
super(
'scm.viewsort',
localize('sortAction', "View & Sort"),
[
...new RadioGroup([
new SCMViewModeListAction(viewModel),
new SCMViewModeTreeAction(viewModel)
]).actions,
...new RadioGroup([listAction, treeAction]).actions,
new Separator(),
...new RadioGroup([
new SCMSortByNameAction(viewModel),
new SCMSortByPathAction(viewModel),
new SCMSortByStatusAction(viewModel)
]).actions
...new RadioGroup([sortByNameAction, sortByPathAction, sortByStatusAction]).actions
]
);
this._register(combinedDisposable(listAction, treeAction, sortByNameAction, sortByPathAction, sortByStatusAction));
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册