Merge branch 'master' into joh/progress-api

This commit is contained in:
Johannes Rieken
2017-01-16 18:12:48 +01:00
130 changed files with 28367 additions and 950 deletions

View File

@@ -21,6 +21,7 @@ import { ExtHostTreeExplorers } from 'vs/workbench/api/node/extHostTreeExplorers
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostQuickOpen } from 'vs/workbench/api/node/extHostQuickOpen';
import { ExtHostProgress } from 'vs/workbench/api/node/extHostProgress';
import { ExtHostSCM } from 'vs/workbench/api/node/extHostSCM';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { ExtHostStatusBar } from 'vs/workbench/api/node/extHostStatusBar';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
@@ -60,6 +61,33 @@ function proposedApiFunction<T>(extension: IExtensionDescription, fn: T): T {
}
}
function proposed(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
*/
@@ -79,6 +107,7 @@ export function createApiFactory(initData: IInitData, threadService: IThreadServ
const extHostFileSystemEvent = col.define(ExtHostContext.ExtHostFileSystemEventService).set<ExtHostFileSystemEventService>(new ExtHostFileSystemEventService());
const extHostQuickOpen = col.define(ExtHostContext.ExtHostQuickOpen).set<ExtHostQuickOpen>(new ExtHostQuickOpen(threadService));
const extHostTerminalService = col.define(ExtHostContext.ExtHostTerminalService).set<ExtHostTerminalService>(new ExtHostTerminalService(threadService));
const extHostSCM = col.define(ExtHostContext.ExtHostSCM).set<ExtHostSCM>(new ExtHostSCM(threadService));
col.define(ExtHostContext.ExtHostExtensionService).set(extensionService);
col.finish(false, threadService);
@@ -369,6 +398,32 @@ export function createApiFactory(initData: IInitData, threadService: IThreadServ
}
};
class SCM {
@proposed(extension)
get activeProvider() {
return extHostSCM.activeProvider;
}
@proposed(extension)
get onDidChangeActiveProvider() {
return extHostSCM.onDidChangeActiveProvider;
}
@proposed(extension)
getResourceFromURI(uri) {
return extHostSCM.getResourceFromURI(uri);
}
@proposed(extension)
registerSCMProvider(id, provider) {
return extHostSCM.registerSCMProvider(id, provider);
}
}
// namespace: scm
const scm: typeof vscode.scm = new SCM();
return {
version: pkg.version,
// namespaces
@@ -378,6 +433,7 @@ export function createApiFactory(initData: IInitData, threadService: IThreadServ
languages,
window,
workspace,
scm,
// types
CancellationTokenSource: CancellationTokenSource,
CodeLens: extHostTypes.CodeLens,

View File

@@ -33,6 +33,7 @@ import { MainThreadTerminalService } from './mainThreadTerminalService';
import { MainThreadWorkspace } from './mainThreadWorkspace';
import { MainProcessExtensionService } from './mainThreadExtensionService';
import { MainThreadFileSystemEventService } from './mainThreadFileSystemEventService';
import { MainThreadSCM } from './mainThreadSCM';
// --- other interested parties
import { MainProcessTextMateSnippet } from 'vs/editor/node/textMate/TMSnippets';
@@ -82,6 +83,7 @@ export class ExtHostContribution implements IWorkbenchContribution {
col.define(MainContext.MainThreadTelemetry).set(create(MainThreadTelemetry));
col.define(MainContext.MainThreadTerminalService).set(create(MainThreadTerminalService));
col.define(MainContext.MainThreadWorkspace).set(create(MainThreadWorkspace));
col.define(MainContext.MainThreadSCM).set(create(MainThreadSCM));
if (this.extensionService instanceof MainProcessExtensionService) {
col.define(MainContext.MainProcessExtensionService).set(<MainProcessExtensionService>this.extensionService);
}

View File

@@ -236,6 +236,27 @@ export abstract class MainProcessExtensionServiceShape {
$onExtensionActivationFailed(extensionId: string): void { throw ni(); }
}
export interface SCMProviderFeatures {
label: string;
supportsCommit: boolean;
supportsOpen: boolean;
supportsDrag: boolean;
supportsOriginalResource: boolean;
}
export type SCMRawResource = [
string /*uri*/,
string[] /*icons: light, dark*/,
boolean /*strike through*/
];
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: SCMRawResourceGroup[]): void { throw ni(); }
}
// -- extension host
export abstract class ExtHostCommandsShape {
@@ -364,6 +385,13 @@ export abstract class ExtHostTerminalServiceShape {
$acceptTerminalProcessId(id: number, processId: number): void { throw ni(); }
}
export abstract class ExtHostSCMShape {
$commit(id: string, message: string): TPromise<void> { throw ni(); }
$open(id: string, resourceGroupId: string, uri: string): TPromise<void> { throw ni(); }
$drag(id: string, fromResourceGroupId: string, fromUri: string, toResourceGroupId: string): TPromise<void> { throw ni(); }
$getOriginalResource(id: string, uri: URI): TPromise<URI> { throw ni(); }
}
// --- proxy identifiers
export const MainContext = {
@@ -386,6 +414,7 @@ export const MainContext = {
MainThreadTerminalService: createMainId<MainThreadTerminalServiceShape>('MainThreadTerminalService', MainThreadTerminalServiceShape),
MainThreadWorkspace: createMainId<MainThreadWorkspaceShape>('MainThreadWorkspace', MainThreadWorkspaceShape),
MainProcessExtensionService: createMainId<MainProcessExtensionServiceShape>('MainProcessExtensionService', MainProcessExtensionServiceShape),
MainThreadSCM: createMainId<MainThreadSCMShape>('MainThreadSCM', MainThreadSCMShape)
};
export const ExtHostContext = {
@@ -401,5 +430,6 @@ export const ExtHostContext = {
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures', ExtHostLanguageFeaturesShape),
ExtHostQuickOpen: createExtId<ExtHostQuickOpenShape>('ExtHostQuickOpen', ExtHostQuickOpenShape),
ExtHostExtensionService: createExtId<ExtHostExtensionServiceShape>('ExtHostExtensionService', ExtHostExtensionServiceShape),
ExtHostTerminalService: createExtId<ExtHostTerminalServiceShape>('ExtHostTerminalService', ExtHostTerminalServiceShape)
ExtHostTerminalService: createExtId<ExtHostTerminalServiceShape>('ExtHostTerminalService', ExtHostTerminalServiceShape),
ExtHostSCM: createExtId<ExtHostSCMShape>('ExtHostSCM', ExtHostSCMShape)
};

View File

@@ -0,0 +1,222 @@
/*---------------------------------------------------------------------------------------------
* 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 URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
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, SCMRawResourceGroup } from './extHost.protocol';
import * as vscode from 'vscode';
function getIconPath(decorations: vscode.SCMResourceThemableDecorations) {
if (!decorations) {
return undefined;
} else if (typeof decorations.iconPath === 'string') {
return URI.file(decorations.iconPath).toString();
} else if (decorations.iconPath) {
return `${decorations.iconPath}`;
}
}
export interface Cache {
[providerId: string]: {
[groupId: string]: {
resourceGroup: vscode.SCMResourceGroup,
resources: { [uri: string]: vscode.SCMResource }
};
};
}
export class ExtHostSCM {
private _proxy: MainThreadSCMShape;
private _providers: { [id: string]: vscode.SCMProvider; } = Object.create(null);
private _onDidChangeActiveProvider = new Emitter<vscode.SCMProvider>();
get onDidChangeActiveProvider(): Event<vscode.SCMProvider> { return this._onDidChangeActiveProvider.event; }
private _activeProvider: vscode.SCMProvider;
get activeProvider(): vscode.SCMProvider | undefined { return this._activeProvider; }
private cache: Cache = Object.create(null);
constructor(threadService: IThreadService) {
this._proxy = threadService.get(MainContext.MainThreadSCM);
}
getResourceFromURI(uri: vscode.Uri): vscode.SCMResource | vscode.SCMResourceGroup | undefined {
if (uri.scheme !== 'scm') {
return undefined;
}
const providerId = uri.authority;
const providerCache = this.cache[providerId];
if (!providerCache) {
return undefined;
}
const match = /^\/([^/]+)(\/(.*))?$/.exec(uri.path);
if (!match) {
return undefined;
}
const resourceGroupId = match[1];
const resourceGroupRef = providerCache[resourceGroupId];
if (!resourceGroupRef) {
return undefined;
}
const rawResourceUri = match[3];
if (!rawResourceUri) {
return resourceGroupRef.resourceGroup;
}
let resourceUri: string;
try {
const rawResource = JSON.parse(rawResourceUri);
const resource = URI.from(rawResource);
resourceUri = resource.toString();
} catch (err) {
resourceUri = undefined;
}
if (!resourceUri) {
return undefined;
}
const resource = resourceGroupRef.resources[resourceUri];
if (!resource) {
return undefined;
}
return resource;
}
registerSCMProvider(providerId: string, provider: vscode.SCMProvider): Disposable {
if (this._providers[providerId]) {
throw new Error(`Provider ${providerId} already registered`);
}
// TODO@joao: should pluck all the things out of the provider
this._providers[providerId] = provider;
this._proxy.$register(providerId, {
label: provider.label,
supportsCommit: !!provider.commit,
supportsOpen: !!provider.open,
supportsDrag: !!provider.drag,
supportsOriginalResource: !!provider.getOriginalResource
});
const onDidChange = debounceEvent(provider.onDidChange, (l, e) => e, 100);
const onDidChangeListener = onDidChange(resourceGroups => {
this.cache[providerId] = Object.create(null);
const rawResourceGroups = resourceGroups.map(g => {
const resources: { [id: string]: vscode.SCMResource; } = Object.create(null);
const rawResources = g.resources.map(r => {
const uri = r.uri.toString();
const iconPath = getIconPath(r.decorations);
const lightIconPath = r.decorations && getIconPath(r.decorations.light) || iconPath;
const darkIconPath = r.decorations && getIconPath(r.decorations.dark) || iconPath;
const icons: string[] = [];
if (lightIconPath || darkIconPath) {
icons.push(lightIconPath);
}
if (darkIconPath !== lightIconPath) {
icons.push(darkIconPath);
}
const strikeThrough = r.decorations && !!r.decorations.strikeThrough;
resources[uri] = r;
return [uri, icons, strikeThrough] as SCMRawResource;
});
this.cache[providerId][g.id] = { resourceGroup: g, resources };
return [g.id, g.label, rawResources] as SCMRawResourceGroup;
});
this._proxy.$onChange(providerId, rawResourceGroups);
});
return new Disposable(() => {
onDidChangeListener.dispose();
delete this._providers[providerId];
this._proxy.$unregister(providerId);
});
}
$commit(providerId: string, message: string): TPromise<void> {
const provider = this._providers[providerId];
if (!provider) {
return TPromise.as(null);
}
return asWinJsPromise(token => provider.commit(message, token));
}
$open(providerId: string, resourceGroupId: string, uri: string): TPromise<void> {
const provider = this._providers[providerId];
if (!provider) {
return TPromise.as(null);
}
const providerCache = this.cache[providerId];
const resourceGroup = providerCache[resourceGroupId];
const resource = resourceGroup && resourceGroup.resources[uri];
if (!resource) {
return TPromise.as(null);
}
return asWinJsPromise(token => provider.open(resource, token));
}
$drag(providerId: string, fromResourceGroupId: string, fromUri: string, toResourceGroupId: string): TPromise<void> {
const provider = this._providers[providerId];
if (!provider) {
return TPromise.as(null);
}
const providerCache = this.cache[providerId];
const fromResourceGroup = providerCache[fromResourceGroupId];
const resource = fromResourceGroup && fromResourceGroup.resources[fromUri];
const toResourceGroup = providerCache[toResourceGroupId];
const resourceGroup = toResourceGroup && toResourceGroup.resourceGroup;
if (!resource || !resourceGroup) {
return TPromise.as(null);
}
return asWinJsPromise(token => provider.drag(resource, resourceGroup, token));
}
$getOriginalResource(id: string, uri: URI): TPromise<URI> {
const provider = this._providers[id];
if (!provider) {
return TPromise.as(null);
}
return asWinJsPromise(token => provider.getOriginalResource(uri, token));
}
}

View File

@@ -5,7 +5,8 @@
'use strict';
import URI from 'vs/base/common/uri';
import { relative, isEqualOrParent } from 'vs/base/common/paths';
// import { relative, isEqualOrParent } from 'vs/base/common/paths';
import { relative } from 'path';
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
import { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
import { TPromise } from 'vs/base/common/winjs.base';
@@ -38,11 +39,16 @@ export class ExtHostWorkspace {
path = pathOrUri.fsPath;
}
if (isEqualOrParent(path, this._workspacePath)) {
return relative(this._workspacePath, path) || path;
if (!path || !this._workspacePath) {
return path;
}
return path;
let result = relative(this._workspacePath, path);
if (!result || result.indexOf('..') === 0) {
return path;
}
return result;
}
findFiles(include: string, exclude: string, maxResults?: number, token?: vscode.CancellationToken): Thenable<vscode.Uri[]> {

View File

@@ -0,0 +1,157 @@
/*---------------------------------------------------------------------------------------------
* 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 } 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, 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, SCMRawResourceGroup } from './extHost.protocol';
class MainThreadSCMProvider implements ISCMProvider {
private _resources: ISCMResourceGroup[] = [];
get resources(): ISCMResourceGroup[] { return this._resources; }
private _onDidChange = new Emitter<ISCMResourceGroup[]>();
get onDidChange(): Event<ISCMResourceGroup[]> { return this._onDidChange.event; }
private disposables: IDisposable[] = [];
get id(): string { return this._id; }
get label(): string { return this.features.label; }
constructor(
private _id: string,
private proxy: ExtHostSCMShape,
private features: SCMProviderFeatures,
@ISCMService scmService: ISCMService,
@ICommandService private commandService: ICommandService
) {
scmService.onDidChangeProvider(this.onDidChangeProvider, this, this.disposables);
this.disposables.push(scmService.registerSCMProvider(this));
}
commit(message: string): TPromise<void> {
if (!this.features.supportsCommit) {
return TPromise.as(null);
}
return this.proxy.$commit(this.id, message);
}
open(resource: ISCMResource): TPromise<void> {
if (!this.features.supportsOpen) {
return TPromise.as(null);
}
return this.proxy.$open(this.id, resource.resourceGroupId, resource.uri.toString());
}
drag(from: ISCMResource, to: ISCMResourceGroup): TPromise<void> {
if (!this.features.supportsDrag) {
return TPromise.as(null);
}
return this.proxy.$drag(this.id, from.resourceGroupId, from.uri.toString(), to.id);
}
getOriginalResource(uri: URI): TPromise<URI> {
if (!this.features.supportsOriginalResource) {
return TPromise.as(null);
}
return this.proxy.$getOriginalResource(this.id, uri);
}
private onDidChangeProvider(provider: ISCMProvider): void {
// if (provider === this) {
// return
// }
}
$onChange(rawResourceGroups: SCMRawResourceGroup[]): void {
this._resources = rawResourceGroups.map(rawGroup => {
const [id, label, rawResources] = rawGroup;
const resources = rawResources.map(rawResource => {
const [uri, icons, strikeThrough] = rawResource;
const icon = icons[0];
const iconDark = icons[1] || icon;
const decorations = {
icon: icon && URI.parse(icon),
iconDark: iconDark && URI.parse(iconDark),
strikeThrough
};
return {
resourceGroupId: id,
uri: URI.parse(uri),
decorations
};
});
return { id, label, resources };
});
this._onDidChange.fire(this.resources);
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
export class MainThreadSCM extends MainThreadSCMShape {
private proxy: ExtHostSCMShape;
private providers: { [id: string]: MainThreadSCMProvider; } = Object.create(null);
constructor(
@IThreadService threadService: IThreadService,
@IInstantiationService private instantiationService: IInstantiationService
) {
super();
this.proxy = threadService.get(ExtHostContext.ExtHostSCM);
}
$register(id: string, features: SCMProviderFeatures): void {
this.providers[id] = this.instantiationService.createInstance(MainThreadSCMProvider, id, this.proxy, features);
}
$unregister(id: string): void {
const provider = this.providers[id];
if (!provider) {
return;
}
provider.dispose();
delete this.providers[id];
}
$onChange(id: string, rawResourceGroups: SCMRawResourceGroup[]): void {
const provider = this.providers[id];
if (!provider) {
return;
}
provider.$onChange(rawResourceGroups);
}
dispose(): void {
Object.keys(this.providers)
.forEach(id => this.providers[id].dispose());
this.providers = Object.create(null);
}
}