From ceda84ef281fd42e5a032e5bf6bea873b8ec9a28 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 24 Nov 2016 16:24:32 +0100 Subject: [PATCH] streamline scm extension api --- extensions/git/package.json | 1 + extensions/git/src/main.ts | 29 +++++---- src/vs/vscode.proposed.d.ts | 28 ++++---- src/vs/workbench/api/node/extHost.api.impl.ts | 51 +++++++++++++-- src/vs/workbench/api/node/extHostSCM.ts | 65 +++++++------------ 5 files changed, 102 insertions(+), 72 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 4b80596c8f7..7b08ec75ad3 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -7,6 +7,7 @@ "engines": { "vscode": "^1.5.0" }, + "enableProposedApi": true, "categories": [ "Other" ], diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index dc488604947..7173b890b5f 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -5,7 +5,7 @@ 'use strict'; -import { scm, ExtensionContext, workspace } from 'vscode'; +import { scm, ExtensionContext, workspace, Uri } from 'vscode'; import * as path from 'path'; import { findGit, Git } from './git'; @@ -13,6 +13,19 @@ export function log(...args: any[]): void { console.log.apply(console, ['git:', ...args]); } +class GitSCMProvider { + resourceGroups = []; + onDidChangeResourceGroup = null; + + getOriginalResource(uri: Uri): Uri { + if (uri.scheme !== 'file') { + return null; + } + + return uri.with({ scheme: 'git-index' }); + } +} + export function activate(context: ExtensionContext): any { if (!workspace) { return; @@ -24,16 +37,8 @@ export function activate(context: ExtensionContext): any { log(`Using git ${info.version} from ${info.path}`); const git = new Git({ gitPath: info.path, version: info.version }); - - const scmProvider = scm.createSCMProvider('git', { - getOriginalResource: uri => { - if (uri.scheme !== 'file') { - return null; - } - - return uri.with({ scheme: 'git-index' }); - } - }); + const provider = new GitSCMProvider(); + const providerDisposable = scm.registerSCMProvider('git', provider); const contentProvider = workspace.registerTextDocumentContentProvider('git-index', { provideTextDocumentContent: uri => { @@ -49,6 +54,6 @@ export function activate(context: ExtensionContext): any { } }); - context.subscriptions.push(scmProvider, contentProvider); + context.subscriptions.push(providerDisposable, contentProvider); }); } \ No newline at end of file diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 039843bdab2..e23ad8d5e36 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -86,28 +86,26 @@ declare module 'vscode' { getClickCommand?(node: T): string; } - export namespace scm { - export function createSCMProvider(id: string, delegate: SCMDelegate): SCMProvider; + export interface SCMResource { + uri: Uri; } - export interface SCMDelegate { + export interface SCMResourceGroup { + resources: SCMResource[]; + } + + export interface SCMProvider { commitCommand?: string; clickCommand?: string; dragCommand?: string; + resourceGroups: SCMResourceGroup[]; + onDidChangeResourceGroup: Event; getOriginalResource?(uri: Uri, token: CancellationToken): Uri | Thenable; } - export interface SCMProvider extends Disposable { - createResourceGroup(id: string, label: string): SCMResourceGroup; - } - - export interface SCMResourceGroup extends Disposable { - set(...resources: SCMResource[]): void; - get(): SCMResource[]; - } - - export interface SCMResource { - uri: Uri; - // TODO: status type, icon decoration, etc + export namespace scm { + export const onDidChangeActiveProvider: Event; + export let activeProvider: SCMProvider | undefined; + export function registerSCMProvider(id: string, provider: SCMProvider): Disposable; } } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 6358e78a423..53d3c1871f5 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -60,6 +60,33 @@ function proposedApiFunction(extension: IExtensionDescription, fn: T): T { } } +function proposedAPI(extension: IExtensionDescription): Function { + return (target: any, key: string, descriptor: any) => { + let fnKey: string = null; + let fn: Function = null; + + if (typeof descriptor.value === 'function') { + fnKey = 'value'; + fn = descriptor.value; + } else if (typeof descriptor.get === 'function') { + fnKey = 'get'; + fn = descriptor.get; + } + + if (!fn) { + throw new Error('not supported'); + } + + if (extension.enableProposedApi) { + return; + } + + descriptor[fnKey] = () => { + throw new Error(`${extension.id} cannot access proposed api`); + }; + }; +} + /** * This method instantiates and returns the extension API surface */ @@ -360,12 +387,26 @@ export function createApiFactory(initData: IInitData, threadService: IThreadServ } }; - // namespace: scm - const scm: typeof vscode.scm = { - createSCMProvider: (id, delegate): vscode.SCMProvider => { - return extHostSCM.createSCMProvider(id, delegate); + class SCM { + + @proposedAPI(extension) + get activeProvider() { + return extHostSCM.activeProvider; } - }; + + @proposedAPI(extension) + get onDidChangeActiveProvider() { + return extHostSCM.onDidChangeActiveProvider; + } + + @proposedAPI(extension) + registerSCMProvider(id, provider) { + return extHostSCM.registerSCMProvider(id, provider); + } + } + + // namespace: scm + const scm: typeof vscode.scm = new SCM(); return { version: pkg.version, diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index ef8539221d8..2afcae7a5f7 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -6,65 +6,50 @@ import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; +import Event, { Emitter } from 'vs/base/common/event'; import { asWinJsPromise } from 'vs/base/common/async'; import { IThreadService } from 'vs/workbench/services/thread/common/threadService'; -import { SCMProvider, SCMDelegate, SCMResourceGroup } from 'vscode'; +import { SCMProvider } from 'vscode'; +import { Disposable } from 'vs/workbench/api/node/extHostTypes'; import { MainContext, MainThreadSCMShape } from './extHost.protocol'; -class ExtHostSCMProvider implements SCMProvider { - - static Providers: { [id: string]: ExtHostSCMProvider; } = Object.create(null); - - constructor( - private _proxy: MainThreadSCMShape, - private _id: string, - private _delegate: SCMDelegate - ) { - if (ExtHostSCMProvider.Providers[_id]) { - throw new Error('provider already exists'); - } - - ExtHostSCMProvider.Providers[_id] = this; - _proxy.$register(this._id, !!this._delegate.getOriginalResource); - } - - get id(): string { - return this._id; - } - - createResourceGroup(id: string, label: string): SCMResourceGroup { - throw new Error('JOAO not implemented'); - } - - getBaselineResource(uri: URI): TPromise { - return asWinJsPromise(token => this._delegate.getOriginalResource(uri, token)); - } - - dispose(): void { - this._proxy.$unregister(this._id); - delete ExtHostSCMProvider.Providers[this.id]; - } -} - export class ExtHostSCM { private _proxy: MainThreadSCMShape; + private _providers: { [id: string]: SCMProvider; } = Object.create(null); + + private _onDidChangeActiveProvider = new Emitter(); + get onDidChangeActiveProvider(): Event { return this._onDidChangeActiveProvider.event; } + + private _activeProvider: SCMProvider; + get activeProvider(): SCMProvider | undefined { return this._activeProvider; } constructor(threadService: IThreadService) { this._proxy = threadService.get(MainContext.MainThreadSCM); } - createSCMProvider(id: string, delegate: SCMDelegate): SCMProvider { - return new ExtHostSCMProvider(this._proxy, id, delegate); + registerSCMProvider(id: string, provider: SCMProvider): Disposable { + if (this._providers[id]) { + throw new Error(`Provider ${id} already registered`); + } + + // TODO@joao: should pluck all the things out of the provider + this._providers[id] = provider; + this._proxy.$register(id, !!provider.getOriginalResource); + + return new Disposable(() => { + delete this._providers[id]; + this._proxy.$unregister(id); + }); } $getBaselineResource(id: string, uri: URI): TPromise { - const provider = ExtHostSCMProvider.Providers[id]; + const provider = this._providers[id]; if (!provider) { return TPromise.as(null); } - return provider.getBaselineResource(uri); + return asWinJsPromise(token => provider.getOriginalResource(uri, token)); } }