diff --git a/extensions/git/src/scmProvider.ts b/extensions/git/src/scmProvider.ts index c69b9fc4d88..ede244543f8 100644 --- a/extensions/git/src/scmProvider.ts +++ b/extensions/git/src/scmProvider.ts @@ -53,9 +53,14 @@ export class GitSCMProvider implements SCMProvider { private disposables: Disposable[] = []; + private _resources: SCMResourceGroup[] = []; + get resources(): SCMResourceGroup[] { return this._resources; } + private _onDidChange = new EventEmitter(); get onDidChange(): Event { return this._onDidChange.event; } + get label(): string { return 'Git'; } + constructor(private model: Model) { model.onDidChange(this.onModelChange, this, this.disposables); model.update(true); @@ -106,21 +111,22 @@ export class GitSCMProvider implements SCMProvider { } }); - const groups: SCMResourceGroup[] = []; + const resources: SCMResourceGroup[] = []; if (merge.length > 0) { - groups.push(new MergeGroup(merge)); + resources.push(new MergeGroup(merge)); } if (index.length > 0) { - groups.push(new IndexGroup(index)); + resources.push(new IndexGroup(index)); } if (workingTree.length > 0) { - groups.push(new WorkingTreeGroup(workingTree)); + resources.push(new WorkingTreeGroup(workingTree)); } - this._onDidChange.fire(groups); + this._resources = resources; + this._onDidChange.fire(resources); } dispose(): void { diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index f679d983b62..f71c084fe7e 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -305,6 +305,7 @@ export function any(...events: Event[]): Event { return emitter.event; } +export function debounceEvent(event: Event, merger: (last: T, event: T) => T, delay?: number): Event; export function debounceEvent(event: Event, merger: (last: O, event: I) => O, delay: number = 100): Event { let subscription: IDisposable; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 62cc7e3284a..3d181534ed0 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -87,21 +87,23 @@ declare module 'vscode' { } export interface SCMResource { - uri: Uri; + readonly uri: Uri; } export interface SCMResourceGroup { - id: string; - label: string; - resources: SCMResource[]; + readonly id: string; + readonly label: string; + readonly resources: SCMResource[]; } export interface SCMProvider { - commitCommand?: string; - clickCommand?: string; - dragCommand?: string; + readonly label: string; + readonly commitCommand?: string; + readonly clickCommand?: string; + readonly dragCommand?: string; - onDidChange: Event; + readonly resources: SCMResourceGroup[]; + readonly onDidChange: Event; getOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult; } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 7251cc8e26e..3d999cba4d7 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -229,21 +229,20 @@ export abstract class MainProcessExtensionServiceShape { } export interface SCMProviderFeatures { + label: string; commitCommand?: string; clickCommand?: string; dragCommand?: string; - resourceGroups: vscode.SCMResourceGroup[]; supportsOriginalResource: boolean; } -export interface SCMRawResource { - uri: string; -} +export type SCMRawResource = [string /*uri*/]; +export type SCMRawResourceGroup = [string /*id*/, string /*label*/, SCMRawResource[]]; export abstract class MainThreadSCMShape { $register(id: string, features: SCMProviderFeatures): void { throw ni(); } $unregister(id: string): void { throw ni(); } - $onChange(id: string, resources: SCMRawResource[][]): void { throw ni(); } + $onChange(id: string, resources: SCMRawResourceGroup[]): void { throw ni(); } } // -- extension host diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index ee13497c1d9..9499f83e8ef 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -6,12 +6,11 @@ import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; -import Event, { Emitter/*, debounceEvent*/ } from 'vs/base/common/event'; -// import { index } from 'vs/base/common/arrays'; +import Event, { Emitter, debounceEvent } from 'vs/base/common/event'; import { asWinJsPromise } from 'vs/base/common/async'; import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; import { Disposable } from 'vs/workbench/api/node/extHostTypes'; -import { MainContext, MainThreadSCMShape/*, SCMRawResource*/ } from './extHost.protocol'; +import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceGroup } from './extHost.protocol'; import * as vscode from 'vscode'; export class ExtHostSCM { @@ -37,37 +36,26 @@ export class ExtHostSCM { // TODO@joao: should pluck all the things out of the provider this._providers[id] = provider; - // const resourceGroupsIds = provider.resourceGroups.map(g => g.id); + this._proxy.$register(id, { + label: provider.label, + commitCommand: provider.commitCommand, + clickCommand: provider.clickCommand, + dragCommand: provider.dragCommand, + supportsOriginalResource: !!provider.getOriginalResource + }); - // this._proxy.$register(id, { - // commitCommand: provider.commitCommand, - // clickCommand: provider.clickCommand, - // dragCommand: provider.dragCommand, - // resourceGroups: provider.resourceGroups, - // supportsOriginalResource: !!provider.getOriginalResource - // }); + const onDidChange = debounceEvent(provider.onDidChange, (l, e) => e, 200); + const onDidChangeListener = onDidChange(resourceGroups => { + const rawResourceGroups = resourceGroups.map(g => { + const rawResources = g.resources.map(r => [r.uri.toString()] as SCMRawResource); + return [g.id, g.label, rawResources] as SCMRawResourceGroup; + }); - // const onDidChange = debounceEvent(provider.onDidChange, (l, e) => e, 200); - // const onDidChangeListener = onDidChange(resources => { - // const resourceGroupsById = index(resourceGroupsIds, id => id, () => [] as SCMRawResource[]); - - // resources.forEach(resource => { - // const resourceGroup = resourceGroupsById[resource.resourceGroup]; - - // if (!resourceGroup) { - // // TODO@Joao: ask Joh: should we warn? should we throw? - // return; - // } - - // resourceGroup.push({ uri: resource.uri.toString() }); - // }); - - // const result = resourceGroupsIds.map(id => resourceGroupsById[id]); - // this._proxy.$onChange(id, result); - // }); + this._proxy.$onChange(id, rawResourceGroups); + }); return new Disposable(() => { - // onDidChangeListener.dispose(); + onDidChangeListener.dispose(); delete this._providers[id]; this._proxy.$unregister(id); }); diff --git a/src/vs/workbench/api/node/mainThreadSCM.ts b/src/vs/workbench/api/node/mainThreadSCM.ts index 8deb73adc1c..d194b60572c 100644 --- a/src/vs/workbench/api/node/mainThreadSCM.ts +++ b/src/vs/workbench/api/node/mainThreadSCM.ts @@ -6,29 +6,36 @@ import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; -import { dispose } from 'vs/base/common/lifecycle'; +import Event, { Emitter } from 'vs/base/common/event'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; -import { ISCMService, ISCMProvider, ISCMResource } from 'vs/workbench/services/scm/common/scm'; +import { ISCMService, ISCMProvider, ISCMResource, ISCMResourceGroup } from 'vs/workbench/services/scm/common/scm'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResource } from './extHost.protocol'; -import { SCMProvider } from 'vs/workbench/services/scm/common/scmProvider'; +import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceGroup } from './extHost.protocol'; -class MainThreadSCMProvider extends SCMProvider { +class MainThreadSCMProvider implements ISCMProvider { + + private _resources: ISCMResourceGroup[] = []; + get resources(): ISCMResourceGroup[] { return this._resources; } + + private _onDidChange = new Emitter(); + get onDidChange(): Event { return this._onDidChange.event; } + + private disposables: IDisposable[] = []; + + get id(): string { return this._id; } + get label(): string { return this.features.label; } constructor( - id: string, + private _id: string, private proxy: ExtHostSCMShape, private features: SCMProviderFeatures, @ISCMService scmService: ISCMService, @ICommandService private commandService: ICommandService ) { - super(id, 'Ext Host SCM Provider'); scmService.onDidChangeProvider(this.onDidChangeProvider, this, this.disposables); this.disposables.push(scmService.registerSCMProvider(this)); - - features.resourceGroups - .forEach(resourceGroup => this.createResourceGroup(resourceGroup.id, resourceGroup.label)); } commit(message: string): TPromise { @@ -75,16 +82,20 @@ class MainThreadSCMProvider extends SCMProvider { // } } - $onChange(raw: SCMRawResource[][]): void { - if (raw.length !== this.resourceGroups.length) { - throw new Error('bad on change'); - } + $onChange(rawResourceGroups: SCMRawResourceGroup[]): void { + this._resources = rawResourceGroups.map(rawGroup => { + const [id, label, rawResources] = rawGroup; - raw.forEach((group, index) => { - const resourceGroup = this.resourceGroups[index]; - const resources = group.map(raw => ({ uri: URI.parse(raw.uri) })); - resourceGroup.set(...resources); + const resources = rawResources.map(rawResource => { + const [uri] = rawResource; + + return { uri: URI.parse(uri) }; + }); + + return { id, label, resources }; }); + + this._onDidChange.fire(this.resources); } dispose(): void { @@ -120,14 +131,14 @@ export class MainThreadSCM extends MainThreadSCMShape { delete this.providers[id]; } - $onChange(id: string, resources: SCMRawResource[][]): void { + $onChange(id: string, rawResourceGroups: SCMRawResourceGroup[]): void { const provider = this.providers[id]; if (!provider) { return; } - provider.$onChange(resources); + provider.$onChange(rawResourceGroups); } dispose(): void { diff --git a/src/vs/workbench/parts/git/browser/gitSCMProvider.ts b/src/vs/workbench/parts/git/browser/gitSCMProvider.ts deleted file mode 100644 index e1f3f0b4687..00000000000 --- a/src/vs/workbench/parts/git/browser/gitSCMProvider.ts +++ /dev/null @@ -1,69 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import { localize } from 'vs/nls'; -import * as path from 'vs/base/common/paths'; -import URI from 'vs/base/common/uri'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { ISCMResourceGroup, ISCMResource } from 'vs/workbench/services/scm/common/scm'; -import { SCMProvider } from 'vs/workbench/services/scm/common/scmProvider'; -import { IGitService, ModelEvents } from 'vs/workbench/parts/git/common/git'; - -export class GitSCMProvider extends SCMProvider { - - private merge: ISCMResourceGroup; - private index: ISCMResourceGroup; - private workingTree: ISCMResourceGroup; - - constructor( - @IGitService private gitService: IGitService - ) { - super('internal-git', 'Git'); - - this.merge = this.createResourceGroup('merge', localize('merge conflicts', "Merge Conflicts")); - this.index = this.createResourceGroup('index', localize('staged changes', "Staged Changes")); - this.workingTree = this.createResourceGroup('workingtree', localize('changes', "Changes")); - - const model = gitService.getModel(); - model.addListener2(ModelEvents.MODEL_UPDATED, () => this.onModelChange()); - } - - private onModelChange(): void { - const model = this.gitService.getModel(); - const root = model.getRepositoryRoot(); - - const status = model.getStatus(); - const mergeStatus = status.getMergeStatus(); - const indexStatus = status.getIndexStatus(); - const workingTreeStatus = status.getWorkingTreeStatus(); - - const toResource = status => ({ uri: URI.file(path.join(root, status.getPath())) }); - - const mergeResources = mergeStatus.all().map(toResource); - const indexResources = indexStatus.all().map(toResource); - const workingTreeResources = workingTreeStatus.all().map(toResource); - - this.merge.set(...mergeResources); - this.index.set(...indexResources); - this.workingTree.set(...workingTreeResources); - } - - commit(message: string): TPromise { - return TPromise.wrapError('not implemented'); - } - - open(resource: ISCMResource): TPromise { - return TPromise.wrapError('not implemented'); - } - - drag(from: ISCMResource, to: ISCMResource): TPromise { - return TPromise.wrapError('not implemented'); - } - - getOriginalResource(uri: URI): TPromise { - return TPromise.wrapError('getOriginalResource not implemented'); - } -} \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/browser/scmViewlet.ts b/src/vs/workbench/parts/scm/browser/scmViewlet.ts index 8f941b4b13c..9ee54ebb041 100644 --- a/src/vs/workbench/parts/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/browser/scmViewlet.ts @@ -35,9 +35,6 @@ import { IAction, IActionItem } from 'vs/base/common/actions'; import { createActionItem } from 'vs/platform/actions/browser/menuItemActionItem'; import { SCMMenus } from './scmMenus'; -// TODO@Joao remove -import { GitSCMProvider } from 'vs/workbench/parts/git/browser/gitSCMProvider'; - interface SearchInputEvent extends Event { target: HTMLInputElement; immediate?: boolean; @@ -64,7 +61,7 @@ class ResourceGroupRenderer implements IRenderer((result, group) => { - const resources = group.get(); - - if (resources.length === 0) { + const elements = provider.resources.reduce<(ISCMResourceGroup | ISCMResource)[]>((result, group) => { + if (group.resources.length === 0) { return result; } - return [...result, group, ...group.get()]; + return [...result, group, ...group.resources]; }, []); this.list.splice(0, this.list.length, ...elements); diff --git a/src/vs/workbench/services/scm/common/scm.ts b/src/vs/workbench/services/scm/common/scm.ts index 2382eaf1c44..143b4d64dcb 100644 --- a/src/vs/workbench/services/scm/common/scm.ts +++ b/src/vs/workbench/services/scm/common/scm.ts @@ -22,16 +22,16 @@ export interface ISCMResource { } export interface ISCMResourceGroup { + readonly id: string; readonly label: string; - readonly onChange: Event; - set(...resources: ISCMResource[]): void; - get(): ISCMResource[]; + readonly resources: ISCMResource[]; } export interface ISCMProvider extends IDisposable { readonly id: string; - readonly onChange: Event; - readonly resourceGroups: ISCMResourceGroup[]; + readonly label: string; + readonly resources: ISCMResourceGroup[]; + readonly onDidChange: Event; commit(message: string): TPromise; open(uri: ISCMResource): TPromise; @@ -41,9 +41,9 @@ export interface ISCMProvider extends IDisposable { export interface ISCMService { - _serviceBrand: any; - activeProvider: ISCMProvider | undefined; - onDidChangeProvider: Event; + readonly _serviceBrand: any; + readonly activeProvider: ISCMProvider | undefined; + readonly onDidChangeProvider: Event; registerSCMProvider(provider: ISCMProvider): IDisposable; } \ No newline at end of file diff --git a/src/vs/workbench/services/scm/common/scmProvider.ts b/src/vs/workbench/services/scm/common/scmProvider.ts deleted file mode 100644 index 353bd728b77..00000000000 --- a/src/vs/workbench/services/scm/common/scmProvider.ts +++ /dev/null @@ -1,94 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import { TPromise } from 'vs/base/common/winjs.base'; -import URI from 'vs/base/common/uri'; -import Event, { Emitter, once, EventMultiplexer } from 'vs/base/common/event'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ISCMProvider, ISCMResource, ISCMResourceGroup } from './scm'; - -export class ResourceGroup implements ISCMResourceGroup, IDisposable { - - private resources: ISCMResource[] = []; - - private _onChange = new Emitter(); - get onChange(): Event { return this._onChange.event; } - - private _onDispose = new Emitter(); - get onDispose(): Event { return this._onDispose.event; } - - get id(): string { return this._id; } - get label(): string { return this._label; } - - constructor(private _id: string, private _label: string) { - - } - - set(...resources: ISCMResource[]): void { - this.resources = resources; - this._onChange.fire(); - } - - get(): ISCMResource[] { - return this.resources; - } - - dispose(): void { - this._onChange.dispose(); - this._onChange = null; - this.resources = null; - } -} - -export abstract class SCMProvider implements ISCMProvider { - - private _onChange = new EventMultiplexer(); - get onChange(): Event { return this._onChange.event; } - - private onResourceGroupsChange = new Emitter(); - - private _resourceGroups: ResourceGroup[] = []; - get resourceGroups(): ResourceGroup[] { return this._resourceGroups; } - - protected disposables: IDisposable[] = []; - - get id(): string { return this._id; } - get label(): string { return this._label; } - - constructor(private _id: string, private _label: string) { - - } - - protected createResourceGroup(id: string, label: string): ISCMResourceGroup { - const resourceGroup = new ResourceGroup(id, label); - this._resourceGroups.push(resourceGroup); - const onChangeListener = this._onChange.add(resourceGroup.onChange); - - once(resourceGroup.onDispose)(() => { - onChangeListener.dispose(); - - const idx = this._resourceGroups.indexOf(resourceGroup); - this._resourceGroups.splice(idx, 1); - - this.onResourceGroupsChange.fire(); - }); - - this.onResourceGroupsChange.fire(); - return resourceGroup; - } - - abstract commit(message: string): TPromise; - abstract open(resource: ISCMResource): TPromise; - abstract drag(from: ISCMResource, to: ISCMResource): TPromise; - abstract getOriginalResource(uri: URI): TPromise; - - dispose(): void { - this._onChange.dispose(); - this._resourceGroups = dispose(this._resourceGroups); - this.disposables = dispose(this.disposables); - } -} \ No newline at end of file