SQL Operations Studio Public Preview 1 (0.23) release source code

This commit is contained in:
Karl Burtram
2017-11-09 14:30:27 -08:00
parent b88ecb8d93
commit 3cdac41339
8829 changed files with 759707 additions and 286 deletions

View File

@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService';
import { IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
export type IExtHostNamedCustomer<T extends IDisposable> = [ProxyIdentifier<T>, IExtHostCustomerCtor<T>];
export type IExtHostCustomerCtor<T extends IDisposable> = IConstructorSignature1<IExtHostContext, T>;
export function extHostNamedCustomer<T extends IDisposable>(id: ProxyIdentifier<T>) {
return function (ctor: IExtHostCustomerCtor<T>): void {
ExtHostCustomersRegistryImpl.INSTANCE.registerNamedCustomer(id, ctor);
};
}
export function extHostCustomer<T extends IDisposable>(ctor: IExtHostCustomerCtor<T>): void {
ExtHostCustomersRegistryImpl.INSTANCE.registerCustomer(ctor);
}
export namespace ExtHostCustomersRegistry {
export function getNamedCustomers(): IExtHostNamedCustomer<IDisposable>[] {
return ExtHostCustomersRegistryImpl.INSTANCE.getNamedCustomers();
}
export function getCustomers(): IExtHostCustomerCtor<IDisposable>[] {
return ExtHostCustomersRegistryImpl.INSTANCE.getCustomers();
}
}
class ExtHostCustomersRegistryImpl {
public static INSTANCE = new ExtHostCustomersRegistryImpl();
private _namedCustomers: IExtHostNamedCustomer<any>[];
private _customers: IExtHostCustomerCtor<any>[];
constructor() {
this._namedCustomers = [];
this._customers = [];
}
public registerNamedCustomer<T extends IDisposable>(id: ProxyIdentifier<T>, ctor: IExtHostCustomerCtor<T>): void {
const entry: IExtHostNamedCustomer<T> = [id, ctor];
this._namedCustomers.push(entry);
}
public getNamedCustomers(): IExtHostNamedCustomer<any>[] {
return this._namedCustomers;
}
public registerCustomer<T extends IDisposable>(ctor: IExtHostCustomerCtor<T>): void {
this._customers.push(ctor);
}
public getCustomers(): IExtHostCustomerCtor<any>[] {
return this._customers;
}
}

View File

@@ -0,0 +1,72 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
// --- other interested parties
import { JSONValidationExtensionPoint } from 'vs/platform/jsonschemas/common/jsonValidationExtensionPoint';
import { ColorExtensionPoint } from 'vs/platform/theme/common/colorExtensionPoint';
import { LanguageConfigurationFileHandler } from 'vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint';
// --- mainThread participants
import './mainThreadCommands';
import './mainThreadConfiguration';
import './mainThreadCredentials';
// {{SQL CARBON EDIT}}
// disable the debug service
// import './mainThreadDebugService';
import './mainThreadDiagnostics';
import './mainThreadDialogs';
import './mainThreadDocumentContentProviders';
import './mainThreadDocuments';
import './mainThreadDocumentsAndEditors';
import './mainThreadEditor';
import './mainThreadEditors';
import './mainThreadErrors';
import './mainThreadExtensionService';
import './mainThreadFileSystemEventService';
import './mainThreadHeapService';
import './mainThreadLanguageFeatures';
import './mainThreadLanguages';
import './mainThreadMessageService';
import './mainThreadOutputService';
import './mainThreadProgress';
import './mainThreadQuickOpen';
import './mainThreadSCM';
import './mainThreadSaveParticipant';
import './mainThreadStatusBar';
import './mainThreadStorage';
import './mainThreadTask';
import './mainThreadTelemetry';
import './mainThreadTerminalService';
import './mainThreadTreeViews';
import './mainThreadWindow';
import './mainThreadWorkspace';
export class ExtensionPoints implements IWorkbenchContribution {
constructor(
@IInstantiationService private instantiationService: IInstantiationService
) {
// Classes that handle extension points...
this.instantiationService.createInstance(JSONValidationExtensionPoint);
this.instantiationService.createInstance(ColorExtensionPoint);
this.instantiationService.createInstance(LanguageConfigurationFileHandler);
}
public getId(): string {
return 'vs.api.extensionPoints';
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(
ExtensionPoints
);

View File

@@ -0,0 +1,102 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ICommandService, CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { IDisposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostContext, MainThreadCommandsShape, ExtHostCommandsShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadCommands)
export class MainThreadCommands implements MainThreadCommandsShape {
private readonly _disposables = new Map<string, IDisposable>();
private readonly _generateCommandsDocumentationRegistration: IDisposable;
private readonly _proxy: ExtHostCommandsShape;
constructor(
extHostContext: IExtHostContext,
@ICommandService private readonly _commandService: ICommandService,
) {
if (extHostContext) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostCommands);
}
this._generateCommandsDocumentationRegistration = CommandsRegistry.registerCommand('_generateCommandsDocumentation', () => this._generateCommandsDocumentation());
}
dispose() {
this._disposables.forEach(value => value.dispose());
this._disposables.clear();
this._generateCommandsDocumentationRegistration.dispose();
}
private _generateCommandsDocumentation(): TPromise<void> {
return this._proxy.$getContributedCommandHandlerDescriptions().then(result => {
// add local commands
const commands = CommandsRegistry.getCommands();
for (let id in commands) {
let { description } = commands[id];
if (description) {
result[id] = description;
}
}
// print all as markdown
const all: string[] = [];
for (let id in result) {
all.push('`' + id + '` - ' + _generateMarkdown(result[id]));
}
console.log(all.join('\n'));
});
}
$registerCommand(id: string): TPromise<any> {
this._disposables.set(
id,
CommandsRegistry.registerCommand(id, (accessor, ...args) => this._proxy.$executeContributedCommand(id, ...args))
);
return undefined;
}
$unregisterCommand(id: string): TPromise<any> {
if (this._disposables.has(id)) {
this._disposables.get(id).dispose();
this._disposables.delete(id);
}
return undefined;
}
$executeCommand<T>(id: string, args: any[]): Thenable<T> {
return this._commandService.executeCommand<T>(id, ...args);
}
$getCommands(): Thenable<string[]> {
return TPromise.as(Object.keys(CommandsRegistry.getCommands()));
}
}
// --- command doc
function _generateMarkdown(description: string | ICommandHandlerDescription): string {
if (typeof description === 'string') {
return description;
} else {
let parts = [description.description];
parts.push('\n\n');
if (description.args) {
for (let arg of description.args) {
parts.push(`* _${arg.name}_ ${arg.description || ''}\n`);
}
}
if (description.returns) {
parts.push(`* _(returns)_ ${description.returns}`);
}
parts.push('\n\n');
return parts.join('');
}
}

View File

@@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. 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 { IDisposable } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadConfiguration)
export class MainThreadConfiguration implements MainThreadConfigurationShape {
private readonly _configurationListener: IDisposable;
constructor(
extHostContext: IExtHostContext,
@IConfigurationEditingService private readonly _configurationEditingService: IConfigurationEditingService,
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
@IWorkspaceConfigurationService configurationService: IWorkspaceConfigurationService
) {
const proxy = extHostContext.get(ExtHostContext.ExtHostConfiguration);
this._configurationListener = configurationService.onDidUpdateConfiguration(() => {
proxy.$acceptConfigurationChanged(configurationService.getConfigurationData());
});
}
public dispose(): void {
this._configurationListener.dispose();
}
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: URI): TPromise<void> {
return this.writeConfiguration(target, key, value, resource);
}
$removeConfigurationOption(target: ConfigurationTarget, key: string, resource: URI): TPromise<void> {
return this.writeConfiguration(target, key, undefined, resource);
}
private writeConfiguration(target: ConfigurationTarget, key: string, value: any, resource: URI): TPromise<void> {
target = target !== null && target !== undefined ? target : this.deriveConfigurationTarget(key, resource);
return this._configurationEditingService.writeConfiguration(target, { key, value }, { donotNotifyError: true, scopes: { resource } });
}
private deriveConfigurationTarget(key: string, resource: URI): ConfigurationTarget {
if (resource && this._workspaceContextService.hasMultiFolderWorkspace()) {
const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
if (configurationProperties[key] && configurationProperties[key].scope === ConfigurationScope.RESOURCE) {
return ConfigurationTarget.FOLDER;
}
}
return ConfigurationTarget.WORKSPACE;
}
}

View File

@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ExtHostContext, MainThreadCredentialsShape, ExtHostCredentialsShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadCredentials)
export class MainThreadCredentials implements MainThreadCredentialsShape {
private _proxy: ExtHostCredentialsShape;
constructor(
extHostContext: IExtHostContext,
@ICredentialsService private _credentialsService: ICredentialsService
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostCredentials);
}
public dispose(): void {
}
$readSecret(service: string, account: string): Thenable<string | undefined> {
return this._credentialsService.readSecret(service, account);
}
$writeSecret(service: string, account: string, secret: string): Thenable<void> {
return this._credentialsService.writeSecret(service, account, secret);
}
$deleteSecret(service: string, account: string): Thenable<boolean> {
return this._credentialsService.deleteSecret(service, account);
}
}

View File

@@ -0,0 +1,112 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
// {{SQL CARBON EDIT}}
/*
import URI from 'vs/base/common/uri';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDebugService, IConfig, IDebugConfigurationProvider } from 'vs/workbench/parts/debug/common/debug';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadDebugService)
export class MainThreadDebugService implements MainThreadDebugServiceShape {
private _proxy: ExtHostDebugServiceShape;
private _toDispose: IDisposable[];
constructor(
extHostContext: IExtHostContext,
@IDebugService private debugService: IDebugService
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostDebugService);
this._toDispose = [];
this._toDispose.push(debugService.onDidNewProcess(proc => this._proxy.$acceptDebugSessionStarted(<DebugSessionUUID>proc.getId(), proc.configuration.type, proc.getName(false))));
this._toDispose.push(debugService.onDidEndProcess(proc => this._proxy.$acceptDebugSessionTerminated(<DebugSessionUUID>proc.getId(), proc.configuration.type, proc.getName(false))));
this._toDispose.push(debugService.getViewModel().onDidFocusProcess(proc => {
if (proc) {
this._proxy.$acceptDebugSessionActiveChanged(<DebugSessionUUID>proc.getId(), proc.configuration.type, proc.getName(false));
} else {
this._proxy.$acceptDebugSessionActiveChanged(undefined);
}
}));
this._toDispose.push(debugService.onDidCustomEvent(event => {
if (event && event.sessionId) {
const process = this.debugService.findProcessByUUID(event.sessionId);
this._proxy.$acceptDebugSessionCustomEvent(event.sessionId, process.configuration.type, process.configuration.name, event);
}
}));
}
public dispose(): void {
this._toDispose = dispose(this._toDispose);
}
public $registerDebugConfigurationProvider(debugType: string, hasProvide: boolean, hasResolve: boolean, handle: number): TPromise<void> {
const provider = <IDebugConfigurationProvider>{
type: debugType
};
if (hasProvide) {
provider.provideDebugConfigurations = (folder: URI | undefined) => {
return this._proxy.$provideDebugConfigurations(handle, folder);
};
}
if (hasResolve) {
provider.resolveDebugConfiguration = (folder: URI | undefined, debugConfiguration: any) => {
return this._proxy.$resolveDebugConfiguration(handle, folder, debugConfiguration);
};
}
this.debugService.getConfigurationManager().registerDebugConfigurationProvider(handle, provider);
return TPromise.as<void>(undefined);
}
public $unregisterDebugConfigurationProvider(handle: number): TPromise<any> {
this.debugService.getConfigurationManager().unregisterDebugConfigurationProvider(handle);
return TPromise.as<void>(undefined);
}
public $startDebugging(folderUri: URI | undefined, nameOrConfiguration: string | IConfig): TPromise<boolean> {
return this.debugService.startDebugging(folderUri, nameOrConfiguration).then(x => {
return true;
}, err => {
return TPromise.wrapError(err && err.message ? err.message : 'cannot start debugging');
});
}
public $startDebugSession(folderUri: URI | undefined, configuration: IConfig): TPromise<DebugSessionUUID> {
if (configuration.request !== 'launch' && configuration.request !== 'attach') {
return TPromise.wrapError(new Error(`only 'launch' or 'attach' allowed for 'request' attribute`));
}
return this.debugService.createProcess(folderUri, configuration).then(process => {
if (process) {
return <DebugSessionUUID>process.getId();
}
return TPromise.wrapError<DebugSessionUUID>(new Error('cannot create debug session'));
}, err => {
return TPromise.wrapError(err && err.message ? err.message : 'cannot start debug session');
});
}
public $customDebugAdapterRequest(sessionId: DebugSessionUUID, request: string, args: any): TPromise<any> {
const process = this.debugService.findProcessByUUID(sessionId);
if (process) {
return process.session.custom(request, args).then(response => {
if (response.success) {
return response.body;
} else {
return TPromise.wrapError(new Error(response.message));
}
});
}
return TPromise.wrapError(new Error('debug session not found'));
}
}
// {{SQL CARBON EDIT}}
*/

View File

@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { MainThreadDiagnosticsShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadDiagnostics)
export class MainThreadDiagnostics implements MainThreadDiagnosticsShape {
private readonly _activeOwners = new Set<string>();
private readonly _markerService: IMarkerService;
constructor(
extHostContext: IExtHostContext,
@IMarkerService markerService: IMarkerService
) {
this._markerService = markerService;
}
dispose(): void {
this._activeOwners.forEach(owner => this._markerService.changeAll(owner, undefined));
}
$changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise<any> {
for (let entry of entries) {
let [uri, markers] = entry;
this._markerService.changeOne(owner, uri, markers);
}
this._activeOwners.add(owner);
return undefined;
}
$clear(owner: string): TPromise<any> {
this._markerService.changeAll(owner, undefined);
this._activeOwners.delete(owner);
return undefined;
}
}

View File

@@ -0,0 +1,63 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { MainThreadDiaglogsShape, MainContext, IExtHostContext, MainThreadDialogOptions } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IWindowService } from 'vs/platform/windows/common/windows';
@extHostNamedCustomer(MainContext.MainThreadDialogs)
export class MainThreadDialogs implements MainThreadDiaglogsShape {
constructor(
context: IExtHostContext,
@IWindowService private readonly _windowService: IWindowService
) {
//
}
dispose(): void {
//
}
$showOpenDialog(options: MainThreadDialogOptions): TPromise<string[]> {
// TODO@joh what about remote dev setup?
if (options.uri && options.uri.scheme !== 'file') {
return TPromise.wrapError(new Error('bad path'));
}
return new TPromise<string[]>(resolve => {
this._windowService.showOpenDialog(MainThreadDialogs._convertOptions(options), filenames => {
resolve(isFalsyOrEmpty(filenames) ? undefined : filenames);
});
});
}
private static _convertOptions(options: MainThreadDialogOptions): Electron.OpenDialogOptions {
const result: Electron.OpenDialogOptions = {
properties: ['createDirectory']
};
if (options.openLabel) {
result.buttonLabel = options.openLabel;
}
if (options.uri) {
result.defaultPath = options.uri.fsPath;
}
if (!options.openFiles && !options.openFolders) {
options.openFiles = true;
}
if (options.openFiles) {
result.properties.push('openFile');
}
if (options.openFolders) {
result.properties.push('openDirectory');
}
if (options.openMany) {
result.properties.push('multiSelections');
}
return result;
}
}

View File

@@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { IModel } from 'vs/editor/common/editorCommon';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { MainThreadDocumentContentProvidersShape, ExtHostContext, ExtHostDocumentContentProvidersShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { ITextSource } from 'vs/editor/common/model/textSource';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadDocumentContentProviders)
export class MainThreadDocumentContentProviders implements MainThreadDocumentContentProvidersShape {
private _resourceContentProvider: { [handle: number]: IDisposable } = Object.create(null);
private readonly _proxy: ExtHostDocumentContentProvidersShape;
constructor(
extHostContext: IExtHostContext,
@ITextModelService private readonly _textModelResolverService: ITextModelService,
@IModeService private readonly _modeService: IModeService,
@IModelService private readonly _modelService: IModelService,
@ICodeEditorService codeEditorService: ICodeEditorService,
@IEditorGroupService editorGroupService: IEditorGroupService
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostDocumentContentProviders);
}
public dispose(): void {
for (let handle in this._resourceContentProvider) {
this._resourceContentProvider[handle].dispose();
}
}
$registerTextContentProvider(handle: number, scheme: string): void {
this._resourceContentProvider[handle] = this._textModelResolverService.registerTextModelContentProvider(scheme, {
provideTextContent: (uri: URI): TPromise<IModel> => {
return this._proxy.$provideTextDocumentContent(handle, uri).then(value => {
if (typeof value === 'string') {
const firstLineText = value.substr(0, 1 + value.search(/\r?\n/));
const mode = this._modeService.getOrCreateModeByFilenameOrFirstLine(uri.fsPath, firstLineText);
return this._modelService.createModel(value, mode, uri);
}
return undefined;
});
}
});
}
$unregisterTextContentProvider(handle: number): void {
const registration = this._resourceContentProvider[handle];
if (registration) {
registration.dispose();
delete this._resourceContentProvider[handle];
}
}
$onVirtualDocumentChange(uri: URI, value: ITextSource): void {
const model = this._modelService.getModel(uri);
if (!model) {
return;
}
const raw: ITextSource = {
lines: value.lines,
length: value.length,
BOM: value.BOM,
EOL: value.EOL,
containsRTL: value.containsRTL,
isBasicASCII: value.isBasicASCII,
};
if (!model.equals(raw)) {
model.setValueFromTextSource(raw);
}
}
}

View File

@@ -0,0 +1,237 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IDisposable, dispose, IReference } from 'vs/base/common/lifecycle';
import { TextFileModelChangeEvent, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { TPromise } from 'vs/base/common/winjs.base';
import { IFileService } from 'vs/platform/files/common/files';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { ExtHostContext, MainThreadDocumentsShape, ExtHostDocumentsShape, IExtHostContext } from '../node/extHost.protocol';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ITextEditorModel } from 'vs/workbench/common/editor';
export class BoundModelReferenceCollection {
private _data = new Array<{ length: number, dispose(): void }>();
private _length = 0;
constructor(
private _maxAge: number = 1000 * 60 * 3,
private _maxLength: number = 1024 * 1024 * 80
) {
//
}
dispose(): void {
this._data = dispose(this._data);
}
add(ref: IReference<ITextEditorModel>): void {
let length = ref.object.textEditorModel.getValueLength();
let handle: number;
let entry: { length: number, dispose(): void };
const dispose = () => {
let idx = this._data.indexOf(entry);
if (idx >= 0) {
this._length -= length;
ref.dispose();
clearTimeout(handle);
this._data.splice(idx, 1);
}
};
handle = setTimeout(dispose, this._maxAge);
entry = { length, dispose };
this._data.push(entry);
this._length += length;
this._cleanup();
}
private _cleanup(): void {
while (this._length > this._maxLength) {
this._data[0].dispose();
}
}
}
export class MainThreadDocuments implements MainThreadDocumentsShape {
private _modelService: IModelService;
private _modeService: IModeService;
private _textModelResolverService: ITextModelService;
private _textFileService: ITextFileService;
private _fileService: IFileService;
private _untitledEditorService: IUntitledEditorService;
private _toDispose: IDisposable[];
private _modelToDisposeMap: { [modelUrl: string]: IDisposable; };
private _proxy: ExtHostDocumentsShape;
private _modelIsSynced: { [modelId: string]: boolean; };
private _modelReferenceCollection = new BoundModelReferenceCollection();
constructor(
documentsAndEditors: MainThreadDocumentsAndEditors,
extHostContext: IExtHostContext,
@IModelService modelService: IModelService,
@IModeService modeService: IModeService,
@ITextFileService textFileService: ITextFileService,
@IFileService fileService: IFileService,
@ITextModelService textModelResolverService: ITextModelService,
@IUntitledEditorService untitledEditorService: IUntitledEditorService,
) {
this._modelService = modelService;
this._modeService = modeService;
this._textModelResolverService = textModelResolverService;
this._textFileService = textFileService;
this._fileService = fileService;
this._untitledEditorService = untitledEditorService;
this._proxy = extHostContext.get(ExtHostContext.ExtHostDocuments);
this._modelIsSynced = {};
this._toDispose = [];
this._toDispose.push(documentsAndEditors.onDocumentAdd(models => models.forEach(this._onModelAdded, this)));
this._toDispose.push(documentsAndEditors.onDocumentRemove(urls => urls.forEach(this._onModelRemoved, this)));
this._toDispose.push(this._modelReferenceCollection);
this._toDispose.push(modelService.onModelModeChanged(this._onModelModeChanged, this));
this._toDispose.push(textFileService.models.onModelSaved(e => {
if (this._shouldHandleFileEvent(e)) {
this._proxy.$acceptModelSaved(e.resource.toString());
}
}));
this._toDispose.push(textFileService.models.onModelReverted(e => {
if (this._shouldHandleFileEvent(e)) {
this._proxy.$acceptDirtyStateChanged(e.resource.toString(), false);
}
}));
this._toDispose.push(textFileService.models.onModelDirty(e => {
if (this._shouldHandleFileEvent(e)) {
this._proxy.$acceptDirtyStateChanged(e.resource.toString(), true);
}
}));
this._modelToDisposeMap = Object.create(null);
}
public dispose(): void {
Object.keys(this._modelToDisposeMap).forEach((modelUrl) => {
this._modelToDisposeMap[modelUrl].dispose();
});
this._modelToDisposeMap = Object.create(null);
this._toDispose = dispose(this._toDispose);
}
private _shouldHandleFileEvent(e: TextFileModelChangeEvent): boolean {
const model = this._modelService.getModel(e.resource);
return model && !model.isTooLargeForHavingARichMode();
}
private _onModelAdded(model: editorCommon.IModel): void {
// Same filter as in mainThreadEditorsTracker
if (model.isTooLargeForHavingARichMode()) {
// don't synchronize too large models
return null;
}
let modelUrl = model.uri;
this._modelIsSynced[modelUrl.toString()] = true;
this._modelToDisposeMap[modelUrl.toString()] = model.onDidChangeContent((e) => {
this._proxy.$acceptModelChanged(modelUrl.toString(), e, this._textFileService.isDirty(modelUrl));
});
}
private _onModelModeChanged(event: { model: editorCommon.IModel; oldModeId: string; }): void {
let { model, oldModeId } = event;
let modelUrl = model.uri;
if (!this._modelIsSynced[modelUrl.toString()]) {
return;
}
this._proxy.$acceptModelModeChanged(model.uri.toString(), oldModeId, model.getLanguageIdentifier().language);
}
private _onModelRemoved(modelUrl: string): void {
if (!this._modelIsSynced[modelUrl]) {
return;
}
delete this._modelIsSynced[modelUrl];
this._modelToDisposeMap[modelUrl].dispose();
delete this._modelToDisposeMap[modelUrl];
}
// --- from extension host process
$trySaveDocument(uri: URI): TPromise<boolean> {
return this._textFileService.save(uri);
}
$tryOpenDocument(uri: URI): TPromise<any> {
if (!uri.scheme || !(uri.fsPath || uri.authority)) {
return TPromise.wrapError(new Error(`Invalid uri. Scheme and authority or path must be set.`));
}
let promise: TPromise<boolean>;
switch (uri.scheme) {
case 'untitled':
promise = this._handleUnititledScheme(uri);
break;
case 'file':
default:
promise = this._handleAsResourceInput(uri);
break;
}
return promise.then(success => {
if (!success) {
return TPromise.wrapError(new Error('cannot open ' + uri.toString()));
}
return undefined;
}, err => {
return TPromise.wrapError(new Error('cannot open ' + uri.toString() + '. Detail: ' + toErrorMessage(err)));
});
}
$tryCreateDocument(options?: { language?: string, content?: string }): TPromise<URI> {
return this._doCreateUntitled(void 0, options ? options.language : void 0, options ? options.content : void 0);
}
private _handleAsResourceInput(uri: URI): TPromise<boolean> {
return this._textModelResolverService.createModelReference(uri).then(ref => {
this._modelReferenceCollection.add(ref);
const result = !!ref.object;
return result;
});
}
private _handleUnititledScheme(uri: URI): TPromise<boolean> {
let asFileUri = uri.with({ scheme: 'file' });
return this._fileService.resolveFile(asFileUri).then(stats => {
// don't create a new file ontop of an existing file
return TPromise.wrapError<boolean>(new Error('file already exists on disk'));
}, err => this._doCreateUntitled(asFileUri).then(resource => !!resource));
}
private _doCreateUntitled(resource?: URI, modeId?: string, initialValue?: string): TPromise<URI> {
return this._untitledEditorService.loadOrCreate({ resource, modeId, initialValue }).then(model => {
const resource = model.getResource();
if (!this._modelIsSynced[resource.toString()]) {
throw new Error(`expected URI ${resource.toString()} to have come to LIFE`);
}
this._proxy.$acceptDirtyStateChanged(resource.toString(), true); // mark as dirty
return resource;
});
}
}

View File

@@ -0,0 +1,378 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModel, ICommonCodeEditor, isCommonCodeEditor, isCommonDiffEditor } from 'vs/editor/common/editorCommon';
import { compare } from 'vs/base/common/strings';
import { delta } from 'vs/base/common/arrays';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import Event, { Emitter } from 'vs/base/common/event';
import { ExtHostContext, ExtHostDocumentsAndEditorsShape, IModelAddedData, ITextEditorAddData, IDocumentsAndEditorsDelta, IExtHostContext, MainContext } from '../node/extHost.protocol';
import { MainThreadTextEditor } from './mainThreadEditor';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { Position as EditorPosition, IEditor } from 'vs/platform/editor/common/editor';
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { MainThreadDocuments } from 'vs/workbench/api/electron-browser/mainThreadDocuments';
import { MainThreadEditors } from 'vs/workbench/api/electron-browser/mainThreadEditors';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IFileService } from 'vs/platform/files/common/files';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
namespace cmp {
export function compareModels(a: IModel, b: IModel): number {
return compare(a.uri.toString(), b.uri.toString());
}
export function compareEditors(a: EditorAndModel, b: EditorAndModel): number {
let ret = compare(a.editor.getId(), b.editor.getId());
if (ret === 0) {
ret = compare(a.document.uri.toString(), b.document.uri.toString());
}
return ret;
}
}
class EditorAndModel {
readonly id: string;
constructor(
readonly editor: ICommonCodeEditor,
readonly document: IModel,
) {
this.id = `${editor.getId()},${document.uri.toString()}`;
}
}
class DocumentAndEditorStateDelta {
readonly isEmpty: boolean;
constructor(
readonly removedDocuments: IModel[],
readonly addedDocuments: IModel[],
readonly removedEditors: EditorAndModel[],
readonly addedEditors: EditorAndModel[],
readonly oldActiveEditor: string,
readonly newActiveEditor: string,
) {
this.isEmpty = this.removedDocuments.length === 0
&& this.addedDocuments.length === 0
&& this.removedEditors.length === 0
&& this.addedEditors.length === 0
&& oldActiveEditor === newActiveEditor;
}
toString(): string {
let ret = 'DocumentAndEditorStateDelta\n';
ret += `\tRemoved Documents: [${this.removedDocuments.map(d => d.uri.toString(true)).join(', ')}]\n`;
ret += `\tAdded Documents: [${this.addedDocuments.map(d => d.uri.toString(true)).join(', ')}]\n`;
ret += `\tRemoved Editors: [${this.removedEditors.map(e => e.id).join(', ')}]\n`;
ret += `\tAdded Editors: [${this.addedEditors.map(e => e.id).join(', ')}]\n`;
ret += `\tNew Active Editor: ${this.newActiveEditor}\n`;
return ret;
}
}
class DocumentAndEditorState {
static compute(before: DocumentAndEditorState, after: DocumentAndEditorState): DocumentAndEditorStateDelta {
if (!before) {
return new DocumentAndEditorStateDelta([], after.documents, [], after.editors, undefined, after.activeEditor);
}
const documentDelta = delta(before.documents, after.documents, cmp.compareModels);
const editorDelta = delta(before.editors, after.editors, cmp.compareEditors);
const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined;
const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;
return new DocumentAndEditorStateDelta(
documentDelta.removed, documentDelta.added,
editorDelta.removed, editorDelta.added,
oldActiveEditor, newActiveEditor
);
}
constructor(
readonly documents: IModel[],
readonly editors: EditorAndModel[],
readonly activeEditor: string,
) {
this.documents = documents.sort(cmp.compareModels);
this.editors = editors.sort(cmp.compareEditors);
}
}
class MainThreadDocumentAndEditorStateComputer {
private _toDispose: IDisposable[] = [];
private _toDisposeOnEditorRemove = new Map<string, IDisposable>();
private _currentState: DocumentAndEditorState;
constructor(
private readonly _onDidChangeState: (delta: DocumentAndEditorStateDelta) => void,
@IModelService private _modelService: IModelService,
@ICodeEditorService private _codeEditorService: ICodeEditorService,
@IWorkbenchEditorService private _workbenchEditorService: IWorkbenchEditorService
) {
this._modelService.onModelAdded(this._updateState, this, this._toDispose);
this._modelService.onModelRemoved(this._updateState, this, this._toDispose);
this._codeEditorService.onCodeEditorAdd(this._onDidAddEditor, this, this._toDispose);
this._codeEditorService.onCodeEditorRemove(this._onDidRemoveEditor, this, this._toDispose);
this._codeEditorService.listCodeEditors().forEach(this._onDidAddEditor, this);
this._updateState();
}
dispose(): void {
this._toDispose = dispose(this._toDispose);
}
private _onDidAddEditor(e: ICommonCodeEditor): void {
this._toDisposeOnEditorRemove.set(e.getId(), e.onDidChangeModel(() => this._updateState()));
this._toDisposeOnEditorRemove.set(e.getId(), e.onDidFocusEditor(() => this._updateState()));
this._toDisposeOnEditorRemove.set(e.getId(), e.onDidBlurEditor(() => this._updateState()));
this._updateState();
}
private _onDidRemoveEditor(e: ICommonCodeEditor): void {
const sub = this._toDisposeOnEditorRemove.get(e.getId());
if (sub) {
this._toDisposeOnEditorRemove.delete(e.getId());
sub.dispose();
this._updateState();
}
}
private _updateState(): void {
// models: ignore too large models
const models = this._modelService.getModels();
for (let i = 0; i < models.length; i++) {
if (models[i].isTooLargeForHavingARichMode()) {
models.splice(i, 1);
i--;
}
}
// editor: only take those that have a not too large model
const editors: EditorAndModel[] = [];
let activeEditor: string = null;
for (const editor of this._codeEditorService.listCodeEditors()) {
const model = editor.getModel();
if (model && !model.isTooLargeForHavingARichMode()
&& !model.isDisposed() // model disposed
&& Boolean(this._modelService.getModel(model.uri)) // model disposing, the flag didn't flip yet but the model service already removed it
) {
const apiEditor = new EditorAndModel(editor, model);
editors.push(apiEditor);
if (editor.isFocused()) {
activeEditor = apiEditor.id;
}
}
}
// active editor: if none of the previous editors had focus we try
// to match the action workbench editor with one of editor we have
// just computed
if (!activeEditor) {
const workbenchEditor = this._workbenchEditorService.getActiveEditor();
if (workbenchEditor) {
const workbenchEditorControl = workbenchEditor.getControl();
let candidate: ICommonCodeEditor;
if (isCommonCodeEditor(workbenchEditorControl)) {
candidate = workbenchEditorControl;
} else if (isCommonDiffEditor(workbenchEditorControl)) {
candidate = workbenchEditorControl.getModifiedEditor();
}
if (candidate) {
for (const { editor, id } of editors) {
if (candidate === editor) {
activeEditor = id;
break;
}
}
}
}
}
// compute new state and compare against old
const newState = new DocumentAndEditorState(models, editors, activeEditor);
const delta = DocumentAndEditorState.compute(this._currentState, newState);
if (!delta.isEmpty) {
this._currentState = newState;
this._onDidChangeState(delta);
}
}
}
@extHostCustomer
export class MainThreadDocumentsAndEditors {
private _toDispose: IDisposable[];
private _proxy: ExtHostDocumentsAndEditorsShape;
private _stateComputer: MainThreadDocumentAndEditorStateComputer;
private _editors = <{ [id: string]: MainThreadTextEditor }>Object.create(null);
private _onTextEditorAdd = new Emitter<MainThreadTextEditor[]>();
private _onTextEditorRemove = new Emitter<string[]>();
private _onDocumentAdd = new Emitter<IModel[]>();
private _onDocumentRemove = new Emitter<string[]>();
readonly onTextEditorAdd: Event<MainThreadTextEditor[]> = this._onTextEditorAdd.event;
readonly onTextEditorRemove: Event<string[]> = this._onTextEditorRemove.event;
readonly onDocumentAdd: Event<IModel[]> = this._onDocumentAdd.event;
readonly onDocumentRemove: Event<string[]> = this._onDocumentRemove.event;
constructor(
extHostContext: IExtHostContext,
@IModelService private _modelService: IModelService,
@ITextFileService private _textFileService: ITextFileService,
@IWorkbenchEditorService private _workbenchEditorService: IWorkbenchEditorService,
@ICodeEditorService codeEditorService: ICodeEditorService,
@IModeService modeService: IModeService,
@IFileService fileService: IFileService,
@ITextModelService textModelResolverService: ITextModelService,
@IUntitledEditorService untitledEditorService: IUntitledEditorService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@ITelemetryService telemetryService: ITelemetryService
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostDocumentsAndEditors);
const mainThreadDocuments = new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService);
extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments);
const mainThreadEditors = new MainThreadEditors(this, extHostContext, codeEditorService, this._workbenchEditorService, editorGroupService, telemetryService);
extHostContext.set(MainContext.MainThreadEditors, mainThreadEditors);
// It is expected that the ctor of the state computer calls our `_onDelta`.
this._stateComputer = new MainThreadDocumentAndEditorStateComputer(delta => this._onDelta(delta), _modelService, codeEditorService, _workbenchEditorService);
this._toDispose = [
mainThreadDocuments,
mainThreadEditors,
this._stateComputer,
this._onTextEditorAdd,
this._onTextEditorRemove,
this._onDocumentAdd,
this._onDocumentRemove,
];
}
dispose(): void {
this._toDispose = dispose(this._toDispose);
}
private _onDelta(delta: DocumentAndEditorStateDelta): void {
let removedDocuments: string[];
let removedEditors: string[] = [];
let addedEditors: MainThreadTextEditor[] = [];
// removed models
removedDocuments = delta.removedDocuments.map(m => m.uri.toString());
// added editors
for (const apiEditor of delta.addedEditors) {
const mainThreadEditor = new MainThreadTextEditor(apiEditor.id, apiEditor.document,
apiEditor.editor, { onGainedFocus() { }, onLostFocus() { } }, this._modelService);
this._editors[apiEditor.id] = mainThreadEditor;
addedEditors.push(mainThreadEditor);
}
// removed editors
for (const { id } of delta.removedEditors) {
const mainThreadEditor = this._editors[id];
if (mainThreadEditor) {
mainThreadEditor.dispose();
delete this._editors[id];
removedEditors.push(id);
}
}
let extHostDelta: IDocumentsAndEditorsDelta = Object.create(null);
let empty = true;
if (delta.newActiveEditor !== undefined) {
empty = false;
extHostDelta.newActiveEditor = delta.newActiveEditor;
}
if (removedDocuments.length > 0) {
empty = false;
extHostDelta.removedDocuments = removedDocuments;
}
if (removedEditors.length > 0) {
empty = false;
extHostDelta.removedEditors = removedEditors;
}
if (delta.addedDocuments.length > 0) {
empty = false;
extHostDelta.addedDocuments = delta.addedDocuments.map(m => this._toModelAddData(m));
}
if (delta.addedEditors.length > 0) {
empty = false;
extHostDelta.addedEditors = addedEditors.map(e => this._toTextEditorAddData(e));
}
if (!empty) {
// first update ext host
this._proxy.$acceptDocumentsAndEditorsDelta(extHostDelta);
// second update dependent state listener
this._onDocumentRemove.fire(removedDocuments);
this._onDocumentAdd.fire(delta.addedDocuments);
this._onTextEditorRemove.fire(removedEditors);
this._onTextEditorAdd.fire(addedEditors);
}
}
private _toModelAddData(model: IModel): IModelAddedData {
return {
url: model.uri,
versionId: model.getVersionId(),
lines: model.getLinesContent(),
EOL: model.getEOL(),
modeId: model.getLanguageIdentifier().language,
isDirty: this._textFileService.isDirty(model.uri)
};
}
private _toTextEditorAddData(textEditor: MainThreadTextEditor): ITextEditorAddData {
return {
id: textEditor.getId(),
document: textEditor.getModel().uri,
options: textEditor.getConfiguration(),
selections: textEditor.getSelections(),
editorPosition: this._findEditorPosition(textEditor)
};
}
private _findEditorPosition(editor: MainThreadTextEditor): EditorPosition {
for (let workbenchEditor of this._workbenchEditorService.getVisibleEditors()) {
if (editor.matches(workbenchEditor)) {
return workbenchEditor.position;
}
}
return undefined;
}
findTextEditorIdFor(editor: IEditor): string {
for (let id in this._editors) {
if (this._editors[id].matches(editor)) {
return id;
}
}
return undefined;
}
getEditor(id: string): MainThreadTextEditor {
return this._editors[id];
}
}

View File

@@ -0,0 +1,379 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import EditorCommon = require('vs/editor/common/editorCommon');
import Event, { Emitter } from 'vs/base/common/event';
import { IEditor } from 'vs/platform/editor/common/editor';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Range, IRange } from 'vs/editor/common/core/range';
import { Selection, ISelection } from 'vs/editor/common/core/selection';
import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2';
import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
import { TextEditorCursorStyle, cursorStyleToString } from 'vs/editor/common/config/editorOptions';
import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { IResolvedTextEditorConfiguration, ISelectionChangeEvent, ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions } from 'vs/workbench/api/node/extHost.protocol';
function configurationsEqual(a: IResolvedTextEditorConfiguration, b: IResolvedTextEditorConfiguration) {
if (a && !b || !a && b) {
return false;
}
if (!a && !b) {
return true;
}
return (
a.tabSize === b.tabSize
&& a.insertSpaces === b.insertSpaces
);
}
export interface IFocusTracker {
onGainedFocus(): void;
onLostFocus(): void;
}
/**
* Text Editor that is permanently bound to the same model.
* It can be bound or not to a CodeEditor.
*/
export class MainThreadTextEditor {
private _id: string;
private _model: EditorCommon.IModel;
private _modelService: IModelService;
private _modelListeners: IDisposable[];
private _codeEditor: EditorCommon.ICommonCodeEditor;
private _focusTracker: IFocusTracker;
private _codeEditorListeners: IDisposable[];
private _lastSelection: Selection[];
private _configuration: IResolvedTextEditorConfiguration;
private _onSelectionChanged: Emitter<ISelectionChangeEvent>;
private _onConfigurationChanged: Emitter<IResolvedTextEditorConfiguration>;
constructor(
id: string,
model: EditorCommon.IModel,
codeEditor: EditorCommon.ICommonCodeEditor,
focusTracker: IFocusTracker,
modelService: IModelService
) {
this._id = id;
this._model = model;
this._codeEditor = null;
this._focusTracker = focusTracker;
this._modelService = modelService;
this._codeEditorListeners = [];
this._onSelectionChanged = new Emitter<ISelectionChangeEvent>();
this._onConfigurationChanged = new Emitter<IResolvedTextEditorConfiguration>();
this._lastSelection = [new Selection(1, 1, 1, 1)];
this._modelListeners = [];
this._modelListeners.push(this._model.onDidChangeOptions((e) => {
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
}));
this.setCodeEditor(codeEditor);
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
}
public dispose(): void {
this._model = null;
this._modelListeners = dispose(this._modelListeners);
this._codeEditor = null;
this._codeEditorListeners = dispose(this._codeEditorListeners);
}
public getId(): string {
return this._id;
}
public getModel(): EditorCommon.IModel {
return this._model;
}
public getCodeEditor(): EditorCommon.ICommonCodeEditor {
return this._codeEditor;
}
public hasCodeEditor(codeEditor: EditorCommon.ICommonCodeEditor): boolean {
return (this._codeEditor === codeEditor);
}
public setCodeEditor(codeEditor: EditorCommon.ICommonCodeEditor): void {
if (this.hasCodeEditor(codeEditor)) {
// Nothing to do...
return;
}
this._codeEditorListeners = dispose(this._codeEditorListeners);
this._codeEditor = codeEditor;
if (this._codeEditor) {
// Catch early the case that this code editor gets a different model set and disassociate from this model
this._codeEditorListeners.push(this._codeEditor.onDidChangeModel(() => {
this.setCodeEditor(null);
}));
let forwardSelection = (event?: ICursorSelectionChangedEvent) => {
this._lastSelection = this._codeEditor.getSelections();
this._onSelectionChanged.fire({
selections: this._lastSelection,
source: event && event.source
});
};
this._codeEditorListeners.push(this._codeEditor.onDidChangeCursorSelection(forwardSelection));
if (!Selection.selectionsArrEqual(this._lastSelection, this._codeEditor.getSelections())) {
forwardSelection();
}
this._codeEditorListeners.push(this._codeEditor.onDidFocusEditor(() => {
this._focusTracker.onGainedFocus();
}));
this._codeEditorListeners.push(this._codeEditor.onDidBlurEditor(() => {
this._focusTracker.onLostFocus();
}));
this._codeEditorListeners.push(this._codeEditor.onDidChangeConfiguration(() => {
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
}));
this._setConfiguration(this._readConfiguration(this._model, this._codeEditor));
}
}
public isVisible(): boolean {
return !!this._codeEditor;
}
public get onSelectionChanged(): Event<ISelectionChangeEvent> {
return this._onSelectionChanged.event;
}
public get onConfigurationChanged(): Event<IResolvedTextEditorConfiguration> {
return this._onConfigurationChanged.event;
}
public getSelections(): Selection[] {
if (this._codeEditor) {
return this._codeEditor.getSelections();
}
return this._lastSelection;
}
public setSelections(selections: ISelection[]): void {
if (this._codeEditor) {
this._codeEditor.setSelections(selections);
return;
}
this._lastSelection = selections.map(Selection.liftSelection);
}
public getConfiguration(): IResolvedTextEditorConfiguration {
return this._configuration;
}
private _setIndentConfiguration(newConfiguration: ITextEditorConfigurationUpdate): void {
if (newConfiguration.tabSize === 'auto' || newConfiguration.insertSpaces === 'auto') {
// one of the options was set to 'auto' => detect indentation
let creationOpts = this._modelService.getCreationOptions(this._model.getLanguageIdentifier().language, this._model.uri);
let insertSpaces = creationOpts.insertSpaces;
let tabSize = creationOpts.tabSize;
if (newConfiguration.insertSpaces !== 'auto' && typeof newConfiguration.insertSpaces !== 'undefined') {
insertSpaces = newConfiguration.insertSpaces;
}
if (newConfiguration.tabSize !== 'auto' && typeof newConfiguration.tabSize !== 'undefined') {
tabSize = newConfiguration.tabSize;
}
this._model.detectIndentation(insertSpaces, tabSize);
return;
}
let newOpts: EditorCommon.ITextModelUpdateOptions = {};
if (typeof newConfiguration.insertSpaces !== 'undefined') {
newOpts.insertSpaces = newConfiguration.insertSpaces;
}
if (typeof newConfiguration.tabSize !== 'undefined') {
newOpts.tabSize = newConfiguration.tabSize;
}
this._model.updateOptions(newOpts);
}
public setConfiguration(newConfiguration: ITextEditorConfigurationUpdate): void {
this._setIndentConfiguration(newConfiguration);
if (!this._codeEditor) {
return;
}
if (newConfiguration.cursorStyle) {
let newCursorStyle = cursorStyleToString(newConfiguration.cursorStyle);
this._codeEditor.updateOptions({
cursorStyle: newCursorStyle
});
}
if (typeof newConfiguration.lineNumbers !== 'undefined') {
let lineNumbers: 'on' | 'off' | 'relative';
switch (newConfiguration.lineNumbers) {
case TextEditorLineNumbersStyle.On:
lineNumbers = 'on';
break;
case TextEditorLineNumbersStyle.Relative:
lineNumbers = 'relative';
break;
default:
lineNumbers = 'off';
}
this._codeEditor.updateOptions({
lineNumbers: lineNumbers
});
}
}
public setDecorations(key: string, ranges: EditorCommon.IDecorationOptions[]): void {
if (!this._codeEditor) {
return;
}
this._codeEditor.setDecorations(key, ranges);
}
public revealRange(range: IRange, revealType: TextEditorRevealType): void {
if (!this._codeEditor) {
return;
}
switch (revealType) {
case TextEditorRevealType.Default:
this._codeEditor.revealRange(range, EditorCommon.ScrollType.Smooth);
break;
case TextEditorRevealType.InCenter:
this._codeEditor.revealRangeInCenter(range, EditorCommon.ScrollType.Smooth);
break;
case TextEditorRevealType.InCenterIfOutsideViewport:
this._codeEditor.revealRangeInCenterIfOutsideViewport(range, EditorCommon.ScrollType.Smooth);
break;
case TextEditorRevealType.AtTop:
this._codeEditor.revealRangeAtTop(range, EditorCommon.ScrollType.Smooth);
break;
default:
console.warn(`Unknown revealType: ${revealType}`);
break;
}
}
private _readConfiguration(model: EditorCommon.IModel, codeEditor: EditorCommon.ICommonCodeEditor): IResolvedTextEditorConfiguration {
if (model.isDisposed()) {
// shutdown time
return this._configuration;
}
let cursorStyle = this._configuration ? this._configuration.cursorStyle : TextEditorCursorStyle.Line;
let lineNumbers: TextEditorLineNumbersStyle = this._configuration ? this._configuration.lineNumbers : TextEditorLineNumbersStyle.On;
if (codeEditor) {
let codeEditorOpts = codeEditor.getConfiguration();
cursorStyle = codeEditorOpts.viewInfo.cursorStyle;
if (codeEditorOpts.viewInfo.renderRelativeLineNumbers) {
lineNumbers = TextEditorLineNumbersStyle.Relative;
} else if (codeEditorOpts.viewInfo.renderLineNumbers) {
lineNumbers = TextEditorLineNumbersStyle.On;
} else {
lineNumbers = TextEditorLineNumbersStyle.Off;
}
}
let indent = model.getOptions();
return {
insertSpaces: indent.insertSpaces,
tabSize: indent.tabSize,
cursorStyle: cursorStyle,
lineNumbers: lineNumbers
};
}
private _setConfiguration(newConfiguration: IResolvedTextEditorConfiguration): void {
if (configurationsEqual(this._configuration, newConfiguration)) {
return;
}
this._configuration = newConfiguration;
this._onConfigurationChanged.fire(this._configuration);
}
public isFocused(): boolean {
if (this._codeEditor) {
return this._codeEditor.isFocused();
}
return false;
}
public matches(editor: IEditor): boolean {
if (!editor) {
return false;
}
return editor.getControl() === this._codeEditor;
}
public applyEdits(versionIdCheck: number, edits: EditorCommon.ISingleEditOperation[], opts: IApplyEditsOptions): boolean {
if (this._model.getVersionId() !== versionIdCheck) {
// throw new Error('Model has changed in the meantime!');
// model changed in the meantime
return false;
}
if (!this._codeEditor) {
// console.warn('applyEdits on invisible editor');
return false;
}
if (opts.setEndOfLine === EndOfLine.CRLF) {
this._model.setEOL(EditorCommon.EndOfLineSequence.CRLF);
} else if (opts.setEndOfLine === EndOfLine.LF) {
this._model.setEOL(EditorCommon.EndOfLineSequence.LF);
}
let transformedEdits = edits.map((edit): EditorCommon.IIdentifiedSingleEditOperation => {
return {
identifier: null,
range: Range.lift(edit.range),
text: edit.text,
forceMoveMarkers: edit.forceMoveMarkers
};
});
if (opts.undoStopBefore) {
this._codeEditor.pushUndoStop();
}
this._codeEditor.executeEdits('MainThreadTextEditor', transformedEdits);
if (opts.undoStopAfter) {
this._codeEditor.pushUndoStop();
}
return true;
}
insertSnippet(template: string, ranges: IRange[], opts: IUndoStopOptions) {
if (!this._codeEditor) {
return false;
}
const snippetController = SnippetController2.get(this._codeEditor);
// // cancel previous snippet mode
// snippetController.leaveSnippet();
// set selection, focus editor
const selections = ranges.map(r => new Selection(r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn));
this._codeEditor.setSelections(selections);
this._codeEditor.focus();
// make modifications
snippetController.insert(template, 0, 0, opts.undoStopBefore, opts.undoStopAfter);
return true;
}
}

View File

@@ -0,0 +1,230 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { disposed } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { ISingleEditOperation, IDecorationRenderOptions, IDecorationOptions, ILineChange } from 'vs/editor/common/editorCommon';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { Position as EditorPosition, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { MainThreadTextEditor } from './mainThreadEditor';
import { ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions } from 'vs/workbench/api/node/extHost.protocol';
import { MainThreadDocumentsAndEditors } from './mainThreadDocumentsAndEditors';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { equals as objectEquals } from 'vs/base/common/objects';
import { ExtHostContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IExtHostContext } from '../node/extHost.protocol';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
export class MainThreadEditors implements MainThreadEditorsShape {
private _proxy: ExtHostEditorsShape;
private _documentsAndEditors: MainThreadDocumentsAndEditors;
private _workbenchEditorService: IWorkbenchEditorService;
private _telemetryService: ITelemetryService;
private _toDispose: IDisposable[];
private _textEditorsListenersMap: { [editorId: string]: IDisposable[]; };
private _editorPositionData: ITextEditorPositionData;
constructor(
documentsAndEditors: MainThreadDocumentsAndEditors,
extHostContext: IExtHostContext,
@ICodeEditorService private _codeEditorService: ICodeEditorService,
@IWorkbenchEditorService workbenchEditorService: IWorkbenchEditorService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@ITelemetryService telemetryService: ITelemetryService
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostEditors);
this._documentsAndEditors = documentsAndEditors;
this._workbenchEditorService = workbenchEditorService;
this._telemetryService = telemetryService;
this._toDispose = [];
this._textEditorsListenersMap = Object.create(null);
this._editorPositionData = null;
this._toDispose.push(documentsAndEditors.onTextEditorAdd(editors => editors.forEach(this._onTextEditorAdd, this)));
this._toDispose.push(documentsAndEditors.onTextEditorRemove(editors => editors.forEach(this._onTextEditorRemove, this)));
this._toDispose.push(editorGroupService.onEditorsChanged(() => this._updateActiveAndVisibleTextEditors()));
this._toDispose.push(editorGroupService.onEditorsMoved(() => this._updateActiveAndVisibleTextEditors()));
}
public dispose(): void {
Object.keys(this._textEditorsListenersMap).forEach((editorId) => {
dispose(this._textEditorsListenersMap[editorId]);
});
this._textEditorsListenersMap = Object.create(null);
this._toDispose = dispose(this._toDispose);
}
private _onTextEditorAdd(textEditor: MainThreadTextEditor): void {
let id = textEditor.getId();
let toDispose: IDisposable[] = [];
toDispose.push(textEditor.onConfigurationChanged((opts) => {
this._proxy.$acceptOptionsChanged(id, opts);
}));
toDispose.push(textEditor.onSelectionChanged((event) => {
this._proxy.$acceptSelectionsChanged(id, event);
}));
this._textEditorsListenersMap[id] = toDispose;
}
private _onTextEditorRemove(id: string): void {
dispose(this._textEditorsListenersMap[id]);
delete this._textEditorsListenersMap[id];
}
private _updateActiveAndVisibleTextEditors(): void {
// editor columns
let editorPositionData = this._getTextEditorPositionData();
if (!objectEquals(this._editorPositionData, editorPositionData)) {
this._editorPositionData = editorPositionData;
this._proxy.$acceptEditorPositionData(this._editorPositionData);
}
}
private _getTextEditorPositionData(): ITextEditorPositionData {
let result: ITextEditorPositionData = Object.create(null);
for (let workbenchEditor of this._workbenchEditorService.getVisibleEditors()) {
const id = this._documentsAndEditors.findTextEditorIdFor(workbenchEditor);
if (id) {
result[id] = workbenchEditor.position;
}
}
return result;
}
// --- from extension host process
$tryShowTextDocument(resource: URI, options: ITextDocumentShowOptions): TPromise<string> {
const editorOptions: ITextEditorOptions = {
preserveFocus: options.preserveFocus,
pinned: options.pinned,
selection: options.selection
};
const input = {
resource,
options: editorOptions
};
return this._workbenchEditorService.openEditor(input, options.position).then(editor => {
if (!editor) {
return undefined;
}
return this._documentsAndEditors.findTextEditorIdFor(editor);
});
}
$tryShowEditor(id: string, position: EditorPosition): TPromise<void> {
// check how often this is used
this._telemetryService.publicLog('api.deprecated', { function: 'TextEditor.show' });
let mainThreadEditor = this._documentsAndEditors.getEditor(id);
if (mainThreadEditor) {
let model = mainThreadEditor.getModel();
return this._workbenchEditorService.openEditor({
resource: model.uri,
options: { preserveFocus: false }
}, position).then(() => { return; });
}
return undefined;
}
$tryHideEditor(id: string): TPromise<void> {
// check how often this is used
this._telemetryService.publicLog('api.deprecated', { function: 'TextEditor.hide' });
let mainThreadEditor = this._documentsAndEditors.getEditor(id);
if (mainThreadEditor) {
let editors = this._workbenchEditorService.getVisibleEditors();
for (let editor of editors) {
if (mainThreadEditor.matches(editor)) {
return this._workbenchEditorService.closeEditor(editor.position, editor.input).then(() => { return; });
}
}
}
return undefined;
}
$trySetSelections(id: string, selections: ISelection[]): TPromise<any> {
if (!this._documentsAndEditors.getEditor(id)) {
return TPromise.wrapError(disposed(`TextEditor(${id})`));
}
this._documentsAndEditors.getEditor(id).setSelections(selections);
return TPromise.as(null);
}
$trySetDecorations(id: string, key: string, ranges: IDecorationOptions[]): TPromise<any> {
if (!this._documentsAndEditors.getEditor(id)) {
return TPromise.wrapError(disposed(`TextEditor(${id})`));
}
this._documentsAndEditors.getEditor(id).setDecorations(key, ranges);
return TPromise.as(null);
}
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<any> {
if (!this._documentsAndEditors.getEditor(id)) {
return TPromise.wrapError(disposed(`TextEditor(${id})`));
}
this._documentsAndEditors.getEditor(id).revealRange(range, revealType);
return undefined;
}
$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): TPromise<any> {
if (!this._documentsAndEditors.getEditor(id)) {
return TPromise.wrapError(disposed(`TextEditor(${id})`));
}
this._documentsAndEditors.getEditor(id).setConfiguration(options);
return TPromise.as(null);
}
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): TPromise<boolean> {
if (!this._documentsAndEditors.getEditor(id)) {
return TPromise.wrapError<boolean>(disposed(`TextEditor(${id})`));
}
return TPromise.as(this._documentsAndEditors.getEditor(id).applyEdits(modelVersionId, edits, opts));
}
$tryInsertSnippet(id: string, template: string, ranges: IRange[], opts: IUndoStopOptions): TPromise<boolean> {
if (!this._documentsAndEditors.getEditor(id)) {
return TPromise.wrapError<boolean>(disposed(`TextEditor(${id})`));
}
return TPromise.as(this._documentsAndEditors.getEditor(id).insertSnippet(template, ranges, opts));
}
$registerTextEditorDecorationType(key: string, options: IDecorationRenderOptions): void {
this._codeEditorService.registerDecorationType(key, options);
}
$removeTextEditorDecorationType(key: string): void {
this._codeEditorService.removeDecorationType(key);
}
$getDiffInformation(id: string): TPromise<ILineChange[]> {
const editor = this._documentsAndEditors.getEditor(id);
if (!editor) {
return TPromise.wrapError<ILineChange[]>(new Error('No such TextEditor'));
}
const codeEditor = editor.getCodeEditor();
const codeEditorId = codeEditor.getId();
const diffEditors = this._codeEditorService.listDiffEditors();
const [diffEditor] = diffEditors.filter(d => d.getOriginalEditor().getId() === codeEditorId || d.getModifiedEditor().getId() === codeEditorId);
if (!diffEditor) {
return TPromise.as([]);
}
return TPromise.as(diffEditor.getLineChanges());
}
}

View File

@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { SerializedError, onUnexpectedError } from 'vs/base/common/errors';
import { MainThreadErrorsShape, MainContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadErrors)
export class MainThreadErrors implements MainThreadErrorsShape {
dispose(): void {
//
}
$onUnexpectedError(err: any | SerializedError, extensionId: string | undefined): void {
if (err.$isError) {
const { name, message, stack } = err;
err = new Error();
err.message = extensionId ? `[${extensionId}] ${message}` : message;
err.name = name;
err.stack = stack;
}
onUnexpectedError(err);
}
}

View File

@@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Severity from 'vs/base/common/severity';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { MainThreadExtensionServiceShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadExtensionService)
export class MainThreadExtensionService implements MainThreadExtensionServiceShape {
private readonly _extensionService: ExtensionService;
constructor(
extHostContext: IExtHostContext,
@IExtensionService extensionService: IExtensionService
) {
if (extensionService instanceof ExtensionService) {
this._extensionService = extensionService;
}
}
public dispose(): void {
}
$localShowMessage(severity: Severity, msg: string): void {
this._extensionService._logOrShowMessage(severity, msg);
}
$onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number): void {
this._extensionService._onExtensionActivated(extensionId, startup, codeLoadingTime, activateCallTime, activateResolvedTime);
}
$onExtensionActivationFailed(extensionId: string): void {
}
}

View File

@@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { FileChangeType, IFileService } from 'vs/platform/files/common/files';
import { ExtHostContext, ExtHostFileSystemEventServiceShape, FileSystemEvents, IExtHostContext } from '../node/extHost.protocol';
import { IDisposable } from 'vs/base/common/lifecycle';
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostCustomer
export class MainThreadFileSystemEventService {
private readonly _listener: IDisposable;
constructor(
extHostContext: IExtHostContext,
@IFileService fileService: IFileService
) {
const proxy: ExtHostFileSystemEventServiceShape = extHostContext.get(ExtHostContext.ExtHostFileSystemEventService);
const events: FileSystemEvents = {
created: [],
changed: [],
deleted: []
};
this._listener = fileService.onFileChanges(event => {
for (let change of event.changes) {
switch (change.type) {
case FileChangeType.ADDED:
events.created.push(change.resource);
break;
case FileChangeType.UPDATED:
events.changed.push(change.resource);
break;
case FileChangeType.DELETED:
events.deleted.push(change.resource);
break;
}
}
proxy.$onFileEvent(events);
events.created.length = 0;
events.changed.length = 0;
events.deleted.length = 0;
});
}
dispose(): void {
this._listener.dispose();
}
}

View File

@@ -0,0 +1,139 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostContext, ObjectIdentifier, IExtHostContext } from '../node/extHost.protocol';
import { consumeSignals, GCSignal } from 'gc-signals';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import Event, { Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
export const IHeapService = createDecorator<IHeapService>('heapService');
export interface IHeapService {
_serviceBrand: any;
readonly onGarbageCollection: Event<number[]>;
/**
* Track gc-collection for all new objects that
* have the $ident-value set.
*/
trackRecursive<T>(p: TPromise<T>): TPromise<T>;
/**
* Track gc-collection for all new objects that
* have the $ident-value set.
*/
trackRecursive<T>(obj: T): T;
}
export class HeapService implements IHeapService {
_serviceBrand: any;
private _onGarbageCollection: Emitter<number[]> = new Emitter<number[]>();
public readonly onGarbageCollection: Event<number[]> = this._onGarbageCollection.event;
private _activeSignals = new WeakMap<any, GCSignal>();
private _activeIds = new Set<number>();
private _consumeHandle: number;
constructor() {
this._consumeHandle = setInterval(() => {
const ids = consumeSignals();
if (ids.length > 0) {
// local book-keeping
for (const id of ids) {
this._activeIds.delete(id);
}
// fire event
this._onGarbageCollection.fire(ids);
}
}, 15 * 1000);
}
dispose() {
clearInterval(this._consumeHandle);
}
trackRecursive<T>(p: TPromise<T>): TPromise<T>;
trackRecursive<T>(obj: T): T;
trackRecursive<T>(obj: any): any {
if (TPromise.is(obj)) {
return obj.then(result => this.trackRecursive(result));
} else {
return this._doTrackRecursive(obj);
}
}
private _doTrackRecursive(obj: any): any {
const stack = [obj];
while (stack.length > 0) {
// remove first element
let obj = stack.shift();
if (!obj || typeof obj !== 'object') {
continue;
}
for (let key in obj) {
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
continue;
}
const value = obj[key];
// recurse -> object/array
if (typeof value === 'object') {
stack.push(value);
} else if (key === ObjectIdentifier.name) {
// track new $ident-objects
if (typeof value === 'number' && !this._activeIds.has(value)) {
this._activeIds.add(value);
this._activeSignals.set(obj, new GCSignal(value));
}
}
}
}
return obj;
}
}
@extHostCustomer
export class MainThreadHeapService {
private _toDispose: IDisposable;
constructor(
extHostContext: IExtHostContext,
@IHeapService heapService: IHeapService,
) {
const proxy = extHostContext.get(ExtHostContext.ExtHostHeapService);
this._toDispose = heapService.onGarbageCollection((ids) => {
// send to ext host
proxy.$onGarbageCollection(ids);
});
}
public dispose(): void {
this._toDispose.dispose();
}
}
registerSingleton(IHeapService, HeapService);

View File

@@ -0,0 +1,358 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import * as vscode from 'vscode';
import { IReadOnlyModel, ISingleEditOperation } from 'vs/editor/common/editorCommon';
import * as modes from 'vs/editor/common/modes';
import { WorkspaceSymbolProviderRegistry, IWorkspaceSymbolProvider } from 'vs/workbench/parts/search/common/search';
import { wireCancellationToken } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, IRawColorFormatMap, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration } from 'vs/editor/common/modes/languageConfiguration';
import { IHeapService } from './mainThreadHeapService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { ColorFormatter, CombinedColorFormatter } from 'vs/editor/contrib/colorPicker/common/colorFormatter';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape {
private _proxy: ExtHostLanguageFeaturesShape;
private _heapService: IHeapService;
private _modeService: IModeService;
private _registrations: { [handle: number]: IDisposable; } = Object.create(null);
private _formatters: Map<number, ColorFormatter>;
constructor(
extHostContext: IExtHostContext,
@IHeapService heapService: IHeapService,
@IModeService modeService: IModeService,
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostLanguageFeatures);
this._heapService = heapService;
this._modeService = modeService;
this._formatters = new Map<number, ColorFormatter>();
}
dispose(): void {
for (const key in this._registrations) {
this._registrations[key].dispose();
}
}
$unregister(handle: number): TPromise<any> {
let registration = this._registrations[handle];
if (registration) {
registration.dispose();
delete this._registrations[handle];
}
return undefined;
}
// --- outline
$registerOutlineSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.DocumentSymbolProviderRegistry.register(selector, <modes.DocumentSymbolProvider>{
provideDocumentSymbols: (model: IReadOnlyModel, token: CancellationToken): Thenable<modes.SymbolInformation[]> => {
return wireCancellationToken(token, this._proxy.$provideDocumentSymbols(handle, model.uri));
}
});
return undefined;
}
// --- code lens
$registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector, eventHandle: number): TPromise<any> {
const provider = <modes.CodeLensProvider>{
provideCodeLenses: (model: IReadOnlyModel, token: CancellationToken): modes.ICodeLensSymbol[] | Thenable<modes.ICodeLensSymbol[]> => {
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideCodeLenses(handle, model.uri)));
},
resolveCodeLens: (model: IReadOnlyModel, codeLens: modes.ICodeLensSymbol, token: CancellationToken): modes.ICodeLensSymbol | Thenable<modes.ICodeLensSymbol> => {
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$resolveCodeLens(handle, model.uri, codeLens)));
}
};
if (typeof eventHandle === 'number') {
const emitter = new Emitter<modes.CodeLensProvider>();
this._registrations[eventHandle] = emitter;
provider.onDidChange = emitter.event;
}
this._registrations[handle] = modes.CodeLensProviderRegistry.register(selector, provider);
return undefined;
}
$emitCodeLensEvent(eventHandle: number, event?: any): TPromise<any> {
const obj = this._registrations[eventHandle];
if (obj instanceof Emitter) {
obj.fire(event);
}
return undefined;
}
// --- declaration
$registerDeclaractionSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.DefinitionProviderRegistry.register(selector, <modes.DefinitionProvider>{
provideDefinition: (model, position, token): Thenable<modes.Definition> => {
return wireCancellationToken(token, this._proxy.$provideDefinition(handle, model.uri, position));
}
});
return undefined;
}
$registerImplementationSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.ImplementationProviderRegistry.register(selector, <modes.ImplementationProvider>{
provideImplementation: (model, position, token): Thenable<modes.Definition> => {
return wireCancellationToken(token, this._proxy.$provideImplementation(handle, model.uri, position));
}
});
return undefined;
}
$registerTypeDefinitionSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.TypeDefinitionProviderRegistry.register(selector, <modes.TypeDefinitionProvider>{
provideTypeDefinition: (model, position, token): Thenable<modes.Definition> => {
return wireCancellationToken(token, this._proxy.$provideTypeDefinition(handle, model.uri, position));
}
});
return undefined;
}
// --- extra info
$registerHoverProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.HoverProviderRegistry.register(selector, <modes.HoverProvider>{
provideHover: (model: IReadOnlyModel, position: EditorPosition, token: CancellationToken): Thenable<modes.Hover> => {
return wireCancellationToken(token, this._proxy.$provideHover(handle, model.uri, position));
}
});
return undefined;
}
// --- occurrences
$registerDocumentHighlightProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.DocumentHighlightProviderRegistry.register(selector, <modes.DocumentHighlightProvider>{
provideDocumentHighlights: (model: IReadOnlyModel, position: EditorPosition, token: CancellationToken): Thenable<modes.DocumentHighlight[]> => {
return wireCancellationToken(token, this._proxy.$provideDocumentHighlights(handle, model.uri, position));
}
});
return undefined;
}
// --- references
$registerReferenceSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.ReferenceProviderRegistry.register(selector, <modes.ReferenceProvider>{
provideReferences: (model: IReadOnlyModel, position: EditorPosition, context: modes.ReferenceContext, token: CancellationToken): Thenable<modes.Location[]> => {
return wireCancellationToken(token, this._proxy.$provideReferences(handle, model.uri, position, context));
}
});
return undefined;
}
// --- quick fix
$registerQuickFixSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.CodeActionProviderRegistry.register(selector, <modes.CodeActionProvider>{
provideCodeActions: (model: IReadOnlyModel, range: EditorRange, token: CancellationToken): Thenable<modes.Command[]> => {
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideCodeActions(handle, model.uri, range)));
}
});
return undefined;
}
// --- formatting
$registerDocumentFormattingSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.DocumentFormattingEditProviderRegistry.register(selector, <modes.DocumentFormattingEditProvider>{
provideDocumentFormattingEdits: (model: IReadOnlyModel, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
return wireCancellationToken(token, this._proxy.$provideDocumentFormattingEdits(handle, model.uri, options));
}
});
return undefined;
}
$registerRangeFormattingSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.DocumentRangeFormattingEditProviderRegistry.register(selector, <modes.DocumentRangeFormattingEditProvider>{
provideDocumentRangeFormattingEdits: (model: IReadOnlyModel, range: EditorRange, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
return wireCancellationToken(token, this._proxy.$provideDocumentRangeFormattingEdits(handle, model.uri, range, options));
}
});
return undefined;
}
$registerOnTypeFormattingSupport(handle: number, selector: vscode.DocumentSelector, autoFormatTriggerCharacters: string[]): TPromise<any> {
this._registrations[handle] = modes.OnTypeFormattingEditProviderRegistry.register(selector, <modes.OnTypeFormattingEditProvider>{
autoFormatTriggerCharacters,
provideOnTypeFormattingEdits: (model: IReadOnlyModel, position: EditorPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
return wireCancellationToken(token, this._proxy.$provideOnTypeFormattingEdits(handle, model.uri, position, ch, options));
}
});
return undefined;
}
// --- navigate type
$registerNavigateTypeSupport(handle: number): TPromise<any> {
this._registrations[handle] = WorkspaceSymbolProviderRegistry.register(<IWorkspaceSymbolProvider>{
provideWorkspaceSymbols: (search: string): TPromise<modes.SymbolInformation[]> => {
return this._heapService.trackRecursive(this._proxy.$provideWorkspaceSymbols(handle, search));
},
resolveWorkspaceSymbol: (item: modes.SymbolInformation): TPromise<modes.SymbolInformation> => {
return this._proxy.$resolveWorkspaceSymbol(handle, item);
}
});
return undefined;
}
// --- rename
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.RenameProviderRegistry.register(selector, <modes.RenameProvider>{
provideRenameEdits: (model: IReadOnlyModel, position: EditorPosition, newName: string, token: CancellationToken): Thenable<modes.WorkspaceEdit> => {
return wireCancellationToken(token, this._proxy.$provideRenameEdits(handle, model.uri, position, newName));
}
});
return undefined;
}
// --- suggest
$registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[]): TPromise<any> {
this._registrations[handle] = modes.SuggestRegistry.register(selector, <modes.ISuggestSupport>{
triggerCharacters,
provideCompletionItems: (model: IReadOnlyModel, position: EditorPosition, token: CancellationToken): Thenable<modes.ISuggestResult> => {
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideCompletionItems(handle, model.uri, position)));
},
resolveCompletionItem: (model: IReadOnlyModel, position: EditorPosition, suggestion: modes.ISuggestion, token: CancellationToken): Thenable<modes.ISuggestion> => {
return wireCancellationToken(token, this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion));
}
});
return undefined;
}
// --- parameter hints
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): TPromise<any> {
this._registrations[handle] = modes.SignatureHelpProviderRegistry.register(selector, <modes.SignatureHelpProvider>{
signatureHelpTriggerCharacters: triggerCharacter,
provideSignatureHelp: (model: IReadOnlyModel, position: EditorPosition, token: CancellationToken): Thenable<modes.SignatureHelp> => {
return wireCancellationToken(token, this._proxy.$provideSignatureHelp(handle, model.uri, position));
}
});
return undefined;
}
// --- links
$registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
this._registrations[handle] = modes.LinkProviderRegistry.register(selector, <modes.LinkProvider>{
provideLinks: (model, token) => {
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideDocumentLinks(handle, model.uri)));
},
resolveLink: (link, token) => {
return wireCancellationToken(token, this._proxy.$resolveDocumentLink(handle, link));
}
});
return undefined;
}
// --- colors
$registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any> {
const proxy = this._proxy;
this._registrations[handle] = modes.ColorProviderRegistry.register(selector, <modes.DocumentColorProvider>{
provideColorRanges: (model, token) => {
return wireCancellationToken(token, proxy.$provideDocumentColors(handle, model.uri))
.then(documentColors => {
return documentColors.map(documentColor => {
const formatters = documentColor.availableFormats.map(f => {
if (typeof f === 'number') {
return this._formatters.get(f);
} else {
return new CombinedColorFormatter(this._formatters.get(f[0]), this._formatters.get(f[1]));
}
});
const [red, green, blue, alpha] = documentColor.color;
const color = {
red: red / 255.0,
green: green / 255.0,
blue: blue / 255.0,
alpha
};
return {
color,
formatters,
range: documentColor.range
};
});
});
}
});
return TPromise.as(null);
}
$registerColorFormats(formats: IRawColorFormatMap): TPromise<any> {
formats.forEach(f => this._formatters.set(f[0], new ColorFormatter(f[1])));
return TPromise.as(null);
}
// --- configuration
$setLanguageConfiguration(handle: number, languageId: string, _configuration: vscode.LanguageConfiguration): TPromise<any> {
let configuration: LanguageConfiguration = {
comments: _configuration.comments,
brackets: _configuration.brackets,
wordPattern: _configuration.wordPattern,
indentationRules: _configuration.indentationRules,
onEnterRules: _configuration.onEnterRules,
autoClosingPairs: null,
surroundingPairs: null,
__electricCharacterSupport: null
};
if (_configuration.__characterPairSupport) {
// backwards compatibility
configuration.autoClosingPairs = _configuration.__characterPairSupport.autoClosingPairs;
}
if (_configuration.__electricCharacterSupport && _configuration.__electricCharacterSupport.docComment) {
configuration.__electricCharacterSupport = {
docComment: {
open: _configuration.__electricCharacterSupport.docComment.open,
close: _configuration.__electricCharacterSupport.docComment.close
}
};
}
let languageIdentifier = this._modeService.getLanguageIdentifier(languageId);
if (languageIdentifier) {
this._registrations[handle] = LanguageConfigurationRegistry.register(languageIdentifier, configuration);
}
return undefined;
}
}

View File

@@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IModeService } from 'vs/editor/common/services/modeService';
import { MainThreadLanguagesShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadLanguages)
export class MainThreadLanguages implements MainThreadLanguagesShape {
private _modeService: IModeService;
constructor(
extHostContext: IExtHostContext,
@IModeService modeService: IModeService
) {
this._modeService = modeService;
}
public dispose(): void {
}
$getLanguages(): TPromise<string[]> {
return TPromise.as(this._modeService.getRegisteredModes());
}
}

View File

@@ -0,0 +1,104 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import nls = require('vs/nls');
import { IMessageService, IChoiceService } from 'vs/platform/message/common/message';
import Severity from 'vs/base/common/severity';
import { Action } from 'vs/base/common/actions';
import { TPromise as Promise } from 'vs/base/common/winjs.base';
import { MainThreadMessageServiceShape, MainContext, IExtHostContext, MainThreadMessageOptions } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IExtensionService, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadMessageService)
export class MainThreadMessageService implements MainThreadMessageServiceShape {
constructor(
extHostContext: IExtHostContext,
@IExtensionService private readonly _extensionService: IExtensionService,
@IMessageService private readonly _messageService: IMessageService,
@IChoiceService private readonly _choiceService: IChoiceService
) {
//
}
dispose(): void {
//
}
$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable<number> {
if (options.modal) {
return this._showModalMessage(severity, message, commands);
} else {
return this._showMessage(severity, message, commands, options.extension);
}
}
private _showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[], extension: IExtensionDescription): Thenable<number> {
return new Promise<number>(resolve => {
let messageHide: Function;
let actions: MessageItemAction[] = [];
let hasCloseAffordance = false;
class MessageItemAction extends Action {
constructor(id: string, label: string, handle: number) {
super(id, label, undefined, true, () => {
resolve(handle);
messageHide(); // triggers dispose! make sure promise is already resolved
return undefined;
});
}
dispose(): void {
resolve(undefined);
}
}
commands.forEach(command => {
if (command.isCloseAffordance === true) {
hasCloseAffordance = true;
}
actions.push(new MessageItemAction('_extension_message_handle_' + command.handle, command.title, command.handle));
});
if (!hasCloseAffordance) {
actions.push(new MessageItemAction('__close', nls.localize('close', "Close"), undefined));
}
messageHide = this._messageService.show(severity, {
message,
actions,
source: extension && `${extension.displayName || extension.name}`
});
});
}
private _showModalMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable<number> {
let cancelId: number | undefined = void 0;
const options = commands.map((command, index) => {
if (command.isCloseAffordance === true) {
cancelId = index;
}
return command.title;
});
if (cancelId === void 0) {
if (options.length > 0) {
options.push(nls.localize('cancel', "Cancel"));
} else {
options.push(nls.localize('ok', "OK"));
}
cancelId = options.length - 1;
}
return this._choiceService.choose(severity, message, options, cancelId, true)
.then(result => result === commands.length ? undefined : commands[result].handle);
}
}

View File

@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
import { IOutputService, IOutputChannel, OUTPUT_PANEL_ID, Extensions, IOutputChannelRegistry } from 'vs/workbench/parts/output/common/output';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { MainThreadOutputServiceShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadOutputService)
export class MainThreadOutputService implements MainThreadOutputServiceShape {
private readonly _outputService: IOutputService;
private readonly _partService: IPartService;
private readonly _panelService: IPanelService;
constructor(
extHostContext: IExtHostContext,
@IOutputService outputService: IOutputService,
@IPartService partService: IPartService,
@IPanelService panelService: IPanelService
) {
this._outputService = outputService;
this._partService = partService;
this._panelService = panelService;
}
public dispose(): void {
// Leave all the existing channels intact (e.g. might help with troubleshooting)
}
public $append(channelId: string, label: string, value: string): TPromise<void> {
this._getChannel(channelId, label).append(value);
return undefined;
}
public $clear(channelId: string, label: string): TPromise<void> {
this._getChannel(channelId, label).clear();
return undefined;
}
public $reveal(channelId: string, label: string, preserveFocus: boolean): TPromise<void> {
this._getChannel(channelId, label).show(preserveFocus);
return undefined;
}
private _getChannel(channelId: string, label: string): IOutputChannel {
if (!Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).getChannel(channelId)) {
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel(channelId, label);
}
return this._outputService.getChannel(channelId);
}
public $close(channelId: string): TPromise<void> {
const panel = this._panelService.getActivePanel();
if (panel && panel.getId() === OUTPUT_PANEL_ID && channelId === this._outputService.getActiveChannel().id) {
return this._partService.setPanelHidden(true);
}
return undefined;
}
public $dispose(channelId: string, label: string): TPromise<void> {
this._getChannel(channelId, label).dispose();
return undefined;
}
}

View File

@@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IProgressService2, IProgress, IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
import { TPromise } from 'vs/base/common/winjs.base';
import { MainThreadProgressShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadProgress)
export class MainThreadProgress implements MainThreadProgressShape {
private _progressService: IProgressService2;
private _progress = new Map<number, { resolve: Function, progress: IProgress<IProgressStep> }>();
constructor(
extHostContext: IExtHostContext,
@IProgressService2 progressService: IProgressService2
) {
this._progressService = progressService;
}
dispose(): void {
this._progress.forEach(handle => handle.resolve());
this._progress.clear();
}
$startProgress(handle: number, options: IProgressOptions): void {
const task = this._createTask(handle);
this._progressService.withProgress(options, task);
}
$progressReport(handle: number, message: IProgressStep): void {
this._progress.get(handle).progress.report(message);
}
$progressEnd(handle: number): void {
this._progress.get(handle).resolve();
this._progress.delete(handle);
}
private _createTask(handle: number) {
return (progress: IProgress<IProgressStep>) => {
return new TPromise<any>(resolve => {
this._progress.set(handle, { resolve, progress });
});
};
}
}

View File

@@ -0,0 +1,102 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { asWinJsPromise } from 'vs/base/common/async';
import { IQuickOpenService, IPickOptions, IInputOptions } from 'vs/platform/quickOpen/common/quickOpen';
import { InputBoxOptions } from 'vscode';
import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadQuickOpen)
export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
private _proxy: ExtHostQuickOpenShape;
private _quickOpenService: IQuickOpenService;
private _doSetItems: (items: MyQuickPickItems[]) => any;
private _doSetError: (error: Error) => any;
private _contents: TPromise<MyQuickPickItems[]>;
private _token: number = 0;
constructor(
extHostContext: IExtHostContext,
@IQuickOpenService quickOpenService: IQuickOpenService
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostQuickOpen);
this._quickOpenService = quickOpenService;
}
public dispose(): void {
}
$show(options: IPickOptions): TPromise<number> {
const myToken = ++this._token;
this._contents = new TPromise<MyQuickPickItems[]>((c, e) => {
this._doSetItems = (items) => {
if (myToken === this._token) {
c(items);
}
};
this._doSetError = (error) => {
if (myToken === this._token) {
e(error);
}
};
});
return asWinJsPromise(token => this._quickOpenService.pick(this._contents, options, token)).then(item => {
if (item) {
return item.handle;
}
return undefined;
}, undefined, progress => {
if (progress) {
this._proxy.$onItemSelected((<MyQuickPickItems>progress).handle);
}
});
}
$setItems(items: MyQuickPickItems[]): TPromise<any> {
if (this._doSetItems) {
this._doSetItems(items);
}
return undefined;
}
$setError(error: Error): TPromise<any> {
if (this._doSetError) {
this._doSetError(error);
}
return undefined;
}
// ---- input
$input(options: InputBoxOptions, validateInput: boolean): TPromise<string> {
const inputOptions: IInputOptions = Object.create(null);
if (options) {
inputOptions.password = options.password;
inputOptions.placeHolder = options.placeHolder;
inputOptions.valueSelection = options.valueSelection;
inputOptions.prompt = options.prompt;
inputOptions.value = options.value;
inputOptions.ignoreFocusLost = options.ignoreFocusOut;
}
if (validateInput) {
inputOptions.validateInput = (value) => {
return this._proxy.$validateInput(value);
};
}
return asWinJsPromise(token => this._quickOpenService.input(inputOptions, token));
}
}

View File

@@ -0,0 +1,346 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. 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 { assign } from 'vs/base/common/objects';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations } 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, SCMGroupFeatures, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { Command } from 'vs/editor/common/modes';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
class MainThreadSCMResourceGroup implements ISCMResourceGroup {
constructor(
private sourceControlHandle: number,
private handle: number,
public provider: ISCMProvider,
public features: SCMGroupFeatures,
public label: string,
public id: string,
public resources: ISCMResource[]
) { }
toJSON(): any {
return {
$mid: 4,
sourceControlHandle: this.sourceControlHandle,
groupHandle: this.handle
};
}
}
class MainThreadSCMResource implements ISCMResource {
constructor(
private sourceControlHandle: number,
private groupHandle: number,
private handle: number,
public sourceUri: URI,
public command: Command | undefined,
public resourceGroup: ISCMResourceGroup,
public decorations: ISCMResourceDecorations
) { }
toJSON(): any {
return {
$mid: 3,
sourceControlHandle: this.sourceControlHandle,
groupHandle: this.groupHandle,
handle: this.handle
};
}
}
class MainThreadSCMProvider implements ISCMProvider {
private static ID_HANDLE = 0;
private _id = `scm${MainThreadSCMProvider.ID_HANDLE++}`;
get id(): string { return this._id; }
private _groups: MainThreadSCMResourceGroup[] = [];
private _groupsByHandle: { [handle: number]: MainThreadSCMResourceGroup; } = Object.create(null);
get resources(): ISCMResourceGroup[] {
return this._groups
.filter(g => g.resources.length > 0 || !g.features.hideWhenEmpty);
}
private _onDidChange = new Emitter<void>();
get onDidChange(): Event<void> { return this._onDidChange.event; }
private features: SCMProviderFeatures = {};
get handle(): number { return this._handle; }
get label(): string { return this._label; }
get contextValue(): string { return this._contextValue; }
get commitTemplate(): string | undefined { return this.features.commitTemplate; }
get acceptInputCommand(): Command | undefined { return this.features.acceptInputCommand; }
get statusBarCommands(): Command[] | undefined { return this.features.statusBarCommands; }
private _onDidChangeCommitTemplate = new Emitter<string>();
get onDidChangeCommitTemplate(): Event<string> { return this._onDidChangeCommitTemplate.event; }
private _count: number | undefined = undefined;
get count(): number | undefined { return this._count; }
constructor(
private proxy: ExtHostSCMShape,
private _handle: number,
private _contextValue: string,
private _label: string,
@ISCMService scmService: ISCMService,
@ICommandService private commandService: ICommandService
) { }
$updateSourceControl(features: SCMProviderFeatures): void {
if ('count' in features) {
this._count = features.count;
}
this.features = assign(this.features, features);
this._onDidChange.fire();
if (typeof features.commitTemplate !== 'undefined') {
this._onDidChangeCommitTemplate.fire(this.commitTemplate);
}
}
$registerGroup(handle: number, id: string, label: string): void {
const group = new MainThreadSCMResourceGroup(
this.handle,
handle,
this,
{},
label,
id,
[]
);
this._groups.push(group);
this._groupsByHandle[handle] = group;
}
$updateGroup(handle: number, features: SCMGroupFeatures): void {
const group = this._groupsByHandle[handle];
if (!group) {
return;
}
group.features = assign(group.features, features);
this._onDidChange.fire();
}
$updateGroupLabel(handle: number, label: string): void {
const group = this._groupsByHandle[handle];
if (!group) {
return;
}
group.label = label;
this._onDidChange.fire();
}
$updateGroupResourceStates(groupHandle: number, resources: SCMRawResource[]): void {
const group = this._groupsByHandle[groupHandle];
if (!group) {
return;
}
group.resources = resources.map(rawResource => {
const [handle, sourceUri, command, icons, tooltip, strikeThrough, faded] = rawResource;
const icon = icons[0];
const iconDark = icons[1] || icon;
const decorations = {
icon: icon && URI.parse(icon),
iconDark: iconDark && URI.parse(iconDark),
tooltip,
strikeThrough,
faded
};
return new MainThreadSCMResource(
this.handle,
groupHandle,
handle,
URI.parse(sourceUri),
command,
group,
decorations
);
});
this._onDidChange.fire();
}
$unregisterGroup(handle: number): void {
const group = this._groupsByHandle[handle];
if (!group) {
return;
}
delete this._groupsByHandle[handle];
this._groups.splice(this._groups.indexOf(group), 1);
}
getOriginalResource(uri: URI): TPromise<URI> {
if (!this.features.hasQuickDiffProvider) {
return TPromise.as(null);
}
return this.proxy.$provideOriginalResource(this.handle, uri);
}
toJSON(): any {
return {
$mid: 5,
handle: this.handle
};
}
dispose(): void {
}
}
@extHostNamedCustomer(MainContext.MainThreadSCM)
export class MainThreadSCM implements MainThreadSCMShape {
private _proxy: ExtHostSCMShape;
private _repositories: { [handle: number]: ISCMRepository; } = Object.create(null);
private _inputDisposables: { [handle: number]: IDisposable; } = Object.create(null);
private _disposables: IDisposable[] = [];
constructor(
extHostContext: IExtHostContext,
@IInstantiationService private instantiationService: IInstantiationService,
@ISCMService private scmService: ISCMService,
@ICommandService private commandService: ICommandService
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostSCM);
}
dispose(): void {
Object.keys(this._repositories)
.forEach(id => this._repositories[id].dispose());
this._repositories = Object.create(null);
Object.keys(this._inputDisposables)
.forEach(id => this._inputDisposables[id].dispose());
this._inputDisposables = Object.create(null);
this._disposables = dispose(this._disposables);
}
$registerSourceControl(handle: number, id: string, label: string): void {
const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, this.scmService, this.commandService);
const repository = this.scmService.registerSCMProvider(provider);
this._repositories[handle] = repository;
const inputDisposable = repository.input.onDidChange(value => this._proxy.$onInputBoxValueChange(handle, value));
this._inputDisposables[handle] = inputDisposable;
}
$updateSourceControl(handle: number, features: SCMProviderFeatures): void {
const repository = this._repositories[handle];
if (!repository) {
return;
}
const provider = repository.provider as MainThreadSCMProvider;
provider.$updateSourceControl(features);
}
$unregisterSourceControl(handle: number): void {
const repository = this._repositories[handle];
if (!repository) {
return;
}
this._inputDisposables[handle].dispose();
delete this._inputDisposables[handle];
repository.dispose();
delete this._repositories[handle];
}
$registerGroup(sourceControlHandle: number, groupHandle: number, id: string, label: string): void {
const repository = this._repositories[sourceControlHandle];
if (!repository) {
return;
}
const provider = repository.provider as MainThreadSCMProvider;
provider.$registerGroup(groupHandle, id, label);
}
$updateGroup(sourceControlHandle: number, groupHandle: number, features: SCMGroupFeatures): void {
const repository = this._repositories[sourceControlHandle];
if (!repository) {
return;
}
const provider = repository.provider as MainThreadSCMProvider;
provider.$updateGroup(groupHandle, features);
}
$updateGroupLabel(sourceControlHandle: number, groupHandle: number, label: string): void {
const repository = this._repositories[sourceControlHandle];
if (!repository) {
return;
}
const provider = repository.provider as MainThreadSCMProvider;
provider.$updateGroupLabel(groupHandle, label);
}
$updateGroupResourceStates(sourceControlHandle: number, groupHandle: number, resources: SCMRawResource[]): void {
const repository = this._repositories[sourceControlHandle];
if (!repository) {
return;
}
const provider = repository.provider as MainThreadSCMProvider;
provider.$updateGroupResourceStates(groupHandle, resources);
}
$unregisterGroup(sourceControlHandle: number, handle: number): void {
const repository = this._repositories[sourceControlHandle];
if (!repository) {
return;
}
const provider = repository.provider as MainThreadSCMProvider;
provider.$unregisterGroup(handle);
}
$setInputBoxValue(sourceControlHandle: number, value: string): void {
const repository = this._repositories[sourceControlHandle];
if (!repository) {
return;
}
repository.input.value = value;
}
}

View File

@@ -0,0 +1,272 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { sequence } from 'vs/base/common/async';
import * as strings from 'vs/base/common/strings';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { ISaveParticipant, ITextFileEditorModel, SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IModel, ICommonCodeEditor, ISingleEditOperation, IIdentifiedSingleEditOperation } from 'vs/editor/common/editorCommon';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { Position } from 'vs/editor/common/core/position';
import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
import { getDocumentFormattingEdits } from 'vs/editor/contrib/format/common/format';
import { EditOperationsCommand } from 'vs/editor/contrib/format/common/formatCommand';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../node/extHost.protocol';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
export interface INamedSaveParticpant extends ISaveParticipant {
readonly name: string;
}
class TrimWhitespaceParticipant implements INamedSaveParticpant {
readonly name = 'TrimWhitespaceParticipant';
constructor(
@IConfigurationService private configurationService: IConfigurationService,
@ICodeEditorService private codeEditorService: ICodeEditorService
) {
// Nothing
}
public participate(model: ITextFileEditorModel, env: { reason: SaveReason }): void {
if (this.configurationService.lookup('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() }).value) {
this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO);
}
}
private doTrimTrailingWhitespace(model: IModel, isAutoSaved: boolean): void {
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
const cursors: Position[] = [];
let editor = findEditor(model, this.codeEditorService);
if (editor) {
// Find `prevSelection` in any case do ensure a good undo stack when pushing the edit
// Collect active cursors in `cursors` only if `isAutoSaved` to avoid having the cursors jump
prevSelection = editor.getSelections();
if (isAutoSaved) {
cursors.push(...prevSelection.map(s => new Position(s.positionLineNumber, s.positionColumn)));
}
}
const ops = trimTrailingWhitespace(model, cursors);
if (!ops.length) {
return; // Nothing to do
}
model.pushEditOperations(prevSelection, ops, (edits) => prevSelection);
}
}
function findEditor(model: IModel, codeEditorService: ICodeEditorService): ICommonCodeEditor {
let candidate: ICommonCodeEditor = null;
if (model.isAttachedToEditor()) {
for (const editor of codeEditorService.listCodeEditors()) {
if (editor.getModel() === model) {
if (editor.isFocused()) {
return editor; // favour focused editor if there are multiple
}
candidate = editor;
}
}
}
return candidate;
}
export class FinalNewLineParticipant implements INamedSaveParticpant {
readonly name = 'FinalNewLineParticipant';
constructor(
@IConfigurationService private configurationService: IConfigurationService,
@ICodeEditorService private codeEditorService: ICodeEditorService
) {
// Nothing
}
public participate(model: ITextFileEditorModel, env: { reason: SaveReason }): void {
if (this.configurationService.lookup('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() }).value) {
this.doInsertFinalNewLine(model.textEditorModel);
}
}
private doInsertFinalNewLine(model: IModel): void {
const lineCount = model.getLineCount();
const lastLine = model.getLineContent(lineCount);
const lastLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(lastLine) === -1;
if (!lineCount || lastLineIsEmptyOrWhitespace) {
return;
}
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
const editor = findEditor(model, this.codeEditorService);
if (editor) {
prevSelection = editor.getSelections();
}
model.pushEditOperations(prevSelection, [EditOperation.insert(new Position(lineCount, model.getLineMaxColumn(lineCount)), model.getEOL())], edits => prevSelection);
if (editor) {
editor.setSelections(prevSelection);
}
}
}
class FormatOnSaveParticipant implements INamedSaveParticpant {
readonly name = 'FormatOnSaveParticipant';
constructor(
@ICodeEditorService private _editorService: ICodeEditorService,
@IConfigurationService private _configurationService: IConfigurationService
) {
// Nothing
}
participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): TPromise<void> {
const model = editorModel.textEditorModel;
if (env.reason === SaveReason.AUTO
|| !this._configurationService.lookup('editor.formatOnSave', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() }).value) {
return undefined;
}
const versionNow = model.getVersionId();
const { tabSize, insertSpaces } = model.getOptions();
return new TPromise<ISingleEditOperation[]>((resolve, reject) => {
setTimeout(reject, 750);
getDocumentFormattingEdits(model, { tabSize, insertSpaces }).then(resolve, reject);
}).then(edits => {
if (edits && versionNow === model.getVersionId()) {
const editor = findEditor(model, this._editorService);
if (editor) {
this._editsWithEditor(editor, edits);
} else {
this._editWithModel(model, edits);
}
}
});
}
private _editsWithEditor(editor: ICommonCodeEditor, edits: ISingleEditOperation[]): void {
EditOperationsCommand.execute(editor, edits);
}
private _editWithModel(model: IModel, edits: ISingleEditOperation[]): void {
const [{ range }] = edits;
const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
model.pushEditOperations([initialSelection], edits.map(FormatOnSaveParticipant._asIdentEdit), undoEdits => {
for (const { range } of undoEdits) {
if (Range.areIntersectingOrTouching(range, initialSelection)) {
return [new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)];
}
}
return undefined;
});
}
private static _asIdentEdit({ text, range }: ISingleEditOperation): IIdentifiedSingleEditOperation {
return {
text,
range: Range.lift(range),
identifier: undefined,
forceMoveMarkers: true
};
}
}
class ExtHostSaveParticipant implements INamedSaveParticpant {
private _proxy: ExtHostDocumentSaveParticipantShape;
readonly name = 'ExtHostSaveParticipant';
constructor(extHostContext: IExtHostContext) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostDocumentSaveParticipant);
}
participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): TPromise<void> {
return new TPromise<any>((resolve, reject) => {
setTimeout(reject, 1750);
this._proxy.$participateInSave(editorModel.getResource(), env.reason).then(values => {
for (const success of values) {
if (!success) {
return TPromise.wrapError(new Error('listener failed'));
}
}
return undefined;
}).then(resolve, reject);
});
}
}
// The save participant can change a model before its saved to support various scenarios like trimming trailing whitespace
@extHostCustomer
export class SaveParticipant implements ISaveParticipant {
private _saveParticipants: INamedSaveParticpant[];
constructor(
extHostContext: IExtHostContext,
@ITelemetryService private _telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IConfigurationService configurationService: IConfigurationService,
@ICodeEditorService codeEditorService: ICodeEditorService
) {
this._saveParticipants = [
new TrimWhitespaceParticipant(configurationService, codeEditorService),
new FormatOnSaveParticipant(codeEditorService, configurationService),
new FinalNewLineParticipant(configurationService, codeEditorService),
new ExtHostSaveParticipant(extHostContext)
];
// Hook into model
TextFileEditorModel.setSaveParticipant(this);
}
dispose(): void {
TextFileEditorModel.setSaveParticipant(undefined);
}
participate(model: ITextFileEditorModel, env: { reason: SaveReason }): TPromise<void> {
const stats: { [name: string]: number } = Object.create(null);
const promiseFactory = this._saveParticipants.map(p => () => {
const { name } = p;
const t1 = Date.now();
return TPromise.as(p.participate(model, env)).then(() => {
stats[`Success-${name}`] = Date.now() - t1;
}, err => {
stats[`Failure-${name}`] = Date.now() - t1;
// console.error(err);
});
});
return sequence(promiseFactory).then(() => {
this._telemetryService.publicLog('saveParticipantStats', stats);
});
}
}

View File

@@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { IDisposable } from 'vs/base/common/lifecycle';
import { MainThreadStatusBarShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadStatusBar)
export class MainThreadStatusBar implements MainThreadStatusBarShape {
private readonly _entries: { [id: number]: IDisposable };
constructor(
extHostContext: IExtHostContext,
@IStatusbarService private readonly _statusbarService: IStatusbarService
) {
this._entries = Object.create(null);
}
dispose(): void {
for (const key in this._entries) {
this._entries[key].dispose();
}
}
$setEntry(id: number, extensionId: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void {
// Dispose any old
this.$dispose(id);
// Add new
let entry = this._statusbarService.addEntry({ text, tooltip, command, color, extensionId }, alignment, priority);
this._entries[id] = entry;
}
$dispose(id: number) {
let disposeable = this._entries[id];
if (disposeable) {
disposeable.dispose();
}
delete this._entries[id];
}
}

View File

@@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { MainThreadStorageShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadStorage)
export class MainThreadStorage implements MainThreadStorageShape {
private _storageService: IStorageService;
constructor(
extHostContext: IExtHostContext,
@IStorageService storageService: IStorageService
) {
this._storageService = storageService;
}
dispose(): void {
}
$getValue<T>(shared: boolean, key: string): TPromise<T> {
let jsonValue = this._storageService.get(key, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
if (!jsonValue) {
return TPromise.as(undefined);
}
let value: T;
try {
value = JSON.parse(jsonValue);
return TPromise.as(value);
} catch (err) {
return TPromise.wrapError<T>(err);
}
}
$setValue(shared: boolean, key: string, value: any): TPromise<any> {
let jsonValue: any;
try {
jsonValue = JSON.stringify(value);
this._storageService.store(key, jsonValue, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
} catch (err) {
return TPromise.wrapError(err);
}
return undefined;
}
}

View File

@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadTask)
export class MainThreadTask implements MainThreadTaskShape {
private _proxy: ExtHostTaskShape;
private _activeHandles: { [handle: number]: boolean; };
constructor(
extHostContext: IExtHostContext,
@ITaskService private _taskService: ITaskService
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostTask);
this._activeHandles = Object.create(null);
}
public dispose(): void {
Object.keys(this._activeHandles).forEach((handle) => {
this._taskService.unregisterTaskProvider(parseInt(handle, 10));
});
this._activeHandles = Object.create(null);
}
public $registerTaskProvider(handle: number): TPromise<void> {
this._taskService.registerTaskProvider(handle, {
provideTasks: () => {
return this._proxy.$provideTasks(handle);
}
});
this._activeHandles[handle] = true;
return TPromise.as<void>(undefined);
}
public $unregisterTaskProvider(handle: number): TPromise<any> {
this._taskService.unregisterTaskProvider(handle);
delete this._activeHandles[handle];
return TPromise.as<void>(undefined);
}
}

View File

@@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { MainThreadTelemetryShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadTelemetry)
export class MainThreadTelemetry implements MainThreadTelemetryShape {
private static _name = 'pluginHostTelemetry';
constructor(
extHostContext: IExtHostContext,
@ITelemetryService private readonly _telemetryService: ITelemetryService
) {
//
}
dispose(): void {
//
}
$publicLog(eventName: string, data: any = Object.create(null)): void {
data[MainThreadTelemetry._name] = true;
this._telemetryService.publicLog(eventName, data);
}
}

View File

@@ -0,0 +1,82 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
private _proxy: ExtHostTerminalServiceShape;
private _toDispose: IDisposable[];
constructor(
extHostContext: IExtHostContext,
@ITerminalService private terminalService: ITerminalService
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostTerminalService);
this._toDispose = [];
this._toDispose.push(terminalService.onInstanceDisposed((terminalInstance) => this._onTerminalDisposed(terminalInstance)));
this._toDispose.push(terminalService.onInstanceProcessIdReady((terminalInstance) => this._onTerminalProcessIdReady(terminalInstance)));
}
public dispose(): void {
this._toDispose = dispose(this._toDispose);
// TODO@Daniel: Should all the previously created terminals be disposed
// when the extension host process goes down ?
}
public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], waitOnExit?: boolean): TPromise<number> {
const shellLaunchConfig: IShellLaunchConfig = {
name,
executable: shellPath,
args: shellArgs,
waitOnExit,
ignoreConfigurationCwd: true
};
return TPromise.as(this.terminalService.createInstance(shellLaunchConfig).id);
}
public $show(terminalId: number, preserveFocus: boolean): void {
let terminalInstance = this.terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
this.terminalService.setActiveInstance(terminalInstance);
this.terminalService.showPanel(!preserveFocus);
}
}
public $hide(terminalId: number): void {
if (this.terminalService.getActiveInstance().id === terminalId) {
this.terminalService.hidePanel();
}
}
public $dispose(terminalId: number): void {
let terminalInstance = this.terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
terminalInstance.dispose();
}
}
public $sendText(terminalId: number, text: string, addNewLine: boolean): void {
let terminalInstance = this.terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
terminalInstance.sendText(text, addNewLine);
}
}
private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalClosed(terminalInstance.id);
}
private _onTerminalProcessIdReady(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId);
}
}

View File

@@ -0,0 +1,145 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { ViewsRegistry } from 'vs/workbench/parts/views/browser/viewsRegistry';
import { ITreeViewDataProvider, ITreeItem, TreeItemCollapsibleState } from 'vs/workbench/parts/views/common/views';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadTreeViews)
export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape {
private _proxy: ExtHostTreeViewsShape;
constructor(
extHostContext: IExtHostContext,
@IMessageService private messageService: IMessageService
) {
super();
this._proxy = extHostContext.get(ExtHostContext.ExtHostTreeViews);
}
$registerView(treeViewId: string): void {
ViewsRegistry.registerTreeViewDataProvider(treeViewId, this._register(new TreeViewDataProvider(treeViewId, this._proxy, this.messageService)));
}
$refresh(treeViewId: string, treeItemHandles: number[]): void {
const treeViewDataProvider: TreeViewDataProvider = <TreeViewDataProvider>ViewsRegistry.getTreeViewDataProvider(treeViewId);
if (treeViewDataProvider) {
treeViewDataProvider.refresh(treeItemHandles);
}
}
dispose(): void {
ViewsRegistry.deregisterTreeViewDataProviders();
super.dispose();
}
}
type TreeItemHandle = number;
class TreeViewDataProvider implements ITreeViewDataProvider {
private _onDidChange: Emitter<ITreeItem[] | undefined | null> = new Emitter<ITreeItem[] | undefined | null>();
readonly onDidChange: Event<ITreeItem[] | undefined | null> = this._onDidChange.event;
private _onDispose: Emitter<void> = new Emitter<void>();
readonly onDispose: Event<void> = this._onDispose.event;
private childrenMap: Map<TreeItemHandle, TreeItemHandle[]> = new Map<TreeItemHandle, TreeItemHandle[]>();
private itemsMap: Map<TreeItemHandle, ITreeItem> = new Map<TreeItemHandle, ITreeItem>();
constructor(private treeViewId: string,
private _proxy: ExtHostTreeViewsShape,
private messageService: IMessageService
) {
}
getElements(): TPromise<ITreeItem[]> {
return this._proxy.$getElements(this.treeViewId)
.then(elements => {
this.postGetElements(null, elements);
return elements;
}, err => {
this.messageService.show(Severity.Error, err);
return null;
});
}
getChildren(treeItem: ITreeItem): TPromise<ITreeItem[]> {
if (treeItem.children) {
return TPromise.as(treeItem.children);
}
return this._proxy.$getChildren(this.treeViewId, treeItem.handle)
.then(children => {
this.postGetElements(treeItem.handle, children);
return children;
}, err => {
this.messageService.show(Severity.Error, err);
return null;
});
}
refresh(treeItemHandles: number[]) {
if (treeItemHandles && treeItemHandles.length) {
let treeItems = treeItemHandles.map(treeItemHandle => this.itemsMap.get(treeItemHandle))
.filter(treeItem => !!treeItem);
if (treeItems.length) {
this._onDidChange.fire(treeItems);
}
} else {
this._onDidChange.fire();
}
}
dispose(): void {
this._onDispose.fire();
}
private clearChildren(treeItemHandle: TreeItemHandle): void {
const children = this.childrenMap.get(treeItemHandle);
if (children) {
for (const child of children) {
this.clearChildren(child);
this.itemsMap.delete(child);
}
this.childrenMap.delete(treeItemHandle);
}
}
private postGetElements(parent: TreeItemHandle, children: ITreeItem[]) {
this.setElements(parent, children);
}
private setElements(parent: TreeItemHandle, children: ITreeItem[]) {
if (children && children.length) {
for (const child of children) {
this.itemsMap.set(child.handle, child);
if (child.children && child.children.length) {
this.setElements(child.handle, child.children);
}
}
if (parent) {
this.childrenMap.set(parent, children.map(child => child.handle));
}
}
}
private populateElementsToExpand(elements: ITreeItem[], toExpand: ITreeItem[]) {
for (const element of elements) {
if (element.collapsibleState === TreeItemCollapsibleState.Expanded) {
toExpand.push(element);
if (element.children && element.children.length) {
this.populateElementsToExpand(element.children, toExpand);
}
}
}
}
}

View File

@@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { MainThreadWindowShape, ExtHostWindowShape, ExtHostContext, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadWindow)
export class MainThreadWindow implements MainThreadWindowShape {
private readonly proxy: ExtHostWindowShape;
private disposables: IDisposable[] = [];
constructor(
extHostContext: IExtHostContext,
@IWindowService private windowService: IWindowService
) {
this.proxy = extHostContext.get(ExtHostContext.ExtHostWindow);
windowService.onDidChangeFocus(this.proxy.$onDidChangeWindowFocus, this.proxy, this.disposables);
}
$getWindowVisibility(): TPromise<boolean> {
return this.windowService.isFocused();
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}

View File

@@ -0,0 +1,201 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import URI from 'vs/base/common/uri';
import { ISearchService, QueryType, ISearchQuery, ISearchProgressItem, ISearchComplete } from 'vs/platform/search/common/search';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { ICommonCodeEditor, isCommonCodeEditor } from 'vs/editor/common/editorCommon';
import { bulkEdit, IResourceEdit } from 'vs/editor/common/services/bulkEdit';
import { TPromise, PPromise } from 'vs/base/common/winjs.base';
import { MainThreadWorkspaceShape, ExtHostWorkspaceShape, ExtHostContext, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { IFileService } from 'vs/platform/files/common/files';
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
import { RemoteFileService } from 'vs/workbench/services/files/electron-browser/remoteFileService';
import { Emitter } from 'vs/base/common/event';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
private readonly _toDispose: IDisposable[] = [];
private readonly _activeSearches: { [id: number]: TPromise<URI[]> } = Object.create(null);
private readonly _proxy: ExtHostWorkspaceShape;
constructor(
extHostContext: IExtHostContext,
@ISearchService private readonly _searchService: ISearchService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@ITextFileService private readonly _textFileService: ITextFileService,
@IWorkbenchEditorService private readonly _editorService: IWorkbenchEditorService,
@ITextModelService private readonly _textModelResolverService: ITextModelService,
@IFileService private readonly _fileService: IFileService
) {
this._proxy = extHostContext.get(ExtHostContext.ExtHostWorkspace);
this._contextService.onDidChangeWorkspaceRoots(this._onDidChangeWorkspace, this, this._toDispose);
}
dispose(): void {
dispose(this._toDispose);
for (let requestId in this._activeSearches) {
const search = this._activeSearches[requestId];
search.cancel();
}
}
// --- workspace ---
private _onDidChangeWorkspace(): void {
this._proxy.$acceptWorkspaceData(this._contextService.getWorkspace());
}
// --- search ---
$startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable<URI[]> {
const workspace = this._contextService.getWorkspace();
if (!workspace) {
return undefined;
}
const query: ISearchQuery = {
folderQueries: workspace.roots.map(root => ({ folder: root })),
type: QueryType.File,
maxResults,
includePattern: { [include]: true },
excludePattern: { [exclude]: true },
};
this._searchService.extendQuery(query);
const search = this._searchService.search(query).then(result => {
return result.results.map(m => m.resource);
}, err => {
if (!isPromiseCanceledError(err)) {
return TPromise.wrapError(err);
}
return undefined;
});
this._activeSearches[requestId] = search;
const onDone = () => delete this._activeSearches[requestId];
search.done(onDone, onDone);
return search;
}
$cancelSearch(requestId: number): Thenable<boolean> {
const search = this._activeSearches[requestId];
if (search) {
delete this._activeSearches[requestId];
search.cancel();
return TPromise.as(true);
}
return undefined;
}
// --- save & edit resources ---
$saveAll(includeUntitled?: boolean): Thenable<boolean> {
return this._textFileService.saveAll(includeUntitled).then(result => {
return result.results.every(each => each.success === true);
});
}
$applyWorkspaceEdit(edits: IResourceEdit[]): TPromise<boolean> {
let codeEditor: ICommonCodeEditor;
let editor = this._editorService.getActiveEditor();
if (editor) {
let candidate = editor.getControl();
if (isCommonCodeEditor(candidate)) {
codeEditor = candidate;
}
}
return bulkEdit(this._textModelResolverService, codeEditor, edits, this._fileService)
.then(() => true);
}
// --- EXPERIMENT: workspace provider
private _idPool: number = 0;
private readonly _provider = new Map<number, [IDisposable, Emitter<URI>]>();
private readonly _searchSessions = new Map<number, { resolve: (result: ISearchComplete) => void, reject: Function, progress: (item: ISearchProgressItem) => void, matches: URI[] }>();
$registerFileSystemProvider(handle: number, authority: string): void {
if (!(this._fileService instanceof RemoteFileService)) {
throw new Error();
}
const emitter = new Emitter<URI>();
const provider = {
onDidChange: emitter.event,
resolve: (resource) => {
return this._proxy.$resolveFile(handle, resource);
},
update: (resource, value) => {
return this._proxy.$storeFile(handle, resource, value);
}
};
const searchProvider = {
search: (query) => {
if (query.type !== QueryType.File) {
return undefined;
}
const session = ++this._idPool;
return new PPromise<any, any>((resolve, reject, progress) => {
this._searchSessions.set(session, { resolve, reject, progress, matches: [] });
this._proxy.$startSearch(handle, session, query.filePattern);
}, () => {
this._proxy.$cancelSearch(handle, session);
});
}
};
const registrations = combinedDisposable([
this._fileService.registerProvider(authority, provider),
this._searchService.registerSearchResultProvider(searchProvider),
]);
this._provider.set(handle, [registrations, emitter]);
}
$unregisterFileSystemProvider(handle: number): void {
if (this._provider.has(handle)) {
dispose(this._provider.get(handle)[0]);
this._provider.delete(handle);
}
}
$onFileSystemChange(handle: number, resource: URI) {
const [, emitter] = this._provider.get(handle);
emitter.fire(resource);
};
$updateSearchSession(session: number, data: URI): void {
if (this._searchSessions.has(session)) {
this._searchSessions.get(session).progress({ resource: data });
this._searchSessions.get(session).matches.push(data);
}
}
$finishSearchSession(session: number, err?: any): void {
if (this._searchSessions.has(session)) {
const { matches, resolve, reject } = this._searchSessions.get(session);
this._searchSessions.delete(session);
if (err) {
reject(err);
} else {
resolve({
limitHit: false,
stats: undefined,
results: matches.map(resource => ({ resource }))
});
}
}
}
}

View File

@@ -0,0 +1,667 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Emitter } from 'vs/base/common/event';
import { TrieMap } from 'vs/base/common/map';
import { score } from 'vs/editor/common/modes/languageSelector';
import * as Platform from 'vs/base/common/platform';
import * as errors from 'vs/base/common/errors';
import product from 'vs/platform/node/product';
import pkg from 'vs/platform/node/package';
import { ExtHostFileSystemEventService } from 'vs/workbench/api/node/extHostFileSystemEventService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostDocumentContentProvider } from 'vs/workbench/api/node/extHostDocumentContentProviders';
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/node/extHostDocumentSaveParticipant';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { ExtHostTreeViews } from 'vs/workbench/api/node/extHostTreeViews';
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';
import { ExtHostOutputService } from 'vs/workbench/api/node/extHostOutputService';
import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService';
import { ExtHostMessageService } from 'vs/workbench/api/node/extHostMessageService';
import { ExtHostEditors } from 'vs/workbench/api/node/extHostTextEditors';
import { ExtHostLanguages } from 'vs/workbench/api/node/extHostLanguages';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands';
import { ExtHostTask } from 'vs/workbench/api/node/extHostTask';
// {{SQL CARBON EDIT}}
//import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostCredentials } from 'vs/workbench/api/node/extHostCredentials';
import { ExtHostWindow } from 'vs/workbench/api/node/extHostWindow';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import URI from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import EditorCommon = require('vs/editor/common/editorCommon');
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { TPromise } from 'vs/base/common/winjs.base';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as vscode from 'vscode';
import * as paths from 'vs/base/common/paths';
import { MainContext, ExtHostContext, IInitData } from './extHost.protocol';
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService';
import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService';
import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs';
import { MarkdownString } from 'vs/base/common/htmlContent';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
}
function proposedApiFunction<T>(extension: IExtensionDescription, fn: T): T {
if (extension.enableProposedApi) {
return fn;
} else {
return <any>(() => {
throw new Error(`[${extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.id}`);
});
}
}
/**
* This method instantiates and returns the extension API surface
*/
export function createApiFactory(
initData: IInitData,
threadService: ExtHostThreadService,
extensionService: ExtHostExtensionService
): IExtensionApiFactory {
const mainThreadTelemetry = threadService.get(MainContext.MainThreadTelemetry);
// Addressable instances
const extHostHeapService = threadService.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService());
const extHostDocumentsAndEditors = threadService.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(threadService, extensionService));
const extHostDocuments = threadService.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(threadService, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = threadService.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(threadService, extHostDocumentsAndEditors));
const extHostDocumentSaveParticipant = threadService.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostDocuments, threadService.get(MainContext.MainThreadWorkspace)));
const extHostEditors = threadService.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(threadService, extHostDocumentsAndEditors));
const extHostCommands = threadService.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(threadService, extHostHeapService));
const extHostTreeViews = threadService.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(threadService.get(MainContext.MainThreadTreeViews), extHostCommands));
const extHostWorkspace = threadService.set(ExtHostContext.ExtHostWorkspace, new ExtHostWorkspace(threadService, initData.workspace));
// {{SQL CARBON EDIT}}
// const extHostDebugService = threadService.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(threadService, extHostWorkspace));
const extHostConfiguration = threadService.set(ExtHostContext.ExtHostConfiguration, new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration));
const extHostDiagnostics = threadService.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(threadService));
const languageFeatures = threadService.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(threadService, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics));
const extHostFileSystemEvent = threadService.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService());
const extHostQuickOpen = threadService.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(threadService));
const extHostTerminalService = threadService.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(threadService));
const extHostSCM = threadService.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(threadService, extHostCommands));
const extHostTask = threadService.set(ExtHostContext.ExtHostTask, new ExtHostTask(threadService));
const extHostCredentials = threadService.set(ExtHostContext.ExtHostCredentials, new ExtHostCredentials(threadService));
const extHostWindow = threadService.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(threadService));
threadService.set(ExtHostContext.ExtHostExtensionService, extensionService);
// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = Object.keys(ExtHostContext).map((key) => ExtHostContext[key]);
threadService.assertRegistered(expected);
// Other instances
const extHostMessageService = new ExtHostMessageService(threadService);
const extHostDialogs = new ExtHostDialogs(threadService);
const extHostStatusBar = new ExtHostStatusBar(threadService);
const extHostProgress = new ExtHostProgress(threadService.get(MainContext.MainThreadProgress));
const extHostOutputService = new ExtHostOutputService(threadService);
const extHostLanguages = new ExtHostLanguages(threadService);
// Register API-ish commands
ExtHostApiCommands.register(extHostCommands);
return function (extension: IExtensionDescription): typeof vscode {
if (extension.enableProposedApi && !extension.isBuiltin) {
if (
!initData.environment.enableProposedApiForAll &&
initData.environment.enableProposedApiFor.indexOf(extension.id) < 0
) {
extension.enableProposedApi = false;
console.error(`Extension '${extension.id} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`);
} else {
// proposed api is available when developing or when an extension was explicitly
// spelled out via a command line argument
console.warn(`Extension '${extension.id}' uses PROPOSED API which is subject to change and removal without notice.`);
}
}
const apiUsage = new class {
private _seen = new Set<string>();
publicLog(apiName: string) {
if (this._seen.has(apiName)) {
return undefined;
}
this._seen.add(apiName);
return mainThreadTelemetry.$publicLog('apiUsage', {
name: apiName,
extension: extension.id
});
}
};
// namespace: commands
const commands: typeof vscode.commands = {
registerCommand<T>(id: string, command: <T>(...args: any[]) => T | Thenable<T>, thisArgs?: any): vscode.Disposable {
return extHostCommands.registerCommand(id, command, thisArgs);
},
registerTextEditorCommand(id: string, callback: (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit, ...args: any[]) => void, thisArg?: any): vscode.Disposable {
return extHostCommands.registerCommand(id, (...args: any[]): any => {
let activeTextEditor = extHostEditors.getActiveTextEditor();
if (!activeTextEditor) {
console.warn('Cannot execute ' + id + ' because there is no active text editor.');
return undefined;
}
return activeTextEditor.edit((edit: vscode.TextEditorEdit) => {
args.unshift(activeTextEditor, edit);
callback.apply(thisArg, args);
}).then((result) => {
if (!result) {
console.warn('Edits from command ' + id + ' were not applied.');
}
}, (err) => {
console.warn('An error occurred while running command ' + id, err);
});
});
},
registerDiffInformationCommand: proposedApiFunction(extension, (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => {
return extHostCommands.registerCommand(id, async (...args: any[]) => {
let activeTextEditor = extHostEditors.getActiveTextEditor();
if (!activeTextEditor) {
console.warn('Cannot execute ' + id + ' because there is no active text editor.');
return undefined;
}
const diff = await extHostEditors.getDiffInformation(activeTextEditor.id);
callback.apply(thisArg, [diff, ...args]);
});
}),
executeCommand<T>(id: string, ...args: any[]): Thenable<T> {
return extHostCommands.executeCommand<T>(id, ...args);
},
getCommands(filterInternal: boolean = false): Thenable<string[]> {
return extHostCommands.getCommands(filterInternal);
}
};
// namespace: env
const env: typeof vscode.env = Object.freeze({
get machineId() { return initData.telemetryInfo.machineId; },
get sessionId() { return initData.telemetryInfo.sessionId; },
get language() { return Platform.language; },
get appName() { return product.nameLong; },
get appRoot() { return initData.environment.appRoot; },
});
// namespace: extensions
const extensions: typeof vscode.extensions = {
getExtension(extensionId: string): Extension<any> {
let desc = extensionService.getExtensionDescription(extensionId);
if (desc) {
return new Extension(extensionService, desc);
}
return undefined;
},
get all(): Extension<any>[] {
return extensionService.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc));
}
};
// namespace: languages
const languages: typeof vscode.languages = {
createDiagnosticCollection(name?: string): vscode.DiagnosticCollection {
return extHostDiagnostics.createDiagnosticCollection(name);
},
getLanguages(): TPromise<string[]> {
return extHostLanguages.getLanguages();
},
match(selector: vscode.DocumentSelector, document: vscode.TextDocument): number {
return score(selector, <any>document.uri, document.languageId);
},
registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider): vscode.Disposable {
return languageFeatures.registerCodeActionProvider(selector, provider);
},
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
return languageFeatures.registerCodeLensProvider(selector, provider);
},
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
return languageFeatures.registerDefinitionProvider(selector, provider);
},
registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable {
return languageFeatures.registerImplementationProvider(selector, provider);
},
registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable {
return languageFeatures.registerTypeDefinitionProvider(selector, provider);
},
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable {
return languageFeatures.registerHoverProvider(selector, provider, extension.id);
},
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
return languageFeatures.registerDocumentHighlightProvider(selector, provider);
},
registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable {
return languageFeatures.registerReferenceProvider(selector, provider);
},
registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
return languageFeatures.registerRenameProvider(selector, provider);
},
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable {
return languageFeatures.registerDocumentSymbolProvider(selector, provider);
},
registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable {
return languageFeatures.registerWorkspaceSymbolProvider(provider);
},
registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable {
return languageFeatures.registerDocumentFormattingEditProvider(selector, provider);
},
registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable {
return languageFeatures.registerDocumentRangeFormattingEditProvider(selector, provider);
},
registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacters: string[]): vscode.Disposable {
return languageFeatures.registerOnTypeFormattingEditProvider(selector, provider, [firstTriggerCharacter].concat(moreTriggerCharacters));
},
registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, ...triggerCharacters: string[]): vscode.Disposable {
return languageFeatures.registerSignatureHelpProvider(selector, provider, triggerCharacters);
},
registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable {
return languageFeatures.registerCompletionItemProvider(selector, provider, triggerCharacters);
},
registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
return languageFeatures.registerDocumentLinkProvider(selector, provider);
},
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => {
return languageFeatures.setLanguageConfiguration(language, configuration);
},
// proposed API
registerColorProvider: proposedApiFunction(extension, (selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider) => {
return languageFeatures.registerColorProvider(selector, provider);
})
};
// namespace: window
const window: typeof vscode.window = {
get activeTextEditor() {
return extHostEditors.getActiveTextEditor();
},
get visibleTextEditors() {
return extHostEditors.getVisibleTextEditors();
},
showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): TPromise<vscode.TextEditor> {
let documentPromise: TPromise<vscode.TextDocument>;
if (URI.isUri(documentOrUri)) {
documentPromise = TPromise.wrap(workspace.openTextDocument(documentOrUri));
} else {
documentPromise = TPromise.wrap(<vscode.TextDocument>documentOrUri);
}
return documentPromise.then(document => {
return extHostEditors.showTextDocument(document, columnOrOptions, preserveFocus);
});
},
createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType {
return extHostEditors.createTextEditorDecorationType(options);
},
onDidChangeActiveTextEditor(listener, thisArg?, disposables?) {
return extHostEditors.onDidChangeActiveTextEditor(listener, thisArg, disposables);
},
onDidChangeVisibleTextEditors(listener, thisArg, disposables) {
return extHostEditors.onDidChangeVisibleTextEditors(listener, thisArg, disposables);
},
onDidChangeTextEditorSelection(listener: (e: vscode.TextEditorSelectionChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) {
return extHostEditors.onDidChangeTextEditorSelection(listener, thisArgs, disposables);
},
onDidChangeTextEditorOptions(listener: (e: vscode.TextEditorOptionsChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) {
return extHostEditors.onDidChangeTextEditorOptions(listener, thisArgs, disposables);
},
onDidChangeTextEditorViewColumn(listener, thisArg?, disposables?) {
return extHostEditors.onDidChangeTextEditorViewColumn(listener, thisArg, disposables);
},
onDidCloseTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidCloseTerminal(listener, thisArg, disposables);
},
get state() {
return extHostWindow.state;
},
onDidChangeWindowState: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostWindow.onDidChangeWindowState(listener, thisArg, disposables);
}),
showInformationMessage(message, first, ...rest) {
return extHostMessageService.showMessage(extension, Severity.Info, message, first, rest);
},
showWarningMessage(message, first, ...rest) {
return extHostMessageService.showMessage(extension, Severity.Warning, message, first, rest);
},
showErrorMessage(message, first, ...rest) {
return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest);
},
showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken) {
return extHostQuickOpen.showQuickPick(items, options, token);
},
showInputBox(options?: vscode.InputBoxOptions, token?: vscode.CancellationToken) {
return extHostQuickOpen.showInput(options, token);
},
createStatusBarItem(position?: vscode.StatusBarAlignment, priority?: number): vscode.StatusBarItem {
return extHostStatusBar.createStatusBarEntry(extension.id, <number>position, priority);
},
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): vscode.Disposable {
return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable);
},
withScmProgress<R>(task: (progress: vscode.Progress<number>) => Thenable<R>) {
console.warn(`[Deprecation Warning] function 'withScmProgress' is deprecated and should no longer be used. Use 'withProgress' instead.`);
return extHostProgress.withProgress(extension, { location: extHostTypes.ProgressLocation.SourceControl }, (progress, token) => task({ report(n: number) { /*noop*/ } }));
},
withProgress<R>(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; percentage?: number }>) => Thenable<R>) {
return extHostProgress.withProgress(extension, options, task);
},
createOutputChannel(name: string): vscode.OutputChannel {
return extHostOutputService.createOutputChannel(name);
},
createTerminal(nameOrOptions: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
if (typeof nameOrOptions === 'object') {
return extHostTerminalService.createTerminalFromOptions(<vscode.TerminalOptions>nameOrOptions);
}
return extHostTerminalService.createTerminal(<string>nameOrOptions, shellPath, shellArgs);
},
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider);
},
// proposed API
sampleFunction: proposedApiFunction(extension, () => {
return extHostMessageService.showMessage(extension, Severity.Info, 'Hello Proposed Api!', {}, []);
}),
showOpenDialog: proposedApiFunction(extension, options => {
return extHostDialogs.showOpenDialog(options);
})
};
// namespace: workspace
const workspace: typeof vscode.workspace = {
get rootPath() {
apiUsage.publicLog('workspace#rootPath');
return extHostWorkspace.getPath();
},
set rootPath(value) {
throw errors.readonly();
},
getWorkspaceFolder(resource) {
return extHostWorkspace.getWorkspaceFolder(resource);
},
get workspaceFolders() {
apiUsage.publicLog('workspace#workspaceFolders');
return extHostWorkspace.getWorkspaceFolders();
},
onDidChangeWorkspaceFolders: function (listener, thisArgs?, disposables?) {
apiUsage.publicLog('workspace#onDidChangeWorkspaceFolders');
return extHostWorkspace.onDidChangeWorkspace(listener, thisArgs, disposables);
},
asRelativePath: (pathOrUri, includeWorkspace) => {
return extHostWorkspace.getRelativePath(pathOrUri, includeWorkspace);
},
findFiles: (include, exclude, maxResults?, token?) => {
return extHostWorkspace.findFiles(include, exclude, maxResults, token);
},
saveAll: (includeUntitled?) => {
return extHostWorkspace.saveAll(includeUntitled);
},
applyEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
return extHostWorkspace.appyEdit(edit);
},
createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): vscode.FileSystemWatcher => {
return extHostFileSystemEvent.createFileSystemWatcher(pattern, ignoreCreate, ignoreChange, ignoreDelete);
},
get textDocuments() {
return extHostDocuments.getAllDocumentData().map(data => data.document);
},
set textDocuments(value) {
throw errors.readonly();
},
openTextDocument(uriOrFileNameOrOptions?: vscode.Uri | string | { language?: string; content?: string; }) {
let uriPromise: TPromise<URI>;
let options = uriOrFileNameOrOptions as { language?: string; content?: string; };
if (!options || typeof options.language === 'string') {
uriPromise = extHostDocuments.createDocumentData(options);
} else if (typeof uriOrFileNameOrOptions === 'string') {
uriPromise = TPromise.as(URI.file(uriOrFileNameOrOptions));
} else if (uriOrFileNameOrOptions instanceof URI) {
uriPromise = TPromise.as(<URI>uriOrFileNameOrOptions);
} else {
throw new Error('illegal argument - uriOrFileNameOrOptions');
}
return uriPromise.then(uri => {
return extHostDocuments.ensureDocumentData(uri).then(() => {
const data = extHostDocuments.getDocumentData(uri);
return data && data.document;
});
});
},
onDidOpenTextDocument: (listener, thisArgs?, disposables?) => {
return extHostDocuments.onDidAddDocument(listener, thisArgs, disposables);
},
onDidCloseTextDocument: (listener, thisArgs?, disposables?) => {
return extHostDocuments.onDidRemoveDocument(listener, thisArgs, disposables);
},
onDidChangeTextDocument: (listener, thisArgs?, disposables?) => {
return extHostDocuments.onDidChangeDocument(listener, thisArgs, disposables);
},
onDidSaveTextDocument: (listener, thisArgs?, disposables?) => {
return extHostDocuments.onDidSaveDocument(listener, thisArgs, disposables);
},
onWillSaveTextDocument: (listener, thisArgs?, disposables?) => {
return extHostDocumentSaveParticipant.onWillSaveTextDocumentEvent(listener, thisArgs, disposables);
},
onDidChangeConfiguration: (listener: (_: any) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => {
return extHostConfiguration.onDidChangeConfiguration(listener, thisArgs, disposables);
},
getConfiguration: (section?: string, resource?: vscode.Uri): vscode.WorkspaceConfiguration => {
return extHostConfiguration.getConfiguration(section, <URI>resource);
},
registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider) {
return extHostDocumentContentProviders.registerTextDocumentContentProvider(scheme, provider);
},
registerTaskProvider: (type: string, provider: vscode.TaskProvider) => {
return extHostTask.registerTaskProvider(extension, provider);
},
registerFileSystemProvider: proposedApiFunction(extension, (authority, provider) => {
return extHostWorkspace.registerFileSystemProvider(authority, provider);
})
};
// namespace: scm
const scm: typeof vscode.scm = {
get inputBox() {
return extHostSCM.getLastInputBox(extension);
},
createSourceControl(id: string, label: string) {
mainThreadTelemetry.$publicLog('registerSCMProvider', {
extensionId: extension.id,
providerId: id,
providerLabel: label
});
return extHostSCM.createSourceControl(extension, id, label);
}
};
// {{SQL CARBON EDIT}}
// delete namespace: debug
// namespace: credentials
const credentials = {
readSecret(service: string, account: string): Thenable<string | undefined> {
return extHostCredentials.readSecret(service, account);
},
writeSecret(service: string, account: string, secret: string): Thenable<void> {
return extHostCredentials.writeSecret(service, account, secret);
},
deleteSecret(service: string, account: string): Thenable<boolean> {
return extHostCredentials.deleteSecret(service, account);
}
};
const api: typeof vscode = {
version: pkg.version,
// namespaces
commands,
env,
extensions,
languages,
window,
workspace,
scm,
// {{SQL CARBON EDIT}}
// debug,
// types
CancellationTokenSource: CancellationTokenSource,
CodeLens: extHostTypes.CodeLens,
Color: extHostTypes.Color,
ColorRange: extHostTypes.ColorRange,
EndOfLine: extHostTypes.EndOfLine,
CompletionItem: extHostTypes.CompletionItem,
CompletionItemKind: extHostTypes.CompletionItemKind,
CompletionList: extHostTypes.CompletionList,
Diagnostic: extHostTypes.Diagnostic,
DiagnosticSeverity: extHostTypes.DiagnosticSeverity,
Disposable: extHostTypes.Disposable,
DocumentHighlight: extHostTypes.DocumentHighlight,
DocumentHighlightKind: extHostTypes.DocumentHighlightKind,
DocumentLink: extHostTypes.DocumentLink,
EventEmitter: Emitter,
Hover: extHostTypes.Hover,
IndentAction: languageConfiguration.IndentAction,
Location: extHostTypes.Location,
MarkdownString: MarkdownString,
OverviewRulerLane: EditorCommon.OverviewRulerLane,
ParameterInformation: extHostTypes.ParameterInformation,
Position: extHostTypes.Position,
Range: extHostTypes.Range,
Selection: extHostTypes.Selection,
SignatureHelp: extHostTypes.SignatureHelp,
SignatureInformation: extHostTypes.SignatureInformation,
SnippetString: extHostTypes.SnippetString,
StatusBarAlignment: extHostTypes.StatusBarAlignment,
SymbolInformation: extHostTypes.SymbolInformation,
SymbolKind: extHostTypes.SymbolKind,
TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason,
TextEdit: extHostTypes.TextEdit,
TextEditorCursorStyle: TextEditorCursorStyle,
TextEditorLineNumbersStyle: extHostTypes.TextEditorLineNumbersStyle,
TextEditorRevealType: extHostTypes.TextEditorRevealType,
TextEditorSelectionChangeKind: extHostTypes.TextEditorSelectionChangeKind,
DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior,
Uri: <any>URI,
ViewColumn: extHostTypes.ViewColumn,
WorkspaceEdit: extHostTypes.WorkspaceEdit,
ProgressLocation: extHostTypes.ProgressLocation,
TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState,
TreeItem: extHostTypes.TreeItem,
ThemeColor: extHostTypes.ThemeColor,
// functions
TaskRevealKind: extHostTypes.TaskRevealKind,
TaskPanelKind: extHostTypes.TaskPanelKind,
TaskGroup: extHostTypes.TaskGroup,
ProcessExecution: extHostTypes.ProcessExecution,
ShellExecution: extHostTypes.ShellExecution,
Task: extHostTypes.Task,
ConfigurationTarget: extHostTypes.ConfigurationTarget
};
if (extension.enableProposedApi && extension.isBuiltin) {
api['credentials'] = credentials;
}
return api;
};
}
class Extension<T> implements vscode.Extension<T> {
private _extensionService: ExtHostExtensionService;
public id: string;
public extensionPath: string;
public packageJSON: any;
constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription) {
this._extensionService = extensionService;
this.id = description.id;
this.extensionPath = paths.normalize(description.extensionFolderPath, true);
this.packageJSON = description;
}
get isActive(): boolean {
return this._extensionService.isActivated(this.id);
}
get exports(): T {
return <T>this._extensionService.getExtensionExports(this.id);
}
activate(): Thenable<T> {
return this._extensionService.activateById(this.id, false).then(() => this.exports);
}
}
export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): TPromise<void> {
return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie));
}
function defineAPI(factory: IExtensionApiFactory, extensionPaths: TrieMap<IExtensionDescription>): void {
// each extension is meant to get its own api implementation
const extApiImpl = new Map<string, typeof vscode>();
let defaultApiImpl: typeof vscode;
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request, parent, isMain) {
if (request !== 'vscode') {
return original.apply(this, arguments);
}
// get extension id from filename and api for extension
const ext = extensionPaths.findSubstr(parent.filename);
if (ext) {
let apiImpl = extApiImpl.get(ext.id);
if (!apiImpl) {
apiImpl = factory(ext);
extApiImpl.set(ext.id, apiImpl);
}
return apiImpl;
}
// fall back to a default implementation
if (!defaultApiImpl) {
defaultApiImpl = factory(nullExtensionDescription);
}
return defaultApiImpl;
};
}
const nullExtensionDescription: IExtensionDescription = {
id: 'nullExtensionDescription',
name: 'Null Extension Description',
publisher: 'vscode',
activationEvents: undefined,
contributes: undefined,
enableProposedApi: false,
engines: undefined,
extensionDependencies: undefined,
extensionFolderPath: undefined,
isBuiltin: false,
main: undefined,
version: undefined
};

View File

@@ -0,0 +1,614 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import {
createMainContextProxyIdentifier as createMainId,
createExtHostContextProxyIdentifier as createExtId,
ProxyIdentifier
} from 'vs/workbench/services/thread/common/threadService';
import * as vscode from 'vscode';
import URI from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as modes from 'vs/editor/common/modes';
import { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
import { ITextSource } from 'vs/editor/common/model/textSource';
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IConfigurationData } from 'vs/platform/configuration/common/configuration';
import { IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorModel';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { ITreeItem } from 'vs/workbench/parts/views/common/views';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { SerializedError } from 'vs/base/common/errors';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
enableProposedApiForAll: boolean;
enableProposedApiFor: string | string[];
appRoot: string;
appSettingsHome: string;
disableExtensions: boolean;
userExtensionsHome: string;
extensionDevelopmentPath: string;
extensionTestsPath: string;
}
export interface IWorkspaceData {
id: string;
name: string;
roots: URI[];
}
export interface IInitData {
parentPid: number;
environment: IEnvironment;
workspace: IWorkspaceData;
extensions: IExtensionDescription[];
configuration: IConfigurationData<any>;
telemetryInfo: ITelemetryInfo;
}
export interface IExtHostContext {
/**
* Returns a proxy to an object addressable/named in the extension host process.
*/
get<T>(identifier: ProxyIdentifier<T>): T;
/**
* Register manually created instance.
*/
set<T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R;
}
export interface IMainContext {
/**
* Returns a proxy to an object addressable/named in the main/renderer process.
*/
get<T>(identifier: ProxyIdentifier<T>): T;
}
// --- main thread
export interface MainThreadCommandsShape extends IDisposable {
$registerCommand(id: string): TPromise<any>;
$unregisterCommand(id: string): TPromise<any>;
$executeCommand<T>(id: string, args: any[]): Thenable<T>;
$getCommands(): Thenable<string[]>;
}
export interface MainThreadConfigurationShape extends IDisposable {
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: URI): TPromise<void>;
$removeConfigurationOption(target: ConfigurationTarget, key: string, resource: URI): TPromise<void>;
}
export interface MainThreadDiagnosticsShape extends IDisposable {
$changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise<any>;
$clear(owner: string): TPromise<any>;
}
export interface MainThreadDialogOptions {
uri?: URI;
openLabel?: string;
openFiles?: boolean;
openFolders?: boolean;
openMany?: boolean;
}
export interface MainThreadDiaglogsShape extends IDisposable {
$showOpenDialog(options: MainThreadDialogOptions): TPromise<string[]>;
}
export interface MainThreadDocumentContentProvidersShape extends IDisposable {
$registerTextContentProvider(handle: number, scheme: string): void;
$unregisterTextContentProvider(handle: number): void;
$onVirtualDocumentChange(uri: URI, value: ITextSource): void;
}
export interface MainThreadDocumentsShape extends IDisposable {
$tryCreateDocument(options?: { language?: string; content?: string; }): TPromise<any>;
$tryOpenDocument(uri: URI): TPromise<any>;
$trySaveDocument(uri: URI): TPromise<boolean>;
}
export interface ISelectionChangeEvent {
selections: Selection[];
source?: string;
}
export interface ITextEditorConfigurationUpdate {
tabSize?: number | 'auto';
insertSpaces?: boolean | 'auto';
cursorStyle?: TextEditorCursorStyle;
lineNumbers?: TextEditorLineNumbersStyle;
}
export interface IResolvedTextEditorConfiguration {
tabSize: number;
insertSpaces: boolean;
cursorStyle: TextEditorCursorStyle;
lineNumbers: TextEditorLineNumbersStyle;
}
export enum TextEditorRevealType {
Default = 0,
InCenter = 1,
InCenterIfOutsideViewport = 2,
AtTop = 3
}
export interface IUndoStopOptions {
undoStopBefore: boolean;
undoStopAfter: boolean;
}
export interface IApplyEditsOptions extends IUndoStopOptions {
setEndOfLine: EndOfLine;
}
export interface ITextDocumentShowOptions {
position?: EditorPosition;
preserveFocus?: boolean;
pinned?: boolean;
selection?: IRange;
}
export interface MainThreadEditorsShape extends IDisposable {
$tryShowTextDocument(resource: URI, options: ITextDocumentShowOptions): TPromise<string>;
$registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void;
$removeTextEditorDecorationType(key: string): void;
$tryShowEditor(id: string, position: EditorPosition): TPromise<void>;
$tryHideEditor(id: string): TPromise<void>;
$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): TPromise<any>;
$trySetDecorations(id: string, key: string, ranges: editorCommon.IDecorationOptions[]): TPromise<any>;
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<any>;
$trySetSelections(id: string, selections: ISelection[]): TPromise<any>;
$tryApplyEdits(id: string, modelVersionId: number, edits: editorCommon.ISingleEditOperation[], opts: IApplyEditsOptions): TPromise<boolean>;
$tryInsertSnippet(id: string, template: string, selections: IRange[], opts: IUndoStopOptions): TPromise<any>;
$getDiffInformation(id: string): TPromise<editorCommon.ILineChange[]>;
}
export interface MainThreadTreeViewsShape extends IDisposable {
$registerView(treeViewId: string): void;
$refresh(treeViewId: string, treeItemHandles: number[]): void;
}
export interface MainThreadErrorsShape extends IDisposable {
$onUnexpectedError(err: any | SerializedError, extensionId: string | undefined): void;
}
export interface MainThreadLanguageFeaturesShape extends IDisposable {
$unregister(handle: number): TPromise<any>;
$registerOutlineSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector, eventHandle: number): TPromise<any>;
$emitCodeLensEvent(eventHandle: number, event?: any): TPromise<any>;
$registerDeclaractionSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerImplementationSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerTypeDefinitionSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerHoverProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerDocumentHighlightProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerReferenceSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerQuickFixSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerDocumentFormattingSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerRangeFormattingSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerOnTypeFormattingSupport(handle: number, selector: vscode.DocumentSelector, autoFormatTriggerCharacters: string[]): TPromise<any>;
$registerNavigateTypeSupport(handle: number): TPromise<any>;
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[]): TPromise<any>;
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): TPromise<any>;
$registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$registerColorFormats(formats: IRawColorFormatMap): TPromise<any>;
$registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
$setLanguageConfiguration(handle: number, languageId: string, configuration: vscode.LanguageConfiguration): TPromise<any>;
}
export interface MainThreadLanguagesShape extends IDisposable {
$getLanguages(): TPromise<string[]>;
}
export interface MainThreadMessageOptions {
extension?: IExtensionDescription;
modal?: boolean;
}
export interface MainThreadMessageServiceShape extends IDisposable {
$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable<number>;
}
export interface MainThreadOutputServiceShape extends IDisposable {
$append(channelId: string, label: string, value: string): TPromise<void>;
$clear(channelId: string, label: string): TPromise<void>;
$dispose(channelId: string, label: string): TPromise<void>;
$reveal(channelId: string, label: string, preserveFocus: boolean): TPromise<void>;
$close(channelId: string): TPromise<void>;
}
export interface MainThreadProgressShape extends IDisposable {
$startProgress(handle: number, options: IProgressOptions): void;
$progressReport(handle: number, message: IProgressStep): void;
$progressEnd(handle: number): void;
}
export interface MainThreadTerminalServiceShape extends IDisposable {
$createTerminal(name?: string, shellPath?: string, shellArgs?: string[], waitOnExit?: boolean): TPromise<number>;
$dispose(terminalId: number): void;
$hide(terminalId: number): void;
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
$show(terminalId: number, preserveFocus: boolean): void;
}
export interface MyQuickPickItems extends IPickOpenEntry {
handle: number;
}
export interface MainThreadQuickOpenShape extends IDisposable {
$show(options: IPickOptions): TPromise<number>;
$setItems(items: MyQuickPickItems[]): TPromise<any>;
$setError(error: Error): TPromise<any>;
$input(options: vscode.InputBoxOptions, validateInput: boolean): TPromise<string>;
}
export interface MainThreadStatusBarShape extends IDisposable {
$setEntry(id: number, extensionId: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void;
$dispose(id: number);
}
export interface MainThreadStorageShape extends IDisposable {
$getValue<T>(shared: boolean, key: string): TPromise<T>;
$setValue(shared: boolean, key: string, value: any): TPromise<any>;
}
export interface MainThreadTelemetryShape extends IDisposable {
$publicLog(eventName: string, data?: any): void;
}
export interface MainThreadWorkspaceShape extends IDisposable {
$startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable<URI[]>;
$cancelSearch(requestId: number): Thenable<boolean>;
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
$applyWorkspaceEdit(edits: IResourceEdit[]): TPromise<boolean>;
$registerFileSystemProvider(handle: number, authority: string): void;
$unregisterFileSystemProvider(handle): void;
$onFileSystemChange(handle: number, resource: URI): void;
$updateSearchSession(session: number, data): void;
$finishSearchSession(session: number, err?: any): void;
}
export interface MainThreadTaskShape extends IDisposable {
$registerTaskProvider(handle: number): TPromise<any>;
$unregisterTaskProvider(handle: number): TPromise<any>;
}
export interface MainThreadExtensionServiceShape extends IDisposable {
$localShowMessage(severity: Severity, msg: string): void;
$onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number): void;
$onExtensionActivationFailed(extensionId: string): void;
}
export interface SCMProviderFeatures {
hasQuickDiffProvider?: boolean;
count?: number;
commitTemplate?: string;
acceptInputCommand?: modes.Command;
statusBarCommands?: modes.Command[];
}
export interface SCMGroupFeatures {
hideWhenEmpty?: boolean;
}
export type SCMRawResource = [
number /*handle*/,
string /*resourceUri*/,
modes.Command /*command*/,
string[] /*icons: light, dark*/,
string /*tooltip*/,
boolean /*strike through*/,
boolean /*faded*/
];
export interface MainThreadSCMShape extends IDisposable {
$registerSourceControl(handle: number, id: string, label: string): void;
$updateSourceControl(handle: number, features: SCMProviderFeatures): void;
$unregisterSourceControl(handle: number): void;
$registerGroup(sourceControlHandle: number, handle: number, id: string, label: string): void;
$updateGroup(sourceControlHandle: number, handle: number, features: SCMGroupFeatures): void;
$updateGroupLabel(sourceControlHandle: number, handle: number, label: string): void;
$updateGroupResourceStates(sourceControlHandle: number, groupHandle: number, resources: SCMRawResource[]): void;
$unregisterGroup(sourceControlHandle: number, handle: number): void;
$setInputBoxValue(sourceControlHandle: number, value: string): void;
}
// {{SQL CARBON EDIT}}
/*
export type DebugSessionUUID = string;
export interface MainThreadDebugServiceShape extends IDisposable {
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, handle: number): TPromise<any>;
$unregisterDebugConfigurationProvider(handle: number): TPromise<any>;
$startDebugging(folderUri: URI | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise<boolean>;
$startDebugSession(folderUri: URI | undefined, config: vscode.DebugConfiguration): TPromise<DebugSessionUUID>;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): TPromise<any>;
}
*/
export interface MainThreadCredentialsShape extends IDisposable {
$readSecret(service: string, account: string): Thenable<string | undefined>;
$writeSecret(service: string, account: string, secret: string): Thenable<void>;
$deleteSecret(service: string, account: string): Thenable<boolean>;
}
export interface MainThreadWindowShape extends IDisposable {
$getWindowVisibility(): TPromise<boolean>;
}
// -- extension host
export interface ExtHostCommandsShape {
$executeContributedCommand<T>(id: string, ...args: any[]): Thenable<T>;
$getContributedCommandHandlerDescriptions(): TPromise<{ [id: string]: string | ICommandHandlerDescription }>;
}
export interface ExtHostConfigurationShape {
$acceptConfigurationChanged(data: IConfigurationData<any>);
}
export interface ExtHostDiagnosticsShape {
}
export interface ExtHostDocumentContentProvidersShape {
$provideTextDocumentContent(handle: number, uri: URI): TPromise<string>;
}
export interface IModelAddedData {
url: URI;
versionId: number;
lines: string[];
EOL: string;
modeId: string;
isDirty: boolean;
}
export interface ExtHostDocumentsShape {
$acceptModelModeChanged(strURL: string, oldModeId: string, newModeId: string): void;
$acceptModelSaved(strURL: string): void;
$acceptDirtyStateChanged(strURL: string, isDirty: boolean): void;
$acceptModelChanged(strURL: string, e: IModelChangedEvent, isDirty: boolean): void;
}
export interface ExtHostDocumentSaveParticipantShape {
$participateInSave(resource: URI, reason: SaveReason): TPromise<boolean[]>;
}
export interface ITextEditorAddData {
id: string;
document: URI;
options: IResolvedTextEditorConfiguration;
selections: ISelection[];
editorPosition: EditorPosition;
}
export interface ITextEditorPositionData {
[id: string]: EditorPosition;
}
export interface ExtHostEditorsShape {
$acceptOptionsChanged(id: string, opts: IResolvedTextEditorConfiguration): void;
$acceptSelectionsChanged(id: string, event: ISelectionChangeEvent): void;
$acceptEditorPositionData(data: ITextEditorPositionData): void;
}
export interface IDocumentsAndEditorsDelta {
removedDocuments?: string[];
addedDocuments?: IModelAddedData[];
removedEditors?: string[];
addedEditors?: ITextEditorAddData[];
newActiveEditor?: string;
}
export interface ExtHostDocumentsAndEditorsShape {
$acceptDocumentsAndEditorsDelta(delta: IDocumentsAndEditorsDelta): void;
}
export interface ExtHostTreeViewsShape {
$getElements(treeViewId: string): TPromise<ITreeItem[]>;
$getChildren(treeViewId: string, treeItemHandle: number): TPromise<ITreeItem[]>;
}
export interface ExtHostWorkspaceShape {
$acceptWorkspaceData(workspace: IWorkspaceData): void;
$resolveFile(handle: number, resource: URI): TPromise<string>;
$storeFile(handle: number, resource: URI, content: string): TPromise<any>;
$startSearch(handle: number, session: number, query: string): void;
$cancelSearch(handle: number, session: number): void;
}
export interface ExtHostExtensionServiceShape {
$activateByEvent(activationEvent: string): TPromise<void>;
}
export interface FileSystemEvents {
created: URI[];
changed: URI[];
deleted: URI[];
}
export interface ExtHostFileSystemEventServiceShape {
$onFileEvent(events: FileSystemEvents);
}
export interface ObjectIdentifier {
$ident: number;
}
export namespace ObjectIdentifier {
export const name = '$ident';
export function mixin<T>(obj: T, id: number): T & ObjectIdentifier {
Object.defineProperty(obj, name, { value: id, enumerable: true });
return <T & ObjectIdentifier>obj;
}
export function of(obj: any): number {
return obj[name];
}
}
export interface ExtHostHeapServiceShape {
$onGarbageCollection(ids: number[]): void;
}
export interface IRawColorInfo {
color: [number, number, number, number];
availableFormats: (number | [number, number])[];
range: IRange;
}
export type IRawColorFormatMap = [number, string][];
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: URI): TPromise<modes.SymbolInformation[]>;
$provideCodeLenses(handle: number, resource: URI): TPromise<modes.ICodeLensSymbol[]>;
$resolveCodeLens(handle: number, resource: URI, symbol: modes.ICodeLensSymbol): TPromise<modes.ICodeLensSymbol>;
$provideDefinition(handle: number, resource: URI, position: IPosition): TPromise<modes.Definition>;
$provideImplementation(handle: number, resource: URI, position: IPosition): TPromise<modes.Definition>;
$provideTypeDefinition(handle: number, resource: URI, position: IPosition): TPromise<modes.Definition>;
$provideHover(handle: number, resource: URI, position: IPosition): TPromise<modes.Hover>;
$provideDocumentHighlights(handle: number, resource: URI, position: IPosition): TPromise<modes.DocumentHighlight[]>;
$provideReferences(handle: number, resource: URI, position: IPosition, context: modes.ReferenceContext): TPromise<modes.Location[]>;
$provideCodeActions(handle: number, resource: URI, range: IRange): TPromise<modes.Command[]>;
$provideDocumentFormattingEdits(handle: number, resource: URI, options: modes.FormattingOptions): TPromise<editorCommon.ISingleEditOperation[]>;
$provideDocumentRangeFormattingEdits(handle: number, resource: URI, range: IRange, options: modes.FormattingOptions): TPromise<editorCommon.ISingleEditOperation[]>;
$provideOnTypeFormattingEdits(handle: number, resource: URI, position: IPosition, ch: string, options: modes.FormattingOptions): TPromise<editorCommon.ISingleEditOperation[]>;
$provideWorkspaceSymbols(handle: number, search: string): TPromise<modes.SymbolInformation[]>;
$resolveWorkspaceSymbol(handle: number, symbol: modes.SymbolInformation): TPromise<modes.SymbolInformation>;
$provideRenameEdits(handle: number, resource: URI, position: IPosition, newName: string): TPromise<modes.WorkspaceEdit>;
$provideCompletionItems(handle: number, resource: URI, position: IPosition): TPromise<modes.ISuggestResult>;
$resolveCompletionItem(handle: number, resource: URI, position: IPosition, suggestion: modes.ISuggestion): TPromise<modes.ISuggestion>;
$provideSignatureHelp(handle: number, resource: URI, position: IPosition): TPromise<modes.SignatureHelp>;
$provideDocumentLinks(handle: number, resource: URI): TPromise<modes.ILink[]>;
$provideDocumentColors(handle: number, resource: URI): TPromise<IRawColorInfo[]>;
$resolveDocumentLink(handle: number, link: modes.ILink): TPromise<modes.ILink>;
}
export interface ExtHostQuickOpenShape {
$onItemSelected(handle: number): void;
$validateInput(input: string): TPromise<string>;
}
export interface ExtHostTerminalServiceShape {
$acceptTerminalClosed(id: number): void;
$acceptTerminalProcessId(id: number, processId: number): void;
}
export interface ExtHostSCMShape {
$provideOriginalResource(sourceControlHandle: number, uri: URI): TPromise<URI>;
$onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise<void>;
}
export interface ExtHostTaskShape {
$provideTasks(handle: number): TPromise<TaskSet>;
}
// {{SQL CARBON EDIT}}
/*
export interface ExtHostDebugServiceShape {
$resolveDebugConfiguration(handle: number, folder: URI | undefined, debugConfiguration: any): TPromise<any>;
$provideDebugConfigurations(handle: number, folder: URI | undefined): TPromise<any[]>;
$acceptDebugSessionStarted(id: DebugSessionUUID, type: string, name: string): void;
$acceptDebugSessionTerminated(id: DebugSessionUUID, type: string, name: string): void;
$acceptDebugSessionActiveChanged(id: DebugSessionUUID | undefined, type?: string, name?: string): void;
$acceptDebugSessionCustomEvent(id: DebugSessionUUID, type: string, name: string, event: any): void;
}
*/
export interface ExtHostCredentialsShape {
}
export interface ExtHostWindowShape {
$onDidChangeWindowFocus(value: boolean): void;
}
// --- proxy identifiers
export const MainContext = {
MainThreadCommands: createMainId<MainThreadCommandsShape>('MainThreadCommands'),
MainThreadConfiguration: createMainId<MainThreadConfigurationShape>('MainThreadConfiguration'),
// {{SQL CARBON EDIT}}
// MainThreadDebugService: createMainId<MainThreadDebugServiceShape>('MainThreadDebugService'),
MainThreadDiagnostics: createMainId<MainThreadDiagnosticsShape>('MainThreadDiagnostics'),
MainThreadDialogs: createMainId<MainThreadDiaglogsShape>('MainThreadDiaglogs'),
MainThreadDocuments: createMainId<MainThreadDocumentsShape>('MainThreadDocuments'),
MainThreadDocumentContentProviders: createMainId<MainThreadDocumentContentProvidersShape>('MainThreadDocumentContentProviders'),
MainThreadEditors: createMainId<MainThreadEditorsShape>('MainThreadEditors'),
MainThreadErrors: createMainId<MainThreadErrorsShape>('MainThreadErrors'),
MainThreadTreeViews: createMainId<MainThreadTreeViewsShape>('MainThreadTreeViews'),
MainThreadLanguageFeatures: createMainId<MainThreadLanguageFeaturesShape>('MainThreadLanguageFeatures'),
MainThreadLanguages: createMainId<MainThreadLanguagesShape>('MainThreadLanguages'),
MainThreadMessageService: createMainId<MainThreadMessageServiceShape>('MainThreadMessageService'),
MainThreadOutputService: createMainId<MainThreadOutputServiceShape>('MainThreadOutputService'),
MainThreadProgress: createMainId<MainThreadProgressShape>('MainThreadProgress'),
MainThreadQuickOpen: createMainId<MainThreadQuickOpenShape>('MainThreadQuickOpen'),
MainThreadStatusBar: createMainId<MainThreadStatusBarShape>('MainThreadStatusBar'),
MainThreadStorage: createMainId<MainThreadStorageShape>('MainThreadStorage'),
MainThreadTelemetry: createMainId<MainThreadTelemetryShape>('MainThreadTelemetry'),
MainThreadTerminalService: createMainId<MainThreadTerminalServiceShape>('MainThreadTerminalService'),
MainThreadWorkspace: createMainId<MainThreadWorkspaceShape>('MainThreadWorkspace'),
MainThreadExtensionService: createMainId<MainThreadExtensionServiceShape>('MainThreadExtensionService'),
MainThreadSCM: createMainId<MainThreadSCMShape>('MainThreadSCM'),
MainThreadTask: createMainId<MainThreadTaskShape>('MainThreadTask'),
MainThreadCredentials: createMainId<MainThreadCredentialsShape>('MainThreadCredentials'),
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
};
export const ExtHostContext = {
ExtHostCommands: createExtId<ExtHostCommandsShape>('ExtHostCommands'),
ExtHostConfiguration: createExtId<ExtHostConfigurationShape>('ExtHostConfiguration'),
ExtHostDiagnostics: createExtId<ExtHostDiagnosticsShape>('ExtHostDiagnostics'),
// {{SQL CARBON EDIT}}
// ExtHostDebugService: createExtId<ExtHostDebugServiceShape>('ExtHostDebugService'),
ExtHostDocumentsAndEditors: createExtId<ExtHostDocumentsAndEditorsShape>('ExtHostDocumentsAndEditors'),
ExtHostDocuments: createExtId<ExtHostDocumentsShape>('ExtHostDocuments'),
ExtHostDocumentContentProviders: createExtId<ExtHostDocumentContentProvidersShape>('ExtHostDocumentContentProviders'),
ExtHostDocumentSaveParticipant: createExtId<ExtHostDocumentSaveParticipantShape>('ExtHostDocumentSaveParticipant'),
ExtHostEditors: createExtId<ExtHostEditorsShape>('ExtHostEditors'),
ExtHostTreeViews: createExtId<ExtHostTreeViewsShape>('ExtHostTreeViews'),
ExtHostFileSystemEventService: createExtId<ExtHostFileSystemEventServiceShape>('ExtHostFileSystemEventService'),
ExtHostHeapService: createExtId<ExtHostHeapServiceShape>('ExtHostHeapMonitor'),
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures'),
ExtHostQuickOpen: createExtId<ExtHostQuickOpenShape>('ExtHostQuickOpen'),
ExtHostExtensionService: createExtId<ExtHostExtensionServiceShape>('ExtHostExtensionService'),
ExtHostTerminalService: createExtId<ExtHostTerminalServiceShape>('ExtHostTerminalService'),
ExtHostSCM: createExtId<ExtHostSCMShape>('ExtHostSCM'),
ExtHostTask: createExtId<ExtHostTaskShape>('ExtHostTask'),
ExtHostWorkspace: createExtId<ExtHostWorkspaceShape>('ExtHostWorkspace'),
ExtHostCredentials: createExtId<ExtHostCredentialsShape>('ExtHostCredentials'),
ExtHostWindow: createExtId<ExtHostWindowShape>('ExtHostWindow'),
};

View File

@@ -0,0 +1,474 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. 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 { IDisposable } from 'vs/base/common/lifecycle';
import * as vscode from 'vscode';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import * as types from 'vs/workbench/api/node/extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/editorCommon';
import * as modes from 'vs/editor/common/modes';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { IWorkspaceSymbolProvider } from 'vs/workbench/parts/search/common/search';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
export class ExtHostApiCommands {
static register(commands: ExtHostCommands) {
return new ExtHostApiCommands(commands).registerCommands();
}
private _commands: ExtHostCommands;
private _disposables: IDisposable[] = [];
private constructor(commands: ExtHostCommands) {
this._commands = commands;
}
registerCommands() {
this._register('vscode.executeWorkspaceSymbolProvider', this._executeWorkspaceSymbolProvider, {
description: 'Execute all workspace symbol provider.',
args: [{ name: 'query', description: 'Search string', constraint: String }],
returns: 'A promise that resolves to an array of SymbolInformation-instances.'
});
this._register('vscode.executeDefinitionProvider', this._executeDefinitionProvider, {
description: 'Execute all definition provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position of a symbol', constraint: types.Position }
],
returns: 'A promise that resolves to an array of Location-instances.'
});
this._register('vscode.executeImplementationProvider', this._executeImplementationProvider, {
description: 'Execute all implementation providers.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position of a symbol', constraint: types.Position }
],
returns: 'A promise that resolves to an array of Location-instance.'
});
this._register('vscode.executeHoverProvider', this._executeHoverProvider, {
description: 'Execute all hover provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position of a symbol', constraint: types.Position }
],
returns: 'A promise that resolves to an array of Hover-instances.'
});
this._register('vscode.executeDocumentHighlights', this._executeDocumentHighlights, {
description: 'Execute document highlight provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position }
],
returns: 'A promise that resolves to an array of DocumentHighlight-instances.'
});
this._register('vscode.executeReferenceProvider', this._executeReferenceProvider, {
description: 'Execute reference provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position }
],
returns: 'A promise that resolves to an array of Location-instances.'
});
this._register('vscode.executeDocumentRenameProvider', this._executeDocumentRenameProvider, {
description: 'Execute rename provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
{ name: 'newName', description: 'The new symbol name', constraint: String }
],
returns: 'A promise that resolves to a WorkspaceEdit.'
});
this._register('vscode.executeSignatureHelpProvider', this._executeSignatureHelpProvider, {
description: 'Execute signature help provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
{ name: 'triggerCharacter', description: '(optional) Trigger signature help when the user types the character, like `,` or `(`', constraint: value => value === void 0 || typeof value === 'string' }
],
returns: 'A promise that resolves to SignatureHelp.'
});
this._register('vscode.executeDocumentSymbolProvider', this._executeDocumentSymbolProvider, {
description: 'Execute document symbol provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI }
],
returns: 'A promise that resolves to an array of SymbolInformation-instances.'
});
this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider, {
description: 'Execute completion item provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
{ name: 'triggerCharacter', description: '(optional) Trigger completion when the user types the character, like `,` or `(`', constraint: value => value === void 0 || typeof value === 'string' }
],
returns: 'A promise that resolves to a CompletionList-instance.'
});
this._register('vscode.executeCodeActionProvider', this._executeCodeActionProvider, {
description: 'Execute code action provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'range', description: 'Range in a text document', constraint: types.Range }
],
returns: 'A promise that resolves to an array of Command-instances.'
});
this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider, {
description: 'Execute CodeLens provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI }
],
returns: 'A promise that resolves to an array of CodeLens-instances.'
});
this._register('vscode.executeFormatDocumentProvider', this._executeFormatDocumentProvider, {
description: 'Execute document format provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'options', description: 'Formatting options' }
],
returns: 'A promise that resolves to an array of TextEdits.'
});
this._register('vscode.executeFormatRangeProvider', this._executeFormatRangeProvider, {
description: 'Execute range format provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'range', description: 'Range in a text document', constraint: types.Range },
{ name: 'options', description: 'Formatting options' }
],
returns: 'A promise that resolves to an array of TextEdits.'
});
this._register('vscode.executeFormatOnTypeProvider', this._executeFormatOnTypeProvider, {
description: 'Execute document format provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
{ name: 'ch', description: 'Character that got typed', constraint: String },
{ name: 'options', description: 'Formatting options' }
],
returns: 'A promise that resolves to an array of TextEdits.'
});
this._register('vscode.executeLinkProvider', this._executeDocumentLinkProvider, {
description: 'Execute document link provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI }
],
returns: 'A promise that resolves to an array of DocumentLink-instances.'
});
this._register('vscode.previewHtml', (uri: URI, position?: vscode.ViewColumn, label?: string, options?: any) => {
return this._commands.executeCommand('_workbench.previewHtml',
uri,
typeof position === 'number' && typeConverters.fromViewColumn(position),
label,
options);
}, {
description: `
Render the html of the resource in an editor view.
See [working with the html preview](https://code.visualstudio.com/docs/extensionAPI/vscode-api-commands#working-with-the-html-preview) for more information about the html preview's intergration with the editor and for best practices for extension authors.
`,
args: [
{ name: 'uri', description: 'Uri of the resource to preview.', constraint: value => value instanceof URI || typeof value === 'string' },
{ name: 'column', description: '(optional) Column in which to preview.', constraint: value => typeof value === 'undefined' || (typeof value === 'number' && typeof types.ViewColumn[value] === 'string') },
{ name: 'label', description: '(optional) An human readable string that is used as title for the preview.', constraint: v => typeof v === 'string' || typeof v === 'undefined' },
{ name: 'options', description: '(optional) Options for controlling webview environment.', constraint: v => typeof v === 'object' || typeof v === 'undefined' }
]
});
this._register('vscode.openFolder', (uri?: URI, forceNewWindow?: boolean) => {
if (!uri) {
return this._commands.executeCommand('_files.pickFolderAndOpen', forceNewWindow);
}
return this._commands.executeCommand('_files.windowOpen', [uri.fsPath], forceNewWindow);
}, {
description: 'Open a folder in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder unless the newWindow parameter is set to true.',
args: [
{ name: 'uri', description: '(optional) Uri of the folder to open. If not provided, a native dialog will ask the user for the folder', constraint: value => value === void 0 || value instanceof URI },
{ name: 'newWindow', description: '(optional) Whether to open the folder in a new window or the same. Defaults to opening in the same window.', constraint: value => value === void 0 || typeof value === 'boolean' }
]
});
this._register('vscode.startDebug', (configuration?: any, folderUri?: URI) => {
return this._commands.executeCommand('_workbench.startDebug', configuration, folderUri);
}, {
description: 'Start a debugging session.',
args: [
{ name: 'configuration', description: '(optional) Name of the debug configuration from \'launch.json\' to use. Or a configuration json object to use.' }
]
});
this._register('vscode.diff', (left: URI, right: URI, label: string, options?: vscode.TextDocumentShowOptions) => {
let editorOptions: ITextEditorOptions;
if (options) {
editorOptions = {
pinned: typeof options.preview === 'boolean' ? !options.preview : undefined,
preserveFocus: options.preserveFocus,
selection: typeof options.selection === 'object' ? typeConverters.fromRange(options.selection) : undefined
};
}
return this._commands.executeCommand('_workbench.diff', [
left, right,
label,
undefined,
editorOptions,
options ? typeConverters.fromViewColumn(options.viewColumn) : undefined
]);
}, {
description: 'Opens the provided resources in the diff editor to compare their contents.',
args: [
{ name: 'left', description: 'Left-hand side resource of the diff editor', constraint: URI },
{ name: 'right', description: 'Right-hand side resource of the diff editor', constraint: URI },
{ name: 'title', description: '(optional) Human readable title for the diff editor', constraint: v => v === void 0 || typeof v === 'string' },
{ name: 'options', description: '(optional) Editor options, see vscode.TextDocumentShowOptions' }
]
});
this._register('vscode.open', (resource: URI, column: vscode.ViewColumn) => {
return this._commands.executeCommand('_workbench.open', [resource, typeConverters.fromViewColumn(column)]);
}, {
description: 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.',
args: [
{ name: 'resource', description: 'Resource to open', constraint: URI },
{ name: 'column', description: '(optional) Column in which to open', constraint: v => v === void 0 || typeof v === 'number' }
]
});
}
// --- command impl
private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void {
let disposable = this._commands.registerCommand(id, handler, this, description);
this._disposables.push(disposable);
}
/**
* Execute workspace symbol provider.
*
* @param query Search string to match query symbol names
* @return A promise that resolves to an array of symbol information.
*/
private _executeWorkspaceSymbolProvider(query: string): Thenable<types.SymbolInformation[]> {
return this._commands.executeCommand<[IWorkspaceSymbolProvider, modes.SymbolInformation[]][]>('_executeWorkspaceSymbolProvider', { query }).then(value => {
const result: types.SymbolInformation[] = [];
if (Array.isArray(value)) {
for (let tuple of value) {
result.push(...tuple[1].map(typeConverters.toSymbolInformation));
}
}
return result;
});
}
private _executeDefinitionProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
};
return this._commands.executeCommand<modes.Location[]>('_executeDefinitionProvider', args).then(value => {
if (Array.isArray(value)) {
return value.map(typeConverters.location.to);
}
return undefined;
});
}
private _executeImplementationProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
};
return this._commands.executeCommand<modes.Location[]>('_executeImplementationProvider', args).then(value => {
if (Array.isArray(value)) {
return value.map(typeConverters.location.to);
}
return undefined;
});
}
private _executeHoverProvider(resource: URI, position: types.Position): Thenable<types.Hover[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
};
return this._commands.executeCommand<modes.Hover[]>('_executeHoverProvider', args).then(value => {
if (Array.isArray(value)) {
return value.map(typeConverters.toHover);
}
return undefined;
});
}
private _executeDocumentHighlights(resource: URI, position: types.Position): Thenable<types.DocumentHighlight[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
};
return this._commands.executeCommand<modes.DocumentHighlight[]>('_executeDocumentHighlights', args).then(value => {
if (Array.isArray(value)) {
return value.map(typeConverters.toDocumentHighlight);
}
return undefined;
});
}
private _executeReferenceProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
};
return this._commands.executeCommand<modes.Location[]>('_executeReferenceProvider', args).then(value => {
if (Array.isArray(value)) {
return value.map(typeConverters.location.to);
}
return undefined;
});
}
private _executeDocumentRenameProvider(resource: URI, position: types.Position, newName: string): Thenable<types.WorkspaceEdit> {
const args = {
resource,
position: position && typeConverters.fromPosition(position),
newName
};
return this._commands.executeCommand<modes.WorkspaceEdit>('_executeDocumentRenameProvider', args).then(value => {
if (!value) {
return undefined;
}
if (value.rejectReason) {
return TPromise.wrapError<types.WorkspaceEdit>(new Error(value.rejectReason));
}
let workspaceEdit = new types.WorkspaceEdit();
for (let edit of value.edits) {
workspaceEdit.replace(edit.resource, typeConverters.toRange(edit.range), edit.newText);
}
return workspaceEdit;
});
}
private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Thenable<types.SignatureHelp> {
const args = {
resource,
position: position && typeConverters.fromPosition(position),
triggerCharacter
};
return this._commands.executeCommand<modes.SignatureHelp>('_executeSignatureHelpProvider', args).then(value => {
if (value) {
return typeConverters.SignatureHelp.to(value);
}
return undefined;
});
}
private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string): Thenable<types.CompletionList> {
const args = {
resource,
position: position && typeConverters.fromPosition(position),
triggerCharacter
};
return this._commands.executeCommand<modes.ISuggestResult>('_executeCompletionItemProvider', args).then(result => {
if (result) {
const items = result.suggestions.map(suggestion => typeConverters.Suggest.to(position, suggestion));
return new types.CompletionList(items, result.incomplete);
}
return undefined;
});
}
private _executeDocumentSymbolProvider(resource: URI): Thenable<types.SymbolInformation[]> {
const args = {
resource
};
return this._commands.executeCommand<modes.IOutline>('_executeDocumentSymbolProvider', args).then(value => {
if (value && Array.isArray(value.entries)) {
return value.entries.map(typeConverters.toSymbolInformation);
}
return undefined;
});
}
private _executeCodeActionProvider(resource: URI, range: types.Range): Thenable<vscode.Command[]> {
const args = {
resource,
range: typeConverters.fromRange(range)
};
return this._commands.executeCommand<modes.Command[]>('_executeCodeActionProvider', args).then(value => {
if (!Array.isArray(value)) {
return undefined;
}
return value.map(quickFix => this._commands.converter.fromInternal(quickFix));
});
}
private _executeCodeLensProvider(resource: URI): Thenable<vscode.CodeLens[]> {
const args = { resource };
return this._commands.executeCommand<modes.ICodeLensSymbol[]>('_executeCodeLensProvider', args).then(value => {
if (Array.isArray(value)) {
return value.map(item => {
return new types.CodeLens(
typeConverters.toRange(item.range),
this._commands.converter.fromInternal(item.command));
});
}
return undefined;
});
}
private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
const args = {
resource,
options
};
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatDocumentProvider', args).then(value => {
if (Array.isArray(value)) {
return value.map(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text));
}
return undefined;
});
}
private _executeFormatRangeProvider(resource: URI, range: types.Range, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
const args = {
resource,
range: typeConverters.fromRange(range),
options
};
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatRangeProvider', args).then(value => {
if (Array.isArray(value)) {
return value.map(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text));
}
return undefined;
});
}
private _executeFormatOnTypeProvider(resource: URI, position: types.Position, ch: string, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
const args = {
resource,
position: typeConverters.fromPosition(position),
ch,
options
};
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatOnTypeProvider', args).then(value => {
if (Array.isArray(value)) {
return value.map(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text));
}
return undefined;
});
}
private _executeDocumentLinkProvider(resource: URI): Thenable<vscode.DocumentLink[]> {
return this._commands.executeCommand<modes.ILink[]>('_executeLinkProvider', resource).then(value => {
if (Array.isArray(value)) {
return value.map(typeConverters.DocumentLink.to);
}
return undefined;
});
}
}

View File

@@ -0,0 +1,224 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { validateConstraint } from 'vs/base/common/types';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { TPromise } from 'vs/base/common/winjs.base';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import * as extHostTypeConverter from 'vs/workbench/api/node/extHostTypeConverters';
import { cloneAndChange } from 'vs/base/common/objects';
import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ObjectIdentifier, IMainContext } from './extHost.protocol';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import * as modes from 'vs/editor/common/modes';
import * as vscode from 'vscode';
interface CommandHandler {
callback: Function;
thisArg: any;
description: ICommandHandlerDescription;
}
export interface ArgumentProcessor {
processArgument(arg: any): any;
}
export class ExtHostCommands implements ExtHostCommandsShape {
private _commands = new Map<string, CommandHandler>();
private _proxy: MainThreadCommandsShape;
private _converter: CommandsConverter;
private _argumentProcessors: ArgumentProcessor[] = [];
constructor(
mainContext: IMainContext,
heapService: ExtHostHeapService
) {
this._proxy = mainContext.get(MainContext.MainThreadCommands);
this._converter = new CommandsConverter(this, heapService);
}
get converter(): CommandsConverter {
return this._converter;
}
registerArgumentProcessor(processor: ArgumentProcessor): void {
this._argumentProcessors.push(processor);
}
registerCommand(id: string, callback: <T>(...args: any[]) => T | Thenable<T>, thisArg?: any, description?: ICommandHandlerDescription): extHostTypes.Disposable {
if (!id.trim().length) {
throw new Error('invalid id');
}
if (this._commands.has(id)) {
throw new Error(`command '${id}' already exists`);
}
this._commands.set(id, { callback, thisArg, description });
this._proxy.$registerCommand(id);
return new extHostTypes.Disposable(() => {
if (this._commands.delete(id)) {
this._proxy.$unregisterCommand(id);
}
});
}
executeCommand<T>(id: string, ...args: any[]): Thenable<T> {
if (this._commands.has(id)) {
// we stay inside the extension host and support
// to pass any kind of parameters around
return this.$executeContributedCommand<T>(id, ...args);
} else {
// automagically convert some argument types
args = cloneAndChange(args, function (value) {
if (value instanceof extHostTypes.Position) {
return extHostTypeConverter.fromPosition(value);
}
if (value instanceof extHostTypes.Range) {
return extHostTypeConverter.fromRange(value);
}
if (value instanceof extHostTypes.Location) {
return extHostTypeConverter.location.from(value);
}
if (!Array.isArray(value)) {
return value;
}
});
return this._proxy.$executeCommand<T>(id, args);
}
}
$executeContributedCommand<T>(id: string, ...args: any[]): Thenable<T> {
let command = this._commands.get(id);
if (!command) {
return TPromise.wrapError<T>(new Error(`Contributed command '${id}' does not exist.`));
}
let { callback, thisArg, description } = command;
if (description) {
for (let i = 0; i < description.args.length; i++) {
try {
validateConstraint(args[i], description.args[i].constraint);
} catch (err) {
return TPromise.wrapError<T>(new Error(`Running the contributed command:'${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`));
}
}
}
args = args.map(arg => this._argumentProcessors.reduce((r, p) => p.processArgument(r), arg));
try {
let result = callback.apply(thisArg, args);
return TPromise.as(result);
} catch (err) {
// console.log(err);
// try {
// console.log(toErrorMessage(err));
// } catch (err) {
// //
// }
return TPromise.wrapError<T>(new Error(`Running the contributed command:'${id}' failed.`));
}
}
getCommands(filterUnderscoreCommands: boolean = false): Thenable<string[]> {
return this._proxy.$getCommands().then(result => {
if (filterUnderscoreCommands) {
result = result.filter(command => command[0] !== '_');
}
return result;
});
}
$getContributedCommandHandlerDescriptions(): TPromise<{ [id: string]: string | ICommandHandlerDescription }> {
const result: { [id: string]: string | ICommandHandlerDescription } = Object.create(null);
this._commands.forEach((command, id) => {
let { description } = command;
if (description) {
result[id] = description;
}
});
return TPromise.as(result);
}
}
export class CommandsConverter {
private _commands: ExtHostCommands;
private _heap: ExtHostHeapService;
// --- conversion between internal and api commands
constructor(commands: ExtHostCommands, heap: ExtHostHeapService) {
this._commands = commands;
this._heap = heap;
this._commands.registerCommand('_internal_command_delegation', this._executeConvertedCommand, this);
}
toInternal(command: vscode.Command): modes.Command {
if (!command) {
return undefined;
}
const result: modes.Command = {
id: command.command,
title: command.title
};
if (command.command && !isFalsyOrEmpty(command.arguments)) {
// we have a contributed command with arguments. that
// means we don't want to send the arguments around
const id = this._heap.keep(command);
ObjectIdentifier.mixin(result, id);
result.id = '_internal_command_delegation';
result.arguments = [id];
}
if (command.tooltip) {
result.tooltip = command.tooltip;
}
return result;
}
fromInternal(command: modes.Command): vscode.Command {
if (!command) {
return undefined;
}
const id = ObjectIdentifier.of(command);
if (typeof id === 'number') {
return this._heap.get<vscode.Command>(id);
} else {
return {
command: command.id,
title: command.title,
arguments: command.arguments
};
}
}
private _executeConvertedCommand<R>(...args: any[]): Thenable<R> {
const actualCmd = this._heap.get<vscode.Command>(args[0]);
return this._commands.executeCommand(actualCmd.command, ...actualCmd.arguments);
}
}

View File

@@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { mixin } from 'vs/base/common/objects';
import URI from 'vs/base/common/uri';
import Event, { Emitter } from 'vs/base/common/event';
import { WorkspaceConfiguration } from 'vscode';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostConfigurationShape, MainThreadConfigurationShape } from './extHost.protocol';
import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes';
import { IConfigurationData, Configuration } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
function lookUp(tree: any, key: string) {
if (key) {
const parts = key.split('.');
let node = tree;
for (let i = 0; node && i < parts.length; i++) {
node = node[parts[i]];
}
return node;
}
}
type ConfigurationInspect<T> = {
key: string;
defaultValue?: T;
globalValue?: T;
workspaceValue?: T;
workspaceFolderValue?: T;
};
export class ExtHostConfiguration implements ExtHostConfigurationShape {
private readonly _onDidChangeConfiguration = new Emitter<void>();
private readonly _proxy: MainThreadConfigurationShape;
private readonly _extHostWorkspace: ExtHostWorkspace;
private _configuration: Configuration<any>;
constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationData<any>) {
this._proxy = proxy;
this._extHostWorkspace = extHostWorkspace;
this._configuration = Configuration.parse(data, extHostWorkspace.workspace);
}
get onDidChangeConfiguration(): Event<void> {
return this._onDidChangeConfiguration && this._onDidChangeConfiguration.event;
}
$acceptConfigurationChanged(data: IConfigurationData<any>) {
this._configuration = Configuration.parse(data, this._extHostWorkspace.workspace);
this._onDidChangeConfiguration.fire(undefined);
}
getConfiguration(section?: string, resource?: URI): WorkspaceConfiguration {
const config = section
? lookUp(this._configuration.getValue(null, { resource }), section)
: this._configuration.getValue(null, { resource });
function parseConfigurationTarget(arg: boolean | ExtHostConfigurationTarget): ConfigurationTarget {
if (arg === void 0 || arg === null) {
return null;
}
if (typeof arg === 'boolean') {
return arg ? ConfigurationTarget.USER : ConfigurationTarget.WORKSPACE;
}
switch (arg) {
case ExtHostConfigurationTarget.Global: return ConfigurationTarget.USER;
case ExtHostConfigurationTarget.Workspace: return ConfigurationTarget.WORKSPACE;
case ExtHostConfigurationTarget.WorkspaceFolder: return ConfigurationTarget.FOLDER;
}
}
const result: WorkspaceConfiguration = {
has(key: string): boolean {
return typeof lookUp(config, key) !== 'undefined';
},
get<T>(key: string, defaultValue?: T): T {
let result = lookUp(config, key);
if (typeof result === 'undefined') {
result = defaultValue;
}
return result;
},
update: (key: string, value: any, arg: ExtHostConfigurationTarget | boolean) => {
key = section ? `${section}.${key}` : key;
const target = parseConfigurationTarget(arg);
if (value !== void 0) {
return this._proxy.$updateConfigurationOption(target, key, value, resource);
} else {
return this._proxy.$removeConfigurationOption(target, key, resource);
}
},
inspect: <T>(key: string): ConfigurationInspect<T> => {
key = section ? `${section}.${key}` : key;
const config = this._configuration.lookup<T>(key, { resource });
if (config) {
return {
key,
defaultValue: config.default,
globalValue: config.user,
workspaceValue: config.workspace,
workspaceFolderValue: config.folder
};
}
return undefined;
}
};
if (typeof config === 'object') {
mixin(result, config, false);
}
return <WorkspaceConfiguration>Object.freeze(result);
}
}

View File

@@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { MainContext, MainThreadCredentialsShape, ExtHostCredentialsShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
export class ExtHostCredentials implements ExtHostCredentialsShape {
private _proxy: MainThreadCredentialsShape;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.get(MainContext.MainThreadCredentials);
};
readSecret(service: string, account: string): Thenable<string | undefined> {
return this._proxy.$readSecret(service, account);
}
writeSecret(service: string, account: string, secret: string): Thenable<void> {
return this._proxy.$writeSecret(service, account, secret);
}
deleteSecret(service: string, account: string): Thenable<boolean> {
return this._proxy.$deleteSecret(service, account);
}
}

View File

@@ -0,0 +1,213 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
// {{SQL CARBON EDIT}}
/*
import { TPromise } from 'vs/base/common/winjs.base';
import Event, { Emitter } from 'vs/base/common/event';
import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import * as vscode from 'vscode';
import URI from 'vs/base/common/uri';
import * as types from 'vs/workbench/api/node/extHostTypes';
export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _workspace: ExtHostWorkspace;
private _handleCounter: number;
private _handlers: Map<number, vscode.DebugConfigurationProvider>;
private _debugServiceProxy: MainThreadDebugServiceShape;
private _debugSessions: Map<DebugSessionUUID, ExtHostDebugSession> = new Map<DebugSessionUUID, ExtHostDebugSession>();
private _onDidStartDebugSession: Emitter<vscode.DebugSession>;
get onDidStartDebugSession(): Event<vscode.DebugSession> { return this._onDidStartDebugSession.event; }
private _onDidTerminateDebugSession: Emitter<vscode.DebugSession>;
get onDidTerminateDebugSession(): Event<vscode.DebugSession> { return this._onDidTerminateDebugSession.event; }
private _onDidChangeActiveDebugSession: Emitter<vscode.DebugSession | undefined>;
get onDidChangeActiveDebugSession(): Event<vscode.DebugSession | undefined> { return this._onDidChangeActiveDebugSession.event; }
private _activeDebugSession: ExtHostDebugSession | undefined;
get activeDebugSession(): ExtHostDebugSession | undefined { return this._activeDebugSession; }
private _onDidReceiveDebugSessionCustomEvent: Emitter<vscode.DebugSessionCustomEvent>;
get onDidReceiveDebugSessionCustomEvent(): Event<vscode.DebugSessionCustomEvent> { return this._onDidReceiveDebugSessionCustomEvent.event; }
constructor(mainContext: IMainContext, workspace: ExtHostWorkspace) {
this._workspace = workspace;
this._handleCounter = 0;
this._handlers = new Map<number, vscode.DebugConfigurationProvider>();
this._onDidStartDebugSession = new Emitter<vscode.DebugSession>();
this._onDidTerminateDebugSession = new Emitter<vscode.DebugSession>();
this._onDidChangeActiveDebugSession = new Emitter<vscode.DebugSession>();
this._onDidReceiveDebugSessionCustomEvent = new Emitter<vscode.DebugSessionCustomEvent>();
this._debugServiceProxy = mainContext.get(MainContext.MainThreadDebugService);
}
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable {
if (!provider) {
return new types.Disposable(() => { });
}
let handle = this.nextHandle();
this._handlers.set(handle, provider);
this._debugServiceProxy.$registerDebugConfigurationProvider(type, !!provider.provideDebugConfigurations, !!provider.resolveDebugConfiguration, handle);
return new types.Disposable(() => {
this._handlers.delete(handle);
this._debugServiceProxy.$unregisterDebugConfigurationProvider(handle);
});
}
public $provideDebugConfigurations(handle: number, folderUri: URI | undefined): TPromise<vscode.DebugConfiguration[]> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('no handler found'));
}
if (!handler.provideDebugConfigurations) {
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('handler has no method provideDebugConfigurations'));
}
return asWinJsPromise(token => handler.provideDebugConfigurations(this.getFolder(folderUri), token));
}
public $resolveDebugConfiguration(handle: number, folderUri: URI | undefined, debugConfiguration: vscode.DebugConfiguration): TPromise<vscode.DebugConfiguration> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<vscode.DebugConfiguration>(new Error('no handler found'));
}
if (!handler.resolveDebugConfiguration) {
return TPromise.wrapError<vscode.DebugConfiguration>(new Error('handler has no method resolveDebugConfiguration'));
}
return asWinJsPromise(token => handler.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, token));
}
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise<boolean> {
return this._debugServiceProxy.$startDebugging(folder ? <URI>folder.uri : undefined, nameOrConfig);
}
public startDebugSession(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration): TPromise<vscode.DebugSession> {
return this._debugServiceProxy.$startDebugSession(folder ? <URI>folder.uri : undefined, config).then((id: DebugSessionUUID) => {
const debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, config.type, config.name);
this._debugSessions.set(id, debugSession);
return debugSession;
});
}
public $acceptDebugSessionStarted(id: DebugSessionUUID, type: string, name: string): void {
let debugSession = this._debugSessions.get(id);
if (!debugSession) {
debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
this._debugSessions.set(id, debugSession);
}
this._onDidStartDebugSession.fire(debugSession);
}
public $acceptDebugSessionTerminated(id: DebugSessionUUID, type: string, name: string): void {
let debugSession = this._debugSessions.get(id);
if (!debugSession) {
debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
this._debugSessions.set(id, debugSession);
}
this._onDidTerminateDebugSession.fire(debugSession);
this._debugSessions.delete(id);
}
public $acceptDebugSessionActiveChanged(id: DebugSessionUUID | undefined, type?: string, name?: string): void {
if (id) {
this._activeDebugSession = this._debugSessions.get(id);
if (!this._activeDebugSession) {
this._activeDebugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
this._debugSessions.set(id, this._activeDebugSession);
}
} else {
this._activeDebugSession = undefined;
}
this._onDidChangeActiveDebugSession.fire(this._activeDebugSession);
}
public $acceptDebugSessionCustomEvent(id: DebugSessionUUID, type: string, name: string, event: any): void {
let debugSession = this._debugSessions.get(id);
if (!debugSession) {
debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
this._debugSessions.set(id, debugSession);
}
const ee: vscode.DebugSessionCustomEvent = {
session: debugSession,
event: event.event,
body: event.body
};
this._onDidReceiveDebugSessionCustomEvent.fire(ee);
}
private getFolder(folderUri: URI | undefined) {
if (folderUri) {
const folders = this._workspace.getWorkspaceFolders();
const found = folders.filter(f => f.uri.toString() === folderUri.toString());
if (found && found.length > 0) {
return found[0];
}
}
return undefined;
}
private nextHandle(): number {
return this._handleCounter++;
}
}
export class ExtHostDebugSession implements vscode.DebugSession {
private _debugServiceProxy: MainThreadDebugServiceShape;
private _id: DebugSessionUUID;
private _type: string;
private _name: string;
constructor(proxy: MainThreadDebugServiceShape, id: DebugSessionUUID, type: string, name: string) {
this._debugServiceProxy = proxy;
this._id = id;
this._type = type;
this._name = name;
};
public get id(): string {
return this._id;
}
public get type(): string {
return this._type;
}
public get name(): string {
return this._name;
}
public customRequest(command: string, args: any): Thenable<any> {
return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args);
}
}
// {{SQL CARBON EDIT}}
*/

View File

@@ -0,0 +1,258 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { localize } from 'vs/nls';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import URI from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import * as vscode from 'vscode';
import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol';
import { DiagnosticSeverity } from './extHostTypes';
import { mergeSort } from 'vs/base/common/arrays';
export class DiagnosticCollection implements vscode.DiagnosticCollection {
private static readonly _maxDiagnosticsPerFile: number = 250;
private readonly _name: string;
private _proxy: MainThreadDiagnosticsShape;
private _isDisposed = false;
private _data = new Map<string, vscode.Diagnostic[]>();
constructor(name: string, proxy: MainThreadDiagnosticsShape) {
this._name = name;
this._proxy = proxy;
}
dispose(): void {
if (!this._isDisposed) {
this._proxy.$clear(this.name);
this._proxy = undefined;
this._data = undefined;
this._isDisposed = true;
}
}
get name(): string {
this._checkDisposed();
return this._name;
}
set(uri: vscode.Uri, diagnostics: vscode.Diagnostic[]): void;
set(entries: [vscode.Uri, vscode.Diagnostic[]][]): void;
set(first: vscode.Uri | [vscode.Uri, vscode.Diagnostic[]][], diagnostics?: vscode.Diagnostic[]) {
if (!first) {
// this set-call is a clear-call
this.clear();
return;
}
// the actual implementation for #set
this._checkDisposed();
let toSync: vscode.Uri[];
if (first instanceof URI) {
if (!diagnostics) {
// remove this entry
this.delete(first);
return;
}
// update single row
this._data.set(first.toString(), diagnostics);
toSync = [first];
} else if (Array.isArray(first)) {
// update many rows
toSync = [];
let lastUri: vscode.Uri;
// ensure stable-sort
mergeSort(first, DiagnosticCollection._compareIndexedTuplesByUri);
for (const tuple of first) {
const [uri, diagnostics] = tuple;
if (!lastUri || uri.toString() !== lastUri.toString()) {
if (lastUri && this._data.get(lastUri.toString()).length === 0) {
this._data.delete(lastUri.toString());
}
lastUri = uri;
toSync.push(uri);
this._data.set(uri.toString(), []);
}
if (!diagnostics) {
// [Uri, undefined] means clear this
this._data.get(uri.toString()).length = 0;
} else {
this._data.get(uri.toString()).push(...diagnostics);
}
}
}
// compute change and send to main side
const entries: [URI, IMarkerData[]][] = [];
for (let uri of toSync) {
let marker: IMarkerData[];
let diagnostics = this._data.get(uri.toString());
if (diagnostics) {
// no more than 250 diagnostics per file
if (diagnostics.length > DiagnosticCollection._maxDiagnosticsPerFile) {
marker = [];
const order = [DiagnosticSeverity.Error, DiagnosticSeverity.Warning, DiagnosticSeverity.Information, DiagnosticSeverity.Hint];
orderLoop: for (let i = 0; i < 4; i++) {
for (let diagnostic of diagnostics) {
if (diagnostic.severity === order[i]) {
const len = marker.push(DiagnosticCollection._toMarkerData(diagnostic));
if (len === DiagnosticCollection._maxDiagnosticsPerFile) {
break orderLoop;
}
}
}
}
// add 'signal' marker for showing omitted errors/warnings
marker.push({
severity: Severity.Error,
message: localize({ key: 'limitHit', comment: ['amount of errors/warning skipped due to limits'] }, "Not showing {0} further errors and warnings.", diagnostics.length - DiagnosticCollection._maxDiagnosticsPerFile),
startLineNumber: marker[marker.length - 1].startLineNumber,
startColumn: marker[marker.length - 1].startColumn,
endLineNumber: marker[marker.length - 1].endLineNumber,
endColumn: marker[marker.length - 1].endColumn
});
} else {
marker = diagnostics.map(DiagnosticCollection._toMarkerData);
}
}
entries.push([<URI>uri, marker]);
}
this._proxy.$changeMany(this.name, entries);
}
delete(uri: vscode.Uri): void {
this._checkDisposed();
this._data.delete(uri.toString());
this._proxy.$changeMany(this.name, [[<URI>uri, undefined]]);
}
clear(): void {
this._checkDisposed();
this._data.clear();
this._proxy.$clear(this.name);
}
forEach(callback: (uri: URI, diagnostics: vscode.Diagnostic[], collection: DiagnosticCollection) => any, thisArg?: any): void {
this._checkDisposed();
this._data.forEach((value, key) => {
let uri = URI.parse(key);
callback.apply(thisArg, [uri, this.get(uri), this]);
});
}
get(uri: URI): vscode.Diagnostic[] {
this._checkDisposed();
let result = this._data.get(uri.toString());
if (Array.isArray(result)) {
return <vscode.Diagnostic[]>Object.freeze(result.slice(0));
}
return undefined;
}
has(uri: URI): boolean {
this._checkDisposed();
return Array.isArray(this._data.get(uri.toString()));
}
private _checkDisposed() {
if (this._isDisposed) {
throw new Error('illegal state - object is disposed');
}
}
private static _toMarkerData(diagnostic: vscode.Diagnostic): IMarkerData {
let range = diagnostic.range;
return <IMarkerData>{
startLineNumber: range.start.line + 1,
startColumn: range.start.character + 1,
endLineNumber: range.end.line + 1,
endColumn: range.end.character + 1,
message: diagnostic.message,
source: diagnostic.source,
severity: DiagnosticCollection._convertDiagnosticsSeverity(diagnostic.severity),
code: String(diagnostic.code)
};
}
private static _convertDiagnosticsSeverity(severity: number): Severity {
switch (severity) {
case 0: return Severity.Error;
case 1: return Severity.Warning;
case 2: return Severity.Info;
case 3: return Severity.Ignore;
default: return Severity.Error;
}
}
private static _compareIndexedTuplesByUri(a: [vscode.Uri, vscode.Diagnostic[]], b: [vscode.Uri, vscode.Diagnostic[]]): number {
if (a[0].toString() < b[0].toString()) {
return -1;
} else if (a[0].toString() > b[0].toString()) {
return 1;
} else {
return 0;
}
}
}
export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
private static _idPool: number = 0;
private _proxy: MainThreadDiagnosticsShape;
private _collections: DiagnosticCollection[];
constructor(mainContext: IMainContext) {
this._proxy = mainContext.get(MainContext.MainThreadDiagnostics);
this._collections = [];
}
createDiagnosticCollection(name: string): vscode.DiagnosticCollection {
if (!name) {
name = '_generated_diagnostic_collection_name_#' + ExtHostDiagnostics._idPool++;
}
const { _collections, _proxy } = this;
const result = new class extends DiagnosticCollection {
constructor() {
super(name, _proxy);
_collections.push(this);
}
dispose() {
super.dispose();
let idx = _collections.indexOf(this);
if (idx !== -1) {
_collections.splice(idx, 1);
}
}
};
return result;
}
forEach(callback: (collection: DiagnosticCollection) => any): void {
this._collections.forEach(callback);
}
}

View File

@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import URI from 'vs/base/common/uri';
import { MainContext, MainThreadDiaglogsShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
export class ExtHostDialogs {
private readonly _proxy: MainThreadDiaglogsShape;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.get(MainContext.MainThreadDialogs);
}
showOpenDialog(options: vscode.OpenDialogOptions): Thenable<URI[]> {
return this._proxy.$showOpenDialog(<any>options).then(filepaths => {
return filepaths && filepaths.map(URI.file);
});
}
}

View File

@@ -0,0 +1,88 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { onUnexpectedError } from 'vs/base/common/errors';
import * as editorCommon from 'vs/editor/common/editorCommon';
import URI from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { TPromise } from 'vs/base/common/winjs.base';
import * as vscode from 'vscode';
import { asWinJsPromise } from 'vs/base/common/async';
import { TextSource } from 'vs/editor/common/model/textSource';
import { MainContext, ExtHostDocumentContentProvidersShape, MainThreadDocumentContentProvidersShape, IMainContext } from './extHost.protocol';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
export class ExtHostDocumentContentProvider implements ExtHostDocumentContentProvidersShape {
private static _handlePool = 0;
private readonly _documentContentProviders = new Map<number, vscode.TextDocumentContentProvider>();
private readonly _proxy: MainThreadDocumentContentProvidersShape;
private readonly _documentsAndEditors: ExtHostDocumentsAndEditors;
constructor(mainContext: IMainContext, documentsAndEditors: ExtHostDocumentsAndEditors) {
this._proxy = mainContext.get(MainContext.MainThreadDocumentContentProviders);
this._documentsAndEditors = documentsAndEditors;
}
dispose(): void {
// todo@joh
}
registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider): vscode.Disposable {
if (scheme === 'file' || scheme === 'untitled') {
throw new Error(`scheme '${scheme}' already registered`);
}
const handle = ExtHostDocumentContentProvider._handlePool++;
this._documentContentProviders.set(handle, provider);
this._proxy.$registerTextContentProvider(handle, scheme);
let subscription: IDisposable;
if (typeof provider.onDidChange === 'function') {
subscription = provider.onDidChange(uri => {
if (this._documentsAndEditors.getDocument(uri.toString())) {
this.$provideTextDocumentContent(handle, <URI>uri).then(value => {
const document = this._documentsAndEditors.getDocument(uri.toString());
if (!document) {
// disposed in the meantime
return;
}
// create lines and compare
const textSource = TextSource.fromString(value, editorCommon.DefaultEndOfLine.CRLF);
// broadcast event when content changed
if (!document.equalLines(textSource)) {
return this._proxy.$onVirtualDocumentChange(<URI>uri, textSource);
}
}, onUnexpectedError);
}
});
}
return new Disposable(() => {
if (this._documentContentProviders.delete(handle)) {
this._proxy.$unregisterTextContentProvider(handle);
}
if (subscription) {
subscription.dispose();
subscription = undefined;
}
});
}
$provideTextDocumentContent(handle: number, uri: URI): TPromise<string> {
const provider = this._documentContentProviders.get(handle);
if (!provider) {
return TPromise.wrapError<string>(new Error(`unsupported uri-scheme: ${uri.scheme}`));
}
return asWinJsPromise(token => provider.provideTextDocumentContent(uri, token));
}
}

View File

@@ -0,0 +1,268 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ok } from 'vs/base/common/assert';
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
import { MirrorModel } from 'vs/editor/common/model/mirrorModel';
import URI from 'vs/base/common/uri';
import { Range, Position, EndOfLine } from 'vs/workbench/api/node/extHostTypes';
import * as vscode from 'vscode';
import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper';
import { MainThreadDocumentsShape } from './extHost.protocol';
import { ITextSource } from 'vs/editor/common/model/textSource';
import { TPromise } from 'vs/base/common/winjs.base';
const _modeId2WordDefinition = new Map<string, RegExp>();
export function setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void {
_modeId2WordDefinition.set(modeId, wordDefinition);
}
export function getWordDefinitionFor(modeId: string): RegExp {
return _modeId2WordDefinition.get(modeId);
}
export class ExtHostDocumentData extends MirrorModel {
private _proxy: MainThreadDocumentsShape;
private _languageId: string;
private _isDirty: boolean;
private _document: vscode.TextDocument;
private _textLines: vscode.TextLine[] = [];
private _isDisposed: boolean = false;
constructor(proxy: MainThreadDocumentsShape, uri: URI, lines: string[], eol: string,
languageId: string, versionId: number, isDirty: boolean
) {
super(uri, lines, eol, versionId);
this._proxy = proxy;
this._languageId = languageId;
this._isDirty = isDirty;
}
dispose(): void {
// we don't really dispose documents but let
// extensions still read from them. some
// operations, live saving, will now error tho
ok(!this._isDisposed);
this._isDisposed = true;
this._isDirty = false;
}
equalLines({ lines }: ITextSource): boolean {
const len = lines.length;
if (len !== this._lines.length) {
return false;
}
for (let i = 0; i < len; i++) {
if (lines[i] !== this._lines[i]) {
return false;
}
}
return true;
}
get document(): vscode.TextDocument {
if (!this._document) {
const data = this;
this._document = {
get uri() { return data._uri; },
get fileName() { return data._uri.fsPath; },
get isUntitled() { return data._uri.scheme !== 'file'; },
get languageId() { return data._languageId; },
get version() { return data._versionId; },
get isClosed() { return data._isDisposed; },
get isDirty() { return data._isDirty; },
save() { return data._save(); },
getText(range?) { return range ? data._getTextInRange(range) : data.getText(); },
get eol() { return data._eol === '\n' ? EndOfLine.LF : EndOfLine.CRLF; },
get lineCount() { return data._lines.length; },
lineAt(lineOrPos) { return data._lineAt(lineOrPos); },
offsetAt(pos) { return data._offsetAt(pos); },
positionAt(offset) { return data._positionAt(offset); },
validateRange(ran) { return data._validateRange(ran); },
validatePosition(pos) { return data._validatePosition(pos); },
getWordRangeAtPosition(pos, regexp?) { return data._getWordRangeAtPosition(pos, regexp); }
};
}
return Object.freeze(this._document);
}
_acceptLanguageId(newLanguageId: string): void {
ok(!this._isDisposed);
this._languageId = newLanguageId;
}
_acceptIsDirty(isDirty: boolean): void {
ok(!this._isDisposed);
this._isDirty = isDirty;
}
private _save(): TPromise<boolean> {
if (this._isDisposed) {
return TPromise.wrapError<boolean>(new Error('Document has been closed'));
}
return this._proxy.$trySaveDocument(this._uri);
}
private _getTextInRange(_range: vscode.Range): string {
let range = this._validateRange(_range);
if (range.isEmpty) {
return '';
}
if (range.isSingleLine) {
return this._lines[range.start.line].substring(range.start.character, range.end.character);
}
let lineEnding = this._eol,
startLineIndex = range.start.line,
endLineIndex = range.end.line,
resultLines: string[] = [];
resultLines.push(this._lines[startLineIndex].substring(range.start.character));
for (let i = startLineIndex + 1; i < endLineIndex; i++) {
resultLines.push(this._lines[i]);
}
resultLines.push(this._lines[endLineIndex].substring(0, range.end.character));
return resultLines.join(lineEnding);
}
private _lineAt(lineOrPosition: number | vscode.Position): vscode.TextLine {
let line: number;
if (lineOrPosition instanceof Position) {
line = lineOrPosition.line;
} else if (typeof lineOrPosition === 'number') {
line = lineOrPosition;
}
if (line < 0 || line >= this._lines.length) {
throw new Error('Illegal value for `line`');
}
let result = this._textLines[line];
if (!result || result.lineNumber !== line || result.text !== this._lines[line]) {
const text = this._lines[line];
const firstNonWhitespaceCharacterIndex = /^(\s*)/.exec(text)[1].length;
const range = new Range(line, 0, line, text.length);
const rangeIncludingLineBreak = line < this._lines.length - 1
? new Range(line, 0, line + 1, 0)
: range;
result = Object.freeze({
lineNumber: line,
range,
rangeIncludingLineBreak,
text,
firstNonWhitespaceCharacterIndex, //TODO@api, rename to 'leadingWhitespaceLength'
isEmptyOrWhitespace: firstNonWhitespaceCharacterIndex === text.length
});
this._textLines[line] = result;
}
return result;
}
private _offsetAt(position: vscode.Position): number {
position = this._validatePosition(position);
this._ensureLineStarts();
return this._lineStarts.getAccumulatedValue(position.line - 1) + position.character;
}
private _positionAt(offset: number): vscode.Position {
offset = Math.floor(offset);
offset = Math.max(0, offset);
this._ensureLineStarts();
let out = this._lineStarts.getIndexOf(offset);
let lineLength = this._lines[out.index].length;
// Ensure we return a valid position
return new Position(out.index, Math.min(out.remainder, lineLength));
}
// ---- range math
private _validateRange(range: vscode.Range): vscode.Range {
if (!(range instanceof Range)) {
throw new Error('Invalid argument');
}
let start = this._validatePosition(range.start);
let end = this._validatePosition(range.end);
if (start === range.start && end === range.end) {
return range;
}
return new Range(start.line, start.character, end.line, end.character);
}
private _validatePosition(position: vscode.Position): vscode.Position {
if (!(position instanceof Position)) {
throw new Error('Invalid argument');
}
let { line, character } = position;
let hasChanged = false;
if (line < 0) {
line = 0;
character = 0;
hasChanged = true;
}
else if (line >= this._lines.length) {
line = this._lines.length - 1;
character = this._lines[line].length;
hasChanged = true;
}
else {
let maxCharacter = this._lines[line].length;
if (character < 0) {
character = 0;
hasChanged = true;
}
else if (character > maxCharacter) {
character = maxCharacter;
hasChanged = true;
}
}
if (!hasChanged) {
return position;
}
return new Position(line, character);
}
private _getWordRangeAtPosition(_position: vscode.Position, regexp?: RegExp): vscode.Range {
let position = this._validatePosition(_position);
if (!regexp) {
// use default when custom-regexp isn't provided
regexp = getWordDefinitionFor(this._languageId);
} else if (regExpLeadsToEndlessLoop(regexp)) {
// use default when custom-regexp is bad
console.warn(`[getWordRangeAtPosition]: ignoring custom regexp '${regexp.source}' because it matches the empty string.`);
regexp = getWordDefinitionFor(this._languageId);
}
let wordAtText = getWordAtText(
position.character + 1,
ensureValidWordDefinition(regexp),
this._lines[position.line],
0
);
if (wordAtText) {
return new Range(position.line, wordAtText.startColumn - 1, position.line, wordAtText.endColumn - 1);
}
return undefined;
}
}

View File

@@ -0,0 +1,165 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event from 'vs/base/common/event';
import CallbackList from 'vs/base/common/callbackList';
import URI from 'vs/base/common/uri';
import { sequence, always } from 'vs/base/common/async';
import { illegalState } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { MainThreadWorkspaceShape, ExtHostDocumentSaveParticipantShape } from 'vs/workbench/api/node/extHost.protocol';
import { TextEdit } from 'vs/workbench/api/node/extHostTypes';
import { fromRange, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/node/extHostTypeConverters';
import { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSaveParticipantShape {
private _documents: ExtHostDocuments;
private _workspace: MainThreadWorkspaceShape;
private _callbacks = new CallbackList();
private _badListeners = new WeakMap<Function, number>();
private _thresholds: { timeout: number; errors: number; };
constructor(documents: ExtHostDocuments, workspace: MainThreadWorkspaceShape, thresholds: { timeout: number; errors: number; } = { timeout: 1500, errors: 3 }) {
this._documents = documents;
this._workspace = workspace;
this._thresholds = thresholds;
}
dispose(): void {
this._callbacks.dispose();
}
get onWillSaveTextDocumentEvent(): Event<vscode.TextDocumentWillSaveEvent> {
return (listener, thisArg, disposables) => {
this._callbacks.add(listener, thisArg);
const result = {
dispose: () => {
this._callbacks.remove(listener, thisArg);
}
};
if (Array.isArray(disposables)) {
disposables.push(result);
}
return result;
};
}
$participateInSave(resource: URI, reason: SaveReason): TPromise<boolean[]> {
const entries = this._callbacks.entries();
let didTimeout = false;
let didTimeoutHandle = setTimeout(() => didTimeout = true, this._thresholds.timeout);
const promise = sequence(entries.map(([fn, thisArg]) => {
return () => {
if (didTimeout) {
// timeout - no more listeners
return undefined;
}
const document = this._documents.getDocumentData(resource).document;
return this._deliverEventAsyncAndBlameBadListeners(fn, thisArg, <any>{ document, reason: TextDocumentSaveReason.to(reason) });
};
}));
return always(promise, () => clearTimeout(didTimeoutHandle));
}
private _deliverEventAsyncAndBlameBadListeners(listener: Function, thisArg: any, stubEvent: vscode.TextDocumentWillSaveEvent): TPromise<any> {
const errors = this._badListeners.get(listener);
if (errors > this._thresholds.errors) {
// bad listener - ignore
return TPromise.wrap(false);
}
return this._deliverEventAsync(listener, thisArg, stubEvent).then(() => {
// don't send result across the wire
return true;
}, err => {
if (!(err instanceof Error) || (<Error>err).message !== 'concurrent_edits') {
const errors = this._badListeners.get(listener);
this._badListeners.set(listener, !errors ? 1 : errors + 1);
// todo@joh signal to the listener?
// if (errors === this._thresholds.errors) {
// console.warn('BAD onWillSaveTextDocumentEvent-listener is from now on being ignored');
// }
}
return false;
});
}
private _deliverEventAsync(listener: Function, thisArg: any, stubEvent: vscode.TextDocumentWillSaveEvent): TPromise<any> {
const promises: TPromise<vscode.TextEdit[]>[] = [];
const { document, reason } = stubEvent;
const { version } = document;
const event = Object.freeze(<vscode.TextDocumentWillSaveEvent>{
document,
reason,
waitUntil(p: Thenable<any | vscode.TextEdit[]>) {
if (Object.isFrozen(promises)) {
throw illegalState('waitUntil can not be called async');
}
promises.push(TPromise.wrap(p));
}
});
try {
// fire event
listener.apply(thisArg, [event]);
} catch (err) {
return TPromise.wrapError(err);
}
// freeze promises after event call
Object.freeze(promises);
return new TPromise<vscode.TextEdit[][]>((resolve, reject) => {
// join on all listener promises, reject after timeout
const handle = setTimeout(() => reject(new Error('timeout')), this._thresholds.timeout);
return always(TPromise.join(promises), () => clearTimeout(handle)).then(resolve, reject);
}).then(values => {
let edits: IResourceEdit[] = [];
for (const value of values) {
if (Array.isArray(value) && (<vscode.TextEdit[]>value).every(e => e instanceof TextEdit)) {
for (const { newText, newEol, range } of value) {
edits.push({
resource: <URI>document.uri,
range: range && fromRange(range),
newText,
newEol: EndOfLine.from(newEol)
});
}
}
}
// apply edits if any and if document
// didn't change somehow in the meantime
if (edits.length === 0) {
return undefined;
}
if (version === document.version) {
return this._workspace.$applyWorkspaceEdit(edits);
}
// TODO@joh bubble this to listener?
return TPromise.wrapError(new Error('concurrent_edits'));
});
}
}

View File

@@ -0,0 +1,142 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as TypeConverters from './extHostTypeConverters';
import { TPromise } from 'vs/base/common/winjs.base';
import * as vscode from 'vscode';
import { MainContext, MainThreadDocumentsShape, ExtHostDocumentsShape, IMainContext } from './extHost.protocol';
import { ExtHostDocumentData, setWordDefinitionFor } from './extHostDocumentData';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorModel';
export class ExtHostDocuments implements ExtHostDocumentsShape {
private _onDidAddDocument = new Emitter<vscode.TextDocument>();
private _onDidRemoveDocument = new Emitter<vscode.TextDocument>();
private _onDidChangeDocument = new Emitter<vscode.TextDocumentChangeEvent>();
private _onDidSaveDocument = new Emitter<vscode.TextDocument>();
readonly onDidAddDocument: Event<vscode.TextDocument> = this._onDidAddDocument.event;
readonly onDidRemoveDocument: Event<vscode.TextDocument> = this._onDidRemoveDocument.event;
readonly onDidChangeDocument: Event<vscode.TextDocumentChangeEvent> = this._onDidChangeDocument.event;
readonly onDidSaveDocument: Event<vscode.TextDocument> = this._onDidSaveDocument.event;
private _toDispose: IDisposable[];
private _proxy: MainThreadDocumentsShape;
private _documentsAndEditors: ExtHostDocumentsAndEditors;
private _documentLoader = new Map<string, TPromise<ExtHostDocumentData>>();
constructor(mainContext: IMainContext, documentsAndEditors: ExtHostDocumentsAndEditors) {
this._proxy = mainContext.get(MainContext.MainThreadDocuments);
this._documentsAndEditors = documentsAndEditors;
this._toDispose = [
this._documentsAndEditors.onDidRemoveDocuments(documents => {
for (const data of documents) {
this._onDidRemoveDocument.fire(data.document);
}
}),
this._documentsAndEditors.onDidAddDocuments(documents => {
for (const data of documents) {
this._onDidAddDocument.fire(data.document);
}
})
];
}
public dispose(): void {
dispose(this._toDispose);
}
public getAllDocumentData(): ExtHostDocumentData[] {
return this._documentsAndEditors.allDocuments();
}
public getDocumentData(resource: vscode.Uri): ExtHostDocumentData {
if (!resource) {
return undefined;
}
const data = this._documentsAndEditors.getDocument(resource.toString());
if (data) {
return data;
}
return undefined;
}
public ensureDocumentData(uri: URI): TPromise<ExtHostDocumentData> {
let cached = this._documentsAndEditors.getDocument(uri.toString());
if (cached) {
return TPromise.as(cached);
}
let promise = this._documentLoader.get(uri.toString());
if (!promise) {
promise = this._proxy.$tryOpenDocument(uri).then(() => {
this._documentLoader.delete(uri.toString());
return this._documentsAndEditors.getDocument(uri.toString());
}, err => {
this._documentLoader.delete(uri.toString());
return TPromise.wrapError<ExtHostDocumentData>(err);
});
this._documentLoader.set(uri.toString(), promise);
}
return promise;
}
public createDocumentData(options?: { language?: string; content?: string }): TPromise<URI> {
return this._proxy.$tryCreateDocument(options);
}
public $acceptModelModeChanged(strURL: string, oldModeId: string, newModeId: string): void {
let data = this._documentsAndEditors.getDocument(strURL);
// Treat a mode change as a remove + add
this._onDidRemoveDocument.fire(data.document);
data._acceptLanguageId(newModeId);
this._onDidAddDocument.fire(data.document);
}
public $acceptModelSaved(strURL: string): void {
let data = this._documentsAndEditors.getDocument(strURL);
this.$acceptDirtyStateChanged(strURL, false);
this._onDidSaveDocument.fire(data.document);
}
public $acceptDirtyStateChanged(strURL: string, isDirty: boolean): void {
let data = this._documentsAndEditors.getDocument(strURL);
data._acceptIsDirty(isDirty);
this._onDidChangeDocument.fire({
document: data.document,
contentChanges: []
});
}
public $acceptModelChanged(strURL: string, events: IModelChangedEvent, isDirty: boolean): void {
let data = this._documentsAndEditors.getDocument(strURL);
data._acceptIsDirty(isDirty);
data.onEvents(events);
this._onDidChangeDocument.fire({
document: data.document,
contentChanges: events.changes.map((change) => {
return {
range: TypeConverters.toRange(change.range),
rangeLength: change.rangeLength,
text: change.text
};
})
});
}
public setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void {
setWordDefinitionFor(modeId, wordDefinition);
}
}

View File

@@ -0,0 +1,149 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
import { dispose } from 'vs/base/common/lifecycle';
import { MainContext, ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IMainContext } from './extHost.protocol';
import { ExtHostDocumentData } from './extHostDocumentData';
import { ExtHostTextEditor, ExtHostTextEditor2 } from './extHostTextEditor';
import * as assert from 'assert';
import * as typeConverters from './extHostTypeConverters';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape {
private _activeEditorId: string;
private readonly _editors = new Map<string, ExtHostTextEditor>();
private readonly _documents = new Map<string, ExtHostDocumentData>();
private readonly _onDidAddDocuments = new Emitter<ExtHostDocumentData[]>();
private readonly _onDidRemoveDocuments = new Emitter<ExtHostDocumentData[]>();
private readonly _onDidChangeVisibleTextEditors = new Emitter<ExtHostTextEditor[]>();
private readonly _onDidChangeActiveTextEditor = new Emitter<ExtHostTextEditor>();
readonly onDidAddDocuments: Event<ExtHostDocumentData[]> = this._onDidAddDocuments.event;
readonly onDidRemoveDocuments: Event<ExtHostDocumentData[]> = this._onDidRemoveDocuments.event;
readonly onDidChangeVisibleTextEditors: Event<ExtHostTextEditor[]> = this._onDidChangeVisibleTextEditors.event;
readonly onDidChangeActiveTextEditor: Event<ExtHostTextEditor> = this._onDidChangeActiveTextEditor.event;
constructor(
private readonly _mainContext: IMainContext,
private readonly _extHostExtensions?: ExtHostExtensionService
) {
}
$acceptDocumentsAndEditorsDelta(delta: IDocumentsAndEditorsDelta): void {
const removedDocuments: ExtHostDocumentData[] = [];
const addedDocuments: ExtHostDocumentData[] = [];
const removedEditors: ExtHostTextEditor[] = [];
if (delta.removedDocuments) {
for (const id of delta.removedDocuments) {
const data = this._documents.get(id);
this._documents.delete(id);
removedDocuments.push(data);
}
}
if (delta.addedDocuments) {
for (const data of delta.addedDocuments) {
assert.ok(!this._documents.has(data.url.toString()), `document '${data.url} already exists!'`);
const documentData = new ExtHostDocumentData(
this._mainContext.get(MainContext.MainThreadDocuments),
data.url,
data.lines,
data.EOL,
data.modeId,
data.versionId,
data.isDirty
);
this._documents.set(data.url.toString(), documentData);
addedDocuments.push(documentData);
}
}
if (delta.removedEditors) {
for (const id of delta.removedEditors) {
const editor = this._editors.get(id);
this._editors.delete(id);
removedEditors.push(editor);
}
}
if (delta.addedEditors) {
for (const data of delta.addedEditors) {
assert.ok(this._documents.has(data.document.toString()), `document '${data.document}' does not exist`);
assert.ok(!this._editors.has(data.id), `editor '${data.id}' already exists!`);
const documentData = this._documents.get(data.document.toString());
const editor = new ExtHostTextEditor2(
this._extHostExtensions,
this._mainContext.get(MainContext.MainThreadTelemetry),
this._mainContext.get(MainContext.MainThreadEditors),
data.id,
documentData,
data.selections.map(typeConverters.toSelection),
data.options,
typeConverters.toViewColumn(data.editorPosition)
);
this._editors.set(data.id, editor);
}
}
if (delta.newActiveEditor !== undefined) {
assert.ok(delta.newActiveEditor === null || this._editors.has(delta.newActiveEditor), `active editor '${delta.newActiveEditor}' does not exist`);
this._activeEditorId = delta.newActiveEditor;
}
dispose(removedDocuments);
dispose(removedEditors);
// now that the internal state is complete, fire events
if (delta.removedDocuments) {
this._onDidRemoveDocuments.fire(removedDocuments);
}
if (delta.addedDocuments) {
this._onDidAddDocuments.fire(addedDocuments);
}
if (delta.removedEditors || delta.addedEditors) {
this._onDidChangeVisibleTextEditors.fire(this.allEditors());
}
if (delta.newActiveEditor !== undefined) {
this._onDidChangeActiveTextEditor.fire(this.activeEditor());
}
}
getDocument(strUrl: string): ExtHostDocumentData {
return this._documents.get(strUrl);
}
allDocuments(): ExtHostDocumentData[] {
const result: ExtHostDocumentData[] = [];
this._documents.forEach(data => result.push(data));
return result;
}
getEditor(id: string): ExtHostTextEditor {
return this._editors.get(id);
}
activeEditor(): ExtHostTextEditor | undefined {
if (!this._activeEditorId) {
return undefined;
} else {
return this._editors.get(this._activeEditorId);
}
}
allEditors(): ExtHostTextEditor[] {
const result: ExtHostTextEditor[] = [];
this._editors.forEach(data => result.push(data));
return result;
}
}

View File

@@ -0,0 +1,327 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = TPromise.as<void>(void 0);
export interface IExtensionMemento {
get<T>(key: string, defaultValue: T): T;
update(key: string, value: any): Thenable<boolean>;
}
export interface IExtensionContext {
subscriptions: IDisposable[];
workspaceState: IExtensionMemento;
globalState: IExtensionMemento;
extensionPath: string;
storagePath: string;
asAbsolutePath(relativePath: string): string;
}
/**
* Represents the source code (module) of an extension.
*/
export interface IExtensionModule {
activate(ctx: IExtensionContext): TPromise<IExtensionAPI>;
deactivate(): void;
}
/**
* Represents the API of an extension (return value of `activate`).
*/
export interface IExtensionAPI {
// _extensionAPIBrand: any;
}
export class ExtensionActivationTimes {
public static NONE = new ExtensionActivationTimes(false, -1, -1, -1);
public readonly startup: boolean;
public readonly codeLoadingTime: number;
public readonly activateCallTime: number;
public readonly activateResolvedTime: number;
constructor(startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number) {
this.startup = startup;
this.codeLoadingTime = codeLoadingTime;
this.activateCallTime = activateCallTime;
this.activateResolvedTime = activateResolvedTime;
}
}
export class ExtensionActivationTimesBuilder {
private readonly _startup: boolean;
private _codeLoadingStart: number;
private _codeLoadingStop: number;
private _activateCallStart: number;
private _activateCallStop: number;
private _activateResolveStart: number;
private _activateResolveStop: number;
constructor(startup: boolean) {
this._startup = startup;
this._codeLoadingStart = -1;
this._codeLoadingStop = -1;
this._activateCallStart = -1;
this._activateCallStop = -1;
this._activateResolveStart = -1;
this._activateResolveStop = -1;
}
private _delta(start: number, stop: number): number {
if (start === -1 || stop === -1) {
return -1;
}
return stop - start;
}
public build(): ExtensionActivationTimes {
return new ExtensionActivationTimes(
this._startup,
this._delta(this._codeLoadingStart, this._codeLoadingStop),
this._delta(this._activateCallStart, this._activateCallStop),
this._delta(this._activateResolveStart, this._activateResolveStop)
);
}
public codeLoadingStart(): void {
this._codeLoadingStart = Date.now();
}
public codeLoadingStop(): void {
this._codeLoadingStop = Date.now();
}
public activateCallStart(): void {
this._activateCallStart = Date.now();
}
public activateCallStop(): void {
this._activateCallStop = Date.now();
}
public activateResolveStart(): void {
this._activateResolveStart = Date.now();
}
public activateResolveStop(): void {
this._activateResolveStop = Date.now();
}
}
export class ActivatedExtension {
public readonly activationFailed: boolean;
public readonly activationTimes: ExtensionActivationTimes;
public readonly module: IExtensionModule;
public readonly exports: IExtensionAPI;
public readonly subscriptions: IDisposable[];
constructor(
activationFailed: boolean,
activationTimes: ExtensionActivationTimes,
module: IExtensionModule,
exports: IExtensionAPI,
subscriptions: IDisposable[]
) {
this.activationFailed = activationFailed;
this.activationTimes = activationTimes;
this.module = module;
this.exports = exports;
this.subscriptions = subscriptions;
}
}
export class EmptyExtension extends ActivatedExtension {
constructor(activationTimes: ExtensionActivationTimes) {
super(false, activationTimes, { activate: undefined, deactivate: undefined }, undefined, []);
}
}
export class FailedExtension extends ActivatedExtension {
constructor(activationTimes: ExtensionActivationTimes) {
super(true, activationTimes, { activate: undefined, deactivate: undefined }, undefined, []);
}
}
export interface IExtensionsActivatorHost {
showMessage(severity: Severity, message: string): void;
actualActivateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension>;
}
export class ExtensionsActivator {
private readonly _registry: ExtensionDescriptionRegistry;
private readonly _host: IExtensionsActivatorHost;
private readonly _activatingExtensions: { [extensionId: string]: TPromise<void>; };
private readonly _activatedExtensions: { [extensionId: string]: ActivatedExtension; };
/**
* A map of already activated events to speed things up if the same activation event is triggered multiple times.
*/
private readonly _alreadyActivatedEvents: { [activationEvent: string]: boolean; };
constructor(registry: ExtensionDescriptionRegistry, host: IExtensionsActivatorHost) {
this._registry = registry;
this._host = host;
this._activatingExtensions = {};
this._activatedExtensions = {};
this._alreadyActivatedEvents = Object.create(null);
}
public isActivated(extensionId: string): boolean {
return hasOwnProperty.call(this._activatedExtensions, extensionId);
}
public getActivatedExtension(extensionId: string): ActivatedExtension {
if (!hasOwnProperty.call(this._activatedExtensions, extensionId)) {
throw new Error('Extension `' + extensionId + '` is not known or not activated');
}
return this._activatedExtensions[extensionId];
}
public activateByEvent(activationEvent: string, startup: boolean): TPromise<void> {
if (this._alreadyActivatedEvents[activationEvent]) {
return NO_OP_VOID_PROMISE;
}
let activateExtensions = this._registry.getExtensionDescriptionsForActivationEvent(activationEvent);
return this._activateExtensions(activateExtensions, startup, 0).then(() => {
this._alreadyActivatedEvents[activationEvent] = true;
});
}
public activateById(extensionId: string, startup: boolean): TPromise<void> {
let desc = this._registry.getExtensionDescription(extensionId);
if (!desc) {
throw new Error('Extension `' + extensionId + '` is not known');
}
return this._activateExtensions([desc], startup, 0);
}
/**
* Handle semantics related to dependencies for `currentExtension`.
* semantics: `redExtensions` must wait for `greenExtensions`.
*/
private _handleActivateRequest(currentExtension: IExtensionDescription, greenExtensions: { [id: string]: IExtensionDescription; }, redExtensions: IExtensionDescription[]): void {
let depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies);
let currentExtensionGetsGreenLight = true;
for (let j = 0, lenJ = depIds.length; j < lenJ; j++) {
let depId = depIds[j];
let depDesc = this._registry.getExtensionDescription(depId);
if (!depDesc) {
// Error condition 1: unknown dependency
this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Extension `{1}` failed to activate. Reason: unknown dependency `{0}`.", depId, currentExtension.id));
this._activatedExtensions[currentExtension.id] = new FailedExtension(ExtensionActivationTimes.NONE);
return;
}
if (hasOwnProperty.call(this._activatedExtensions, depId)) {
let dep = this._activatedExtensions[depId];
if (dep.activationFailed) {
// Error condition 2: a dependency has already failed activation
this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Extension `{1}` failed to activate. Reason: dependency `{0}` failed to activate.", depId, currentExtension.id));
this._activatedExtensions[currentExtension.id] = new FailedExtension(ExtensionActivationTimes.NONE);
return;
}
} else {
// must first wait for the dependency to activate
currentExtensionGetsGreenLight = false;
greenExtensions[depId] = depDesc;
}
}
if (currentExtensionGetsGreenLight) {
greenExtensions[currentExtension.id] = currentExtension;
} else {
redExtensions.push(currentExtension);
}
}
private _activateExtensions(extensionDescriptions: IExtensionDescription[], startup: boolean, recursionLevel: number): TPromise<void> {
// console.log(recursionLevel, '_activateExtensions: ', extensionDescriptions.map(p => p.id));
if (extensionDescriptions.length === 0) {
return TPromise.as(void 0);
}
extensionDescriptions = extensionDescriptions.filter((p) => !hasOwnProperty.call(this._activatedExtensions, p.id));
if (extensionDescriptions.length === 0) {
return TPromise.as(void 0);
}
if (recursionLevel > 10) {
// More than 10 dependencies deep => most likely a dependency loop
for (let i = 0, len = extensionDescriptions.length; i < len; i++) {
// Error condition 3: dependency loop
this._host.showMessage(Severity.Error, nls.localize('failedDep2', "Extension `{0}` failed to activate. Reason: more than 10 levels of dependencies (most likely a dependency loop).", extensionDescriptions[i].id));
this._activatedExtensions[extensionDescriptions[i].id] = new FailedExtension(ExtensionActivationTimes.NONE);
}
return TPromise.as(void 0);
}
let greenMap: { [id: string]: IExtensionDescription; } = Object.create(null),
red: IExtensionDescription[] = [];
for (let i = 0, len = extensionDescriptions.length; i < len; i++) {
this._handleActivateRequest(extensionDescriptions[i], greenMap, red);
}
// Make sure no red is also green
for (let i = 0, len = red.length; i < len; i++) {
if (greenMap[red[i].id]) {
delete greenMap[red[i].id];
}
}
let green = Object.keys(greenMap).map(id => greenMap[id]);
// console.log('greenExtensions: ', green.map(p => p.id));
// console.log('redExtensions: ', red.map(p => p.id));
if (red.length === 0) {
// Finally reached only leafs!
return TPromise.join(green.map((p) => this._activateExtension(p, startup))).then(_ => void 0);
}
return this._activateExtensions(green, startup, recursionLevel + 1).then(_ => {
return this._activateExtensions(red, startup, recursionLevel + 1);
});
}
private _activateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<void> {
if (hasOwnProperty.call(this._activatedExtensions, extensionDescription.id)) {
return TPromise.as(void 0);
}
if (hasOwnProperty.call(this._activatingExtensions, extensionDescription.id)) {
return this._activatingExtensions[extensionDescription.id];
}
this._activatingExtensions[extensionDescription.id] = this._host.actualActivateExtension(extensionDescription, startup).then(null, (err) => {
this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension `{0}` failed: {1}.", extensionDescription.id, err.message));
console.error('Activating extension `' + extensionDescription.id + '` failed: ', err.message);
console.log('Here is the error stack: ', err.stack);
// Treat the extension as being empty
return new FailedExtension(ExtensionActivationTimes.NONE);
}).then((x: ActivatedExtension) => {
this._activatedExtensions[extensionDescription.id] = x;
delete this._activatingExtensions[extensionDescription.id];
});
return this._activatingExtensions[extensionDescription.id];
}
}

View File

@@ -0,0 +1,422 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { dispose } from 'vs/base/common/lifecycle';
import { join } from 'path';
import { mkdirp, dirExists } from 'vs/base/node/pfs';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
// {{SQL CARBON EDIT}}
import { createApiFactory, initializeExtensionApi } from 'sql/workbench/api/node/sqlExtHost.api.impl';
import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape } from './extHost.protocol';
import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule, ExtensionActivationTimesBuilder, ExtensionActivationTimes } from 'vs/workbench/api/node/extHostExtensionActivator';
import { Barrier } from 'vs/workbench/services/extensions/node/barrier';
import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService';
import { realpath } from 'fs';
import { TrieMap } from 'vs/base/common/map';
class ExtensionMemento implements IExtensionMemento {
private _id: string;
private _shared: boolean;
private _storage: ExtHostStorage;
private _init: TPromise<ExtensionMemento>;
private _value: { [n: string]: any; };
constructor(id: string, global: boolean, storage: ExtHostStorage) {
this._id = id;
this._shared = global;
this._storage = storage;
this._init = this._storage.getValue(this._shared, this._id, Object.create(null)).then(value => {
this._value = value;
return this;
});
}
get whenReady(): TPromise<ExtensionMemento> {
return this._init;
}
get<T>(key: string, defaultValue: T): T {
let value = this._value[key];
if (typeof value === 'undefined') {
value = defaultValue;
}
return value;
}
update(key: string, value: any): Thenable<boolean> {
this._value[key] = value;
return this._storage
.setValue(this._shared, this._id, this._value)
.then(() => true);
}
}
class ExtensionStoragePath {
private readonly _workspace: IWorkspaceData;
private readonly _environment: IEnvironment;
private readonly _ready: TPromise<string>;
private _value: string;
constructor(workspace: IWorkspaceData, environment: IEnvironment) {
this._workspace = workspace;
this._environment = environment;
this._ready = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
}
get whenReady(): TPromise<any> {
return this._ready;
}
value(extension: IExtensionDescription): string {
if (this._value) {
return join(this._value, extension.id);
}
return undefined;
}
private _getOrCreateWorkspaceStoragePath(): TPromise<string> {
if (!this._workspace) {
return TPromise.as(undefined);
}
const storageName = this._workspace.id;
const storagePath = join(this._environment.appSettingsHome, 'workspaceStorage', storageName);
return dirExists(storagePath).then(exists => {
if (exists) {
return storagePath;
}
return mkdirp(storagePath).then(success => {
return storagePath;
}, err => {
return undefined;
});
});
}
}
export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private readonly _barrier: Barrier;
private readonly _registry: ExtensionDescriptionRegistry;
private readonly _threadService: ExtHostThreadService;
private readonly _mainThreadTelemetry: MainThreadTelemetryShape;
private readonly _storage: ExtHostStorage;
private readonly _storagePath: ExtensionStoragePath;
private readonly _proxy: MainThreadExtensionServiceShape;
private _activator: ExtensionsActivator;
private _extensionPathIndex: TPromise<TrieMap<IExtensionDescription>>;
/**
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
*/
constructor(initData: IInitData, threadService: ExtHostThreadService) {
this._barrier = new Barrier();
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
this._threadService = threadService;
this._mainThreadTelemetry = threadService.get(MainContext.MainThreadTelemetry);
this._storage = new ExtHostStorage(threadService);
this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment);
this._proxy = this._threadService.get(MainContext.MainThreadExtensionService);
this._activator = null;
// initialize API first (i.e. do not release barrier until the API is initialized)
const apiFactory = createApiFactory(initData, threadService, this);
initializeExtensionApi(this, apiFactory).then(() => {
this._activator = new ExtensionsActivator(this._registry, {
showMessage: (severity: Severity, message: string): void => {
this._proxy.$localShowMessage(severity, message);
switch (severity) {
case Severity.Error:
console.error(message);
break;
case Severity.Warning:
console.warn(message);
break;
default:
console.log(message);
}
},
actualActivateExtension: (extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension> => {
return this._activateExtension(extensionDescription, startup);
}
});
this._barrier.open();
});
}
public onExtensionAPIReady(): TPromise<boolean> {
return this._barrier.wait();
}
public isActivated(extensionId: string): boolean {
if (this._barrier.isOpen()) {
return this._activator.isActivated(extensionId);
}
return false;
}
public activateByEvent(activationEvent: string, startup: boolean): TPromise<void> {
if (this._barrier.isOpen()) {
return this._activator.activateByEvent(activationEvent, startup);
} else {
return this._barrier.wait().then(() => this._activator.activateByEvent(activationEvent, startup));
}
}
public activateById(extensionId: string, startup: boolean): TPromise<void> {
if (this._barrier.isOpen()) {
return this._activator.activateById(extensionId, startup);
} else {
return this._barrier.wait().then(() => this._activator.activateById(extensionId, startup));
}
}
public getAllExtensionDescriptions(): IExtensionDescription[] {
return this._registry.getAllExtensionDescriptions();
}
public getExtensionDescription(extensionId: string): IExtensionDescription {
return this._registry.getExtensionDescription(extensionId);
}
public getExtensionExports(extensionId: string): IExtensionAPI {
if (this._barrier.isOpen()) {
return this._activator.getActivatedExtension(extensionId).exports;
} else {
return null;
}
}
// create trie to enable fast 'filename -> extension id' look up
public getExtensionPathIndex(): TPromise<TrieMap<IExtensionDescription>> {
if (!this._extensionPathIndex) {
const trie = new TrieMap<IExtensionDescription>();
const extensions = this.getAllExtensionDescriptions().map(ext => {
if (!ext.main) {
return undefined;
}
return new TPromise((resolve, reject) => {
realpath(ext.extensionFolderPath, (err, path) => {
if (err) {
reject(err);
} else {
trie.insert(path, ext);
resolve(void 0);
}
});
});
});
this._extensionPathIndex = TPromise.join(extensions).then(() => trie);
}
return this._extensionPathIndex;
}
public deactivate(extensionId: string): TPromise<void> {
let result: TPromise<void> = TPromise.as(void 0);
if (!this._barrier.isOpen()) {
return result;
}
if (!this._activator.isActivated(extensionId)) {
return result;
}
let extension = this._activator.getActivatedExtension(extensionId);
if (!extension) {
return result;
}
// call deactivate if available
try {
if (typeof extension.module.deactivate === 'function') {
result = TPromise.wrap(extension.module.deactivate()).then(null, (err) => {
// TODO: Do something with err if this is not the shutdown case
return TPromise.as(void 0);
});
}
} catch (err) {
// TODO: Do something with err if this is not the shutdown case
}
// clean up subscriptions
try {
dispose(extension.subscriptions);
} catch (err) {
// TODO: Do something with err if this is not the shutdown case
}
return result;
}
// --- impl
private _activateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension> {
return this._doActivateExtension(extensionDescription, startup).then((activatedExtension) => {
const activationTimes = activatedExtension.activationTimes;
this._proxy.$onExtensionActivated(extensionDescription.id, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime);
return activatedExtension;
}, (err) => {
this._proxy.$onExtensionActivationFailed(extensionDescription.id);
throw err;
});
}
private _doActivateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension> {
let event = getTelemetryActivationEvent(extensionDescription);
this._mainThreadTelemetry.$publicLog('activatePlugin', event);
if (!extensionDescription.main) {
// Treat the extension as being empty => NOT AN ERROR CASE
return TPromise.as(new EmptyExtension(ExtensionActivationTimes.NONE));
}
const activationTimesBuilder = new ExtensionActivationTimesBuilder(startup);
return TPromise.join<any>([
loadCommonJSModule(extensionDescription.main, activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
return ExtHostExtensionService._callActivate(<IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
}, (errors: any[]) => {
// Avoid failing with an array of errors, fail with a single error
if (errors[0]) {
return TPromise.wrapError<ActivatedExtension>(errors[0]);
}
if (errors[1]) {
return TPromise.wrapError<ActivatedExtension>(errors[1]);
}
return undefined;
});
}
private _loadExtensionContext(extensionDescription: IExtensionDescription): TPromise<IExtensionContext> {
let globalState = new ExtensionMemento(extensionDescription.id, true, this._storage);
let workspaceState = new ExtensionMemento(extensionDescription.id, false, this._storage);
return TPromise.join([
globalState.whenReady,
workspaceState.whenReady,
this._storagePath.whenReady
]).then(() => {
return Object.freeze(<IExtensionContext>{
globalState,
workspaceState,
subscriptions: [],
get extensionPath() { return extensionDescription.extensionFolderPath; },
storagePath: this._storagePath.value(extensionDescription),
asAbsolutePath: (relativePath: string) => { return join(extensionDescription.extensionFolderPath, relativePath); }
});
});
}
private static _callActivate(extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise<ActivatedExtension> {
// Make sure the extension's surface is not undefined
extensionModule = extensionModule || {
activate: undefined,
deactivate: undefined
};
return this._callActivateOptional(extensionModule, context, activationTimesBuilder).then((extensionExports) => {
return new ActivatedExtension(false, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions);
});
}
private static _callActivateOptional(extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise<IExtensionAPI> {
if (typeof extensionModule.activate === 'function') {
try {
activationTimesBuilder.activateCallStart();
const activateResult = extensionModule.activate.apply(global, [context]);
activationTimesBuilder.activateCallStop();
activationTimesBuilder.activateResolveStart();
return TPromise.as(activateResult).then((value) => {
activationTimesBuilder.activateResolveStop();
return value;
});
} catch (err) {
return TPromise.wrapError(err);
}
} else {
// No activate found => the module is the extension's exports
return TPromise.as<IExtensionAPI>(extensionModule);
}
}
// -- called by main thread
public $activateByEvent(activationEvent: string): TPromise<void> {
return this.activateByEvent(activationEvent, false);
}
}
function loadCommonJSModule<T>(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise<T> {
let r: T = null;
activationTimesBuilder.codeLoadingStart();
try {
r = require.__$__nodeRequire<T>(modulePath);
} catch (e) {
return TPromise.wrapError<T>(e);
} finally {
activationTimesBuilder.codeLoadingStop();
}
return TPromise.as(r);
}
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription): any {
let event = {
id: extensionDescription.id,
name: extensionDescription.name,
publisherDisplayName: extensionDescription.publisher,
activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,
isBuiltin: extensionDescription.isBuiltin
};
for (let contribution in extensionDescription.contributes) {
let contributionDetails = extensionDescription.contributes[contribution];
if (!contributionDetails) {
continue;
}
switch (contribution) {
case 'debuggers':
let types = contributionDetails.reduce((p, c) => p ? p + ',' + c['type'] : c['type'], '');
event['contribution.debuggers'] = types;
break;
case 'grammars':
let grammers = contributionDetails.reduce((p, c) => p ? p + ',' + c['language'] : c['language'], '');
event['contribution.grammars'] = grammers;
break;
case 'languages':
let languages = contributionDetails.reduce((p, c) => p ? p + ',' + c['id'] : c['id'], '');
event['contribution.languages'] = languages;
break;
case 'tmSnippets':
let tmSnippets = contributionDetails.reduce((p, c) => p ? p + ',' + c['languageId'] : c['languageId'], '');
event['contribution.tmSnippets'] = tmSnippets;
break;
default:
event[`contribution.${contribution}`] = true;
}
}
return event;
}

View File

@@ -0,0 +1,104 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
import { Disposable } from './extHostTypes';
import { match } from 'vs/base/common/glob';
import { Uri, FileSystemWatcher as _FileSystemWatcher } from 'vscode';
import { FileSystemEvents, ExtHostFileSystemEventServiceShape } from './extHost.protocol';
class FileSystemWatcher implements _FileSystemWatcher {
private _onDidCreate = new Emitter<Uri>();
private _onDidChange = new Emitter<Uri>();
private _onDidDelete = new Emitter<Uri>();
private _disposable: Disposable;
private _config: number;
get ignoreCreateEvents(): boolean {
return Boolean(this._config & 0b001);
}
get ignoreChangeEvents(): boolean {
return Boolean(this._config & 0b010);
}
get ignoreDeleteEvents(): boolean {
return Boolean(this._config & 0b100);
}
constructor(dispatcher: Event<FileSystemEvents>, globPattern: string, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean) {
this._config = 0;
if (ignoreCreateEvents) {
this._config += 0b001;
}
if (ignoreChangeEvents) {
this._config += 0b010;
}
if (ignoreDeleteEvents) {
this._config += 0b100;
}
let subscription = dispatcher(events => {
if (!ignoreCreateEvents) {
for (let created of events.created) {
if (match(globPattern, created.fsPath)) {
this._onDidCreate.fire(created);
}
}
}
if (!ignoreChangeEvents) {
for (let changed of events.changed) {
if (match(globPattern, changed.fsPath)) {
this._onDidChange.fire(changed);
}
}
}
if (!ignoreDeleteEvents) {
for (let deleted of events.deleted) {
if (match(globPattern, deleted.fsPath)) {
this._onDidDelete.fire(deleted);
}
}
}
});
this._disposable = Disposable.from(this._onDidCreate, this._onDidChange, this._onDidDelete, subscription);
}
dispose() {
this._disposable.dispose();
}
get onDidCreate(): Event<Uri> {
return this._onDidCreate.event;
}
get onDidChange(): Event<Uri> {
return this._onDidChange.event;
}
get onDidDelete(): Event<Uri> {
return this._onDidDelete.event;
}
}
export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServiceShape {
private _emitter = new Emitter<FileSystemEvents>();
constructor() {
}
public createFileSystemWatcher(globPattern: string, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): _FileSystemWatcher {
return new FileSystemWatcher(this._emitter.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
}
$onFileEvent(events: FileSystemEvents) {
this._emitter.fire(events);
}
}

View File

@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ExtHostHeapServiceShape } from './extHost.protocol';
export class ExtHostHeapService implements ExtHostHeapServiceShape {
private static _idPool = 0;
private _data = new Map<number, any>();
keep(obj: any): number {
const id = ExtHostHeapService._idPool++;
this._data.set(id, obj);
return id;
}
delete(id: number): boolean {
return this._data.delete(id);
}
get<T>(id: number): T {
return this._data.get(id);
}
$onGarbageCollection(ids: number[]): void {
for (const id of ids) {
this.delete(id);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { MainContext, MainThreadLanguagesShape, IMainContext } from './extHost.protocol';
export class ExtHostLanguages {
private _proxy: MainThreadLanguagesShape;
constructor(
mainContext: IMainContext
) {
this._proxy = mainContext.get(MainContext.MainThreadLanguages);
}
getLanguages(): TPromise<string[]> {
return this._proxy.$getLanguages();
}
}

View File

@@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Severity from 'vs/base/common/severity';
import vscode = require('vscode');
import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
function isMessageItem<T>(item: any): item is vscode.MessageItem {
return item && item.title;
}
export class ExtHostMessageService {
private _proxy: MainThreadMessageServiceShape;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.get(MainContext.MainThreadMessageService);
}
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string, rest: string[]): Thenable<string | undefined>;
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Thenable<vscode.MessageItem | undefined>;
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Thenable<string | vscode.MessageItem | undefined> {
let options: MainThreadMessageOptions = { extension };
let items: (string | vscode.MessageItem)[];
if (typeof optionsOrFirstItem === 'string' || isMessageItem(optionsOrFirstItem)) {
items = [optionsOrFirstItem, ...rest];
} else {
options.modal = optionsOrFirstItem && optionsOrFirstItem.modal;
items = rest;
}
const commands: { title: string; isCloseAffordance: boolean; handle: number; }[] = [];
for (let handle = 0; handle < items.length; handle++) {
let command = items[handle];
if (typeof command === 'string') {
commands.push({ title: command, handle, isCloseAffordance: false });
} else if (typeof command === 'object') {
let { title, isCloseAffordance } = command;
commands.push({ title, isCloseAffordance, handle });
} else {
console.warn('Invalid message item:', command);
}
}
return this._proxy.$showMessage(severity, message, options, commands).then(handle => {
if (typeof handle === 'number') {
return items[handle];
}
return undefined;
});
}
}

View File

@@ -0,0 +1,78 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { MainContext, MainThreadOutputServiceShape, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
export class ExtHostOutputChannel implements vscode.OutputChannel {
private static _idPool = 1;
private _proxy: MainThreadOutputServiceShape;
private _name: string;
private _id: string;
private _disposed: boolean;
constructor(name: string, proxy: MainThreadOutputServiceShape) {
this._name = name;
this._id = 'extension-output-#' + (ExtHostOutputChannel._idPool++);
this._proxy = proxy;
}
get name(): string {
return this._name;
}
dispose(): void {
if (!this._disposed) {
this._proxy.$dispose(this._id, this._name).then(() => {
this._disposed = true;
});
}
}
append(value: string): void {
this._proxy.$append(this._id, this._name, value);
}
appendLine(value: string): void {
this.append(value + '\n');
}
clear(): void {
this._proxy.$clear(this._id, this._name);
}
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
if (typeof columnOrPreserveFocus === 'boolean') {
preserveFocus = columnOrPreserveFocus;
}
this._proxy.$reveal(this._id, this._name, preserveFocus);
}
hide(): void {
this._proxy.$close(this._id);
}
}
export class ExtHostOutputService {
private _proxy: MainThreadOutputServiceShape;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.get(MainContext.MainThreadOutputService);
}
createOutputChannel(name: string): vscode.OutputChannel {
name = name.trim();
if (!name) {
throw new Error('illegal argument `name`. must not be falsy');
} else {
return new ExtHostOutputChannel(name, this._proxy);
}
}
}

View File

@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Progress, ProgressOptions, CancellationToken } from 'vscode';
import { MainThreadProgressShape } from './extHost.protocol';
import { ProgressLocation } from './extHostTypeConverters';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IProgressStep } from 'vs/platform/progress/common/progress';
export class ExtHostProgress {
private _proxy: MainThreadProgressShape;
private _handles: number = 0;
constructor(proxy: MainThreadProgressShape) {
this._proxy = proxy;
}
withProgress<R>(extension: IExtensionDescription, options: ProgressOptions, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Thenable<R> {
const handle = this._handles++;
const { title, location } = options;
this._proxy.$startProgress(handle, { location: ProgressLocation.from(location), title, tooltip: extension.name });
return this._withProgress(handle, task);
}
private _withProgress<R>(handle: number, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Thenable<R> {
const progress = {
report: (p: IProgressStep) => {
this._proxy.$progressReport(handle, p);
}
};
let p: Thenable<R>;
try {
p = task(progress, null);
} catch (err) {
this._proxy.$progressEnd(handle);
throw err;
}
p.then(result => this._proxy.$progressEnd(handle), err => this._proxy.$progressEnd(handle));
return p;
}
}

View File

@@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { wireCancellationToken } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { QuickPickOptions, QuickPickItem, InputBoxOptions } from 'vscode';
import { MainContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, IMainContext } from './extHost.protocol';
export type Item = string | QuickPickItem;
export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
private _proxy: MainThreadQuickOpenShape;
private _onDidSelectItem: (handle: number) => void;
private _validateInput: (input: string) => string;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.get(MainContext.MainThreadQuickOpen);
}
showQuickPick(itemsOrItemsPromise: string[] | Thenable<string[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<string | undefined>;
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<QuickPickItem | undefined>;
showQuickPick(itemsOrItemsPromise: Item[] | Thenable<Item[]>, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable<Item | undefined> {
// clear state from last invocation
this._onDidSelectItem = undefined;
const itemsPromise = <TPromise<Item[]>>TPromise.wrap(itemsOrItemsPromise);
const quickPickWidget = this._proxy.$show({
autoFocus: { autoFocusFirstEntry: true },
placeHolder: options && options.placeHolder,
matchOnDescription: options && options.matchOnDescription,
matchOnDetail: options && options.matchOnDetail,
ignoreFocusLost: options && options.ignoreFocusOut
});
const promise = TPromise.any(<TPromise<number | Item[]>[]>[quickPickWidget, itemsPromise]).then(values => {
if (values.key === '0') {
return undefined;
}
return itemsPromise.then(items => {
let pickItems: MyQuickPickItems[] = [];
for (let handle = 0; handle < items.length; handle++) {
let item = items[handle];
let label: string;
let description: string;
let detail: string;
if (typeof item === 'string') {
label = item;
} else {
label = item.label;
description = item.description;
detail = item.detail;
}
pickItems.push({
label,
description,
handle,
detail
});
}
// handle selection changes
if (options && typeof options.onDidSelectItem === 'function') {
this._onDidSelectItem = (handle) => {
options.onDidSelectItem(items[handle]);
};
}
// show items
this._proxy.$setItems(pickItems);
return quickPickWidget.then(handle => {
if (typeof handle === 'number') {
return items[handle];
}
return undefined;
});
}, (err) => {
this._proxy.$setError(err);
return TPromise.wrapError(err);
});
});
return wireCancellationToken<Item>(token, promise, true);
}
$onItemSelected(handle: number): void {
if (this._onDidSelectItem) {
this._onDidSelectItem(handle);
}
}
// ---- input
showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Thenable<string> {
// global validate fn used in callback below
this._validateInput = options && options.validateInput;
const promise = this._proxy.$input(options, typeof this._validateInput === 'function');
return wireCancellationToken(token, promise, true);
}
$validateInput(input: string): TPromise<string> {
if (this._validateInput) {
return TPromise.as(this._validateInput(input));
}
return undefined;
}
}

View File

@@ -0,0 +1,367 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. 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 } from 'vs/base/common/event';
import { asWinJsPromise } from 'vs/base/common/async';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { MainContext, MainThreadSCMShape, SCMRawResource, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
function getIconPath(decorations: vscode.SourceControlResourceThemableDecorations) {
if (!decorations) {
return undefined;
} else if (typeof decorations.iconPath === 'string') {
return URI.file(decorations.iconPath).toString();
} else if (decorations.iconPath) {
return `${decorations.iconPath}`;
}
return undefined;
}
export class ExtHostSCMInputBox {
private _value: string = '';
get value(): string {
return this._value;
}
set value(value: string) {
this._proxy.$setInputBoxValue(this._sourceControlHandle, value);
this.updateValue(value);
}
private _onDidChange = new Emitter<string>();
get onDidChange(): Event<string> {
return this._onDidChange.event;
}
constructor(private _proxy: MainThreadSCMShape, private _sourceControlHandle: number) {
// noop
}
$onInputBoxValueChange(value: string): void {
this.updateValue(value);
}
private updateValue(value: string): void {
this._value = value;
this._onDidChange.fire(value);
}
}
class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceGroup {
private static _handlePool: number = 0;
private _resourceHandlePool: number = 0;
private _resourceStates: vscode.SourceControlResourceState[] = [];
private _resourceStatesRollingDisposables: { (): void }[] = [];
private _resourceStatesMap: Map<ResourceStateHandle, vscode.SourceControlResourceState> = new Map<ResourceStateHandle, vscode.SourceControlResourceState>();
get id(): string {
return this._id;
}
get label(): string {
return this._label;
}
set label(label: string) {
this._label = label;
this._proxy.$updateGroupLabel(this._sourceControlHandle, this._handle, label);
}
private _hideWhenEmpty: boolean | undefined = undefined;
get hideWhenEmpty(): boolean | undefined {
return this._hideWhenEmpty;
}
set hideWhenEmpty(hideWhenEmpty: boolean | undefined) {
this._hideWhenEmpty = hideWhenEmpty;
this._proxy.$updateGroup(this._sourceControlHandle, this._handle, { hideWhenEmpty });
}
get resourceStates(): vscode.SourceControlResourceState[] {
return [...this._resourceStates];
}
set resourceStates(resources: vscode.SourceControlResourceState[]) {
this._resourceStates = [...resources];
const handles: number[] = [];
const rawResources = resources.map(r => {
const handle = this._resourceHandlePool++;
this._resourceStatesMap.set(handle, r);
handles.push(handle);
const sourceUri = r.resourceUri.toString();
const command = this._commands.toInternal(r.command);
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 tooltip = (r.decorations && r.decorations.tooltip) || '';
const strikeThrough = r.decorations && !!r.decorations.strikeThrough;
const faded = r.decorations && !!r.decorations.faded;
return [handle, sourceUri, command, icons, tooltip, strikeThrough, faded] as SCMRawResource;
});
const disposable = () => handles.forEach(handle => this._resourceStatesMap.delete(handle));
this._resourceStatesRollingDisposables.push(disposable);
while (this._resourceStatesRollingDisposables.length >= 10) {
this._resourceStatesRollingDisposables.shift()();
}
this._proxy.$updateGroupResourceStates(this._sourceControlHandle, this._handle, rawResources);
}
private _handle: GroupHandle = ExtHostSourceControlResourceGroup._handlePool++;
get handle(): GroupHandle {
return this._handle;
}
constructor(
private _proxy: MainThreadSCMShape,
private _commands: CommandsConverter,
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);
}
dispose(): void {
this._proxy.$unregisterGroup(this._sourceControlHandle, this._handle);
}
}
class ExtHostSourceControl implements vscode.SourceControl {
private static _handlePool: number = 0;
private _groups: Map<GroupHandle, ExtHostSourceControlResourceGroup> = new Map<GroupHandle, ExtHostSourceControlResourceGroup>();
get id(): string {
return this._id;
}
get label(): string {
return this._label;
}
private _inputBox: ExtHostSCMInputBox;
get inputBox(): ExtHostSCMInputBox { return this._inputBox; }
private _count: number | undefined = undefined;
get count(): number | undefined {
return this._count;
}
set count(count: number | undefined) {
this._count = count;
this._proxy.$updateSourceControl(this._handle, { count });
}
private _quickDiffProvider: vscode.QuickDiffProvider | undefined = undefined;
get quickDiffProvider(): vscode.QuickDiffProvider | undefined {
return this._quickDiffProvider;
}
set quickDiffProvider(quickDiffProvider: vscode.QuickDiffProvider | undefined) {
this._quickDiffProvider = quickDiffProvider;
this._proxy.$updateSourceControl(this._handle, { hasQuickDiffProvider: !!quickDiffProvider });
}
private _commitTemplate: string | undefined = undefined;
get commitTemplate(): string | undefined {
return this._commitTemplate;
}
set commitTemplate(commitTemplate: string | undefined) {
this._commitTemplate = commitTemplate;
this._proxy.$updateSourceControl(this._handle, { commitTemplate });
}
private _acceptInputCommand: vscode.Command | undefined = undefined;
get acceptInputCommand(): vscode.Command | undefined {
return this._acceptInputCommand;
}
set acceptInputCommand(acceptInputCommand: vscode.Command | undefined) {
this._acceptInputCommand = acceptInputCommand;
const internal = this._commands.toInternal(acceptInputCommand);
this._proxy.$updateSourceControl(this._handle, { acceptInputCommand: internal });
}
private _statusBarCommands: vscode.Command[] | undefined = undefined;
get statusBarCommands(): vscode.Command[] | undefined {
return this._statusBarCommands;
}
set statusBarCommands(statusBarCommands: vscode.Command[] | undefined) {
this._statusBarCommands = statusBarCommands;
const internal = (statusBarCommands || []).map(c => this._commands.toInternal(c));
this._proxy.$updateSourceControl(this._handle, { statusBarCommands: internal });
}
private _handle: number = ExtHostSourceControl._handlePool++;
constructor(
private _proxy: MainThreadSCMShape,
private _commands: CommandsConverter,
private _id: string,
private _label: string,
) {
this._inputBox = new ExtHostSCMInputBox(this._proxy, this._handle);
this._proxy.$registerSourceControl(this._handle, _id, _label);
}
createResourceGroup(id: string, label: string): ExtHostSourceControlResourceGroup {
const group = new ExtHostSourceControlResourceGroup(this._proxy, this._commands, this._handle, id, label);
this._groups.set(group.handle, group);
return group;
}
getResourceGroup(handle: GroupHandle): ExtHostSourceControlResourceGroup | undefined {
return this._groups.get(handle);
}
dispose(): void {
this._proxy.$unregisterSourceControl(this._handle);
}
}
type ProviderHandle = number;
type GroupHandle = number;
type ResourceStateHandle = number;
export class ExtHostSCM {
private static _handlePool: number = 0;
private _proxy: MainThreadSCMShape;
private _sourceControls: Map<ProviderHandle, ExtHostSourceControl> = new Map<ProviderHandle, ExtHostSourceControl>();
private _sourceControlsByExtension: Map<string, ExtHostSourceControl[]> = new Map<string, ExtHostSourceControl[]>();
private _onDidChangeActiveProvider = new Emitter<vscode.SourceControl>();
get onDidChangeActiveProvider(): Event<vscode.SourceControl> { return this._onDidChangeActiveProvider.event; }
constructor(
mainContext: IMainContext,
private _commands: ExtHostCommands
) {
this._proxy = mainContext.get(MainContext.MainThreadSCM);
_commands.registerArgumentProcessor({
processArgument: arg => {
if (arg && arg.$mid === 3) {
const sourceControl = this._sourceControls.get(arg.sourceControlHandle);
if (!sourceControl) {
return arg;
}
const group = sourceControl.getResourceGroup(arg.groupHandle);
if (!group) {
return arg;
}
return group.getResourceState(arg.handle);
} else if (arg && arg.$mid === 4) {
const sourceControl = this._sourceControls.get(arg.sourceControlHandle);
if (!sourceControl) {
return arg;
}
return sourceControl.getResourceGroup(arg.groupHandle);
} else if (arg && arg.$mid === 5) {
const sourceControl = this._sourceControls.get(arg.handle);
if (!sourceControl) {
return arg;
}
return sourceControl;
}
return arg;
}
});
}
createSourceControl(extension: IExtensionDescription, id: string, label: string): vscode.SourceControl {
const handle = ExtHostSCM._handlePool++;
const sourceControl = new ExtHostSourceControl(this._proxy, this._commands.converter, id, label);
this._sourceControls.set(handle, sourceControl);
const sourceControls = this._sourceControlsByExtension.get(extension.id) || [];
sourceControls.push(sourceControl);
this._sourceControlsByExtension.set(extension.id, sourceControls);
return sourceControl;
}
// Deprecated
getLastInputBox(extension: IExtensionDescription): ExtHostSCMInputBox {
const sourceControls = this._sourceControlsByExtension.get(extension.id);
const sourceControl = sourceControls && sourceControls[sourceControls.length - 1];
const inputBox = sourceControl && sourceControl.inputBox;
return inputBox;
}
$provideOriginalResource(sourceControlHandle: number, uri: URI): TPromise<URI> {
const sourceControl = this._sourceControls.get(sourceControlHandle);
if (!sourceControl || !sourceControl.quickDiffProvider) {
return TPromise.as(null);
}
return asWinJsPromise(token => {
const result = sourceControl.quickDiffProvider.provideOriginalResource(uri, token);
return result && URI.parse(result.toString());
});
}
$onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise<void> {
const sourceControl = this._sourceControls.get(sourceControlHandle);
if (!sourceControl || !sourceControl.quickDiffProvider) {
return TPromise.as(null);
}
sourceControl.inputBox.$onInputBoxValueChange(value);
return TPromise.as(null);
}
}

View File

@@ -0,0 +1,190 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { StatusBarAlignment as ExtHostStatusBarAlignment, Disposable, ThemeColor } from './extHostTypes';
import { StatusBarItem, StatusBarAlignment } from 'vscode';
import { MainContext, MainThreadStatusBarShape, IMainContext } from './extHost.protocol';
export class ExtHostStatusBarEntry implements StatusBarItem {
private static ID_GEN = 0;
private _id: number;
private _alignment: number;
private _priority: number;
private _disposed: boolean;
private _visible: boolean;
private _text: string;
private _tooltip: string;
private _color: string | ThemeColor;
private _command: string;
private _timeoutHandle: number;
private _proxy: MainThreadStatusBarShape;
private _extensionId: string;
constructor(proxy: MainThreadStatusBarShape, extensionId: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
this._id = ExtHostStatusBarEntry.ID_GEN++;
this._proxy = proxy;
this._alignment = alignment;
this._priority = priority;
this._extensionId = extensionId;
}
public get id(): number {
return this._id;
}
public get alignment(): StatusBarAlignment {
return this._alignment;
}
public get priority(): number {
return this._priority;
}
public get text(): string {
return this._text;
}
public get tooltip(): string {
return this._tooltip;
}
public get color(): string | ThemeColor {
return this._color;
}
public get command(): string {
return this._command;
}
public set text(text: string) {
this._text = text;
this.update();
}
public set tooltip(tooltip: string) {
this._tooltip = tooltip;
this.update();
}
public set color(color: string | ThemeColor) {
this._color = color;
this.update();
}
public set command(command: string) {
this._command = command;
this.update();
}
public show(): void {
this._visible = true;
this.update();
}
public hide(): void {
clearTimeout(this._timeoutHandle);
this._visible = false;
this._proxy.$dispose(this.id);
}
private update(): void {
if (this._disposed || !this._visible) {
return;
}
clearTimeout(this._timeoutHandle);
// Defer the update so that multiple changes to setters dont cause a redraw each
this._timeoutHandle = setTimeout(() => {
this._timeoutHandle = undefined;
// Set to status bar
this._proxy.$setEntry(this.id, this._extensionId, this.text, this.tooltip, this.command, this.color,
this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT,
this._priority);
}, 0);
}
public dispose(): void {
this.hide();
this._disposed = true;
}
}
class StatusBarMessage {
private _item: StatusBarItem;
private _messages: { message: string }[] = [];
constructor(statusBar: ExtHostStatusBar) {
this._item = statusBar.createStatusBarEntry(void 0, ExtHostStatusBarAlignment.Left, Number.MIN_VALUE);
}
dispose() {
this._messages.length = 0;
this._item.dispose();
}
setMessage(message: string): Disposable {
const data: { message: string } = { message }; // use object to not confuse equal strings
this._messages.unshift(data);
this._update();
return new Disposable(() => {
let idx = this._messages.indexOf(data);
if (idx >= 0) {
this._messages.splice(idx, 1);
this._update();
}
});
}
private _update() {
if (this._messages.length > 0) {
this._item.text = this._messages[0].message;
this._item.show();
} else {
this._item.hide();
}
}
}
export class ExtHostStatusBar {
private _proxy: MainThreadStatusBarShape;
private _statusMessage: StatusBarMessage;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.get(MainContext.MainThreadStatusBar);
this._statusMessage = new StatusBarMessage(this);
}
createStatusBarEntry(extensionId: string, alignment?: ExtHostStatusBarAlignment, priority?: number): StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, extensionId, alignment, priority);
}
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): Disposable {
let d = this._statusMessage.setMessage(text);
let handle: number;
if (typeof timeoutOrThenable === 'number') {
handle = setTimeout(() => d.dispose(), timeoutOrThenable);
} else if (typeof timeoutOrThenable !== 'undefined') {
timeoutOrThenable.then(() => d.dispose(), () => d.dispose());
}
return new Disposable(() => {
d.dispose();
clearTimeout(handle);
});
}
}

View File

@@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { MainContext, MainThreadStorageShape, IMainContext } from './extHost.protocol';
export class ExtHostStorage {
private _proxy: MainThreadStorageShape;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.get(MainContext.MainThreadStorage);
}
getValue<T>(shared: boolean, key: string, defaultValue?: T): TPromise<T> {
return this._proxy.$getValue<T>(shared, key).then(value => value || defaultValue);
}
setValue(shared: boolean, key: string, value: any): TPromise<void> {
return this._proxy.$setValue(shared, key, value);
}
}

View File

@@ -0,0 +1,439 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import * as Objects from 'vs/base/common/objects';
import { asWinJsPromise } from 'vs/base/common/async';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as TaskSystem from 'vs/workbench/parts/tasks/common/tasks';
import { MainContext, MainThreadTaskShape, ExtHostTaskShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
import * as types from 'vs/workbench/api/node/extHostTypes';
import * as vscode from 'vscode';
interface StringMap<V> {
[key: string]: V;
}
/*
namespace ProblemPattern {
export function from(value: vscode.ProblemPattern | vscode.MultiLineProblemPattern): Problems.ProblemPattern | Problems.MultiLineProblemPattern {
if (value === void 0 || value === null) {
return undefined;
}
if (Array.isArray(value)) {
let result: Problems.ProblemPattern[] = [];
for (let pattern of value) {
let converted = fromSingle(pattern);
if (!converted) {
return undefined;
}
result.push(converted);
}
return result;
} else {
return fromSingle(value);
}
}
function copyProperty(target: Problems.ProblemPattern, source: vscode.ProblemPattern, tk: keyof Problems.ProblemPattern) {
let sk: keyof vscode.ProblemPattern = tk;
let value = source[sk];
if (typeof value === 'number') {
target[tk] = value;
}
}
function getValue(value: number, defaultValue: number): number {
if (value !== void 0 && value === null) {
return value;
}
return defaultValue;
}
function fromSingle(problemPattern: vscode.ProblemPattern): Problems.ProblemPattern {
if (problemPattern === void 0 || problemPattern === null || !(problemPattern.regexp instanceof RegExp)) {
return undefined;
}
let result: Problems.ProblemPattern = {
regexp: problemPattern.regexp
};
copyProperty(result, problemPattern, 'file');
copyProperty(result, problemPattern, 'location');
copyProperty(result, problemPattern, 'line');
copyProperty(result, problemPattern, 'character');
copyProperty(result, problemPattern, 'endLine');
copyProperty(result, problemPattern, 'endCharacter');
copyProperty(result, problemPattern, 'severity');
copyProperty(result, problemPattern, 'code');
copyProperty(result, problemPattern, 'message');
if (problemPattern.loop === true || problemPattern.loop === false) {
result.loop = problemPattern.loop;
}
if (result.location) {
result.file = getValue(result.file, 1);
result.message = getValue(result.message, 0);
} else {
result.file = getValue(result.file, 1);
result.line = getValue(result.line, 2);
result.character = getValue(result.character, 3);
result.message = getValue(result.message, 0);
}
return result;
}
}
namespace ApplyTo {
export function from(value: vscode.ApplyToKind): Problems.ApplyToKind {
if (value === void 0 || value === null) {
return Problems.ApplyToKind.allDocuments;
}
switch (value) {
case types.ApplyToKind.OpenDocuments:
return Problems.ApplyToKind.openDocuments;
case types.ApplyToKind.ClosedDocuments:
return Problems.ApplyToKind.closedDocuments;
}
return Problems.ApplyToKind.allDocuments;
}
}
namespace FileLocation {
export function from(value: vscode.FileLocationKind | string): { kind: Problems.FileLocationKind; prefix?: string } {
if (value === void 0 || value === null) {
return { kind: Problems.FileLocationKind.Auto };
}
if (typeof value === 'string') {
return { kind: Problems.FileLocationKind.Relative, prefix: value };
}
switch (value) {
case types.FileLocationKind.Absolute:
return { kind: Problems.FileLocationKind.Absolute };
case types.FileLocationKind.Relative:
return { kind: Problems.FileLocationKind.Relative, prefix: '${workspaceRoot}' };
}
return { kind: Problems.FileLocationKind.Auto };
}
}
namespace WatchingPattern {
export function from(value: RegExp | vscode.BackgroundPattern): Problems.WatchingPattern {
if (value === void 0 || value === null) {
return undefined;
}
if (value instanceof RegExp) {
return { regexp: value };
}
if (!(value.regexp instanceof RegExp)) {
return undefined;
}
let result: Problems.WatchingPattern = {
regexp: value.regexp
};
if (typeof value.file === 'number') {
result.file = value.file;
}
return result;
}
}
namespace BackgroundMonitor {
export function from(value: vscode.BackgroundMonitor): Problems.WatchingMatcher {
if (value === void 0 || value === null) {
return undefined;
}
let result: Problems.WatchingMatcher = {
activeOnStart: !!value.activeOnStart,
beginsPattern: WatchingPattern.from(value.beginsPattern),
endsPattern: WatchingPattern.from(value.endsPattern)
};
return result;
}
}
namespace ProblemMatcher {
export function from(values: (string | vscode.ProblemMatcher)[]): (string | Problems.ProblemMatcher)[] {
if (values === void 0 || values === null) {
return undefined;
}
let result: (string | Problems.ProblemMatcher)[] = [];
for (let value of values) {
let converted = typeof value === 'string' ? value : fromSingle(value);
if (converted) {
result.push(converted);
}
}
return result;
}
function fromSingle(problemMatcher: vscode.ProblemMatcher): Problems.ProblemMatcher {
if (problemMatcher === void 0 || problemMatcher === null) {
return undefined;
}
let location = FileLocation.from(problemMatcher.fileLocation);
let result: Problems.ProblemMatcher = {
owner: typeof problemMatcher.owner === 'string' ? problemMatcher.owner : UUID.generateUuid(),
applyTo: ApplyTo.from(problemMatcher.applyTo),
fileLocation: location.kind,
filePrefix: location.prefix,
pattern: ProblemPattern.from(problemMatcher.pattern),
severity: fromDiagnosticSeverity(problemMatcher.severity),
};
return result;
}
}
*/
namespace TaskRevealKind {
export function from(value: vscode.TaskRevealKind): TaskSystem.RevealKind {
if (value === void 0 || value === null) {
return TaskSystem.RevealKind.Always;
}
switch (value) {
case types.TaskRevealKind.Silent:
return TaskSystem.RevealKind.Silent;
case types.TaskRevealKind.Never:
return TaskSystem.RevealKind.Never;
}
return TaskSystem.RevealKind.Always;
}
}
namespace TaskPanelKind {
export function from(value: vscode.TaskPanelKind): TaskSystem.PanelKind {
if (value === void 0 || value === null) {
return TaskSystem.PanelKind.Shared;
}
switch (value) {
case types.TaskPanelKind.Dedicated:
return TaskSystem.PanelKind.Dedicated;
case types.TaskPanelKind.New:
return TaskSystem.PanelKind.New;
default:
return TaskSystem.PanelKind.Shared;
}
}
}
namespace PresentationOptions {
export function from(value: vscode.TaskPresentationOptions): TaskSystem.PresentationOptions {
if (value === void 0 || value === null) {
return { reveal: TaskSystem.RevealKind.Always, echo: true, focus: false, panel: TaskSystem.PanelKind.Shared };
}
return {
reveal: TaskRevealKind.from(value.reveal),
echo: value.echo === void 0 ? true : !!value.echo,
focus: !!value.focus,
panel: TaskPanelKind.from(value.panel)
};
}
}
namespace Strings {
export function from(value: string[]): string[] {
if (value === void 0 || value === null) {
return undefined;
}
for (let element of value) {
if (typeof element !== 'string') {
return [];
}
}
return value;
}
}
namespace CommandOptions {
function isShellConfiguration(value: any): value is { executable: string; shellArgs?: string[] } {
return value && typeof value.executable === 'string';
}
export function from(value: vscode.ShellExecutionOptions | vscode.ProcessExecutionOptions): TaskSystem.CommandOptions {
if (value === void 0 || value === null) {
return undefined;
}
let result: TaskSystem.CommandOptions = {
};
if (typeof value.cwd === 'string') {
result.cwd = value.cwd;
}
if (value.env) {
result.env = Object.create(null);
Object.keys(value.env).forEach(key => {
let envValue = value.env[key];
if (typeof envValue === 'string') {
result.env[key] = envValue;
}
});
}
if (isShellConfiguration(value)) {
result.shell = ShellConfiguration.from(value);
}
return result;
}
}
namespace ShellConfiguration {
export function from(value: { executable?: string, shellArgs?: string[] }): TaskSystem.ShellConfiguration {
if (value === void 0 || value === null || !value.executable) {
return undefined;
}
let result: TaskSystem.ShellConfiguration = {
executable: value.executable,
args: Strings.from(value.shellArgs)
};
return result;
}
}
namespace Tasks {
export function from(tasks: vscode.Task[], extension: IExtensionDescription): TaskSystem.Task[] {
if (tasks === void 0 || tasks === null) {
return [];
}
let result: TaskSystem.Task[] = [];
for (let task of tasks) {
let converted = fromSingle(task, extension);
if (converted) {
result.push(converted);
}
}
return result;
}
function fromSingle(task: vscode.Task, extension: IExtensionDescription): TaskSystem.ContributedTask {
if (typeof task.name !== 'string') {
return undefined;
}
let command: TaskSystem.CommandConfiguration;
let execution = task.execution;
if (execution instanceof types.ProcessExecution) {
command = getProcessCommand(execution);
} else if (execution instanceof types.ShellExecution) {
command = getShellCommand(execution);
} else {
return undefined;
}
if (command === void 0) {
return undefined;
}
command.presentation = PresentationOptions.from(task.presentationOptions);
let source = {
kind: TaskSystem.TaskSourceKind.Extension,
label: typeof task.source === 'string' ? task.source : extension.name,
extension: extension.id
};
let label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
let key = (task as types.Task).definitionKey;
let kind = (task as types.Task).definition;
let id = `${extension.id}.${key}`;
let taskKind: TaskSystem.TaskIdentifier = {
_key: key,
type: kind.type
};
Objects.assign(taskKind, kind);
let result: TaskSystem.ContributedTask = {
_id: id, // uuidMap.getUUID(identifier),
_source: source,
_label: label,
type: kind.type,
defines: taskKind,
name: task.name,
identifier: label,
group: task.group ? (task.group as types.TaskGroup).id : undefined,
command: command,
isBackground: !!task.isBackground,
problemMatchers: task.problemMatchers.slice(),
hasDefinedMatchers: (task as types.Task).hasDefinedMatchers
};
return result;
}
function getProcessCommand(value: vscode.ProcessExecution): TaskSystem.CommandConfiguration {
if (typeof value.process !== 'string') {
return undefined;
}
let result: TaskSystem.CommandConfiguration = {
name: value.process,
args: Strings.from(value.args),
runtime: TaskSystem.RuntimeType.Process,
suppressTaskName: true,
presentation: undefined
};
if (value.options) {
result.options = CommandOptions.from(value.options);
}
return result;
}
function getShellCommand(value: vscode.ShellExecution): TaskSystem.CommandConfiguration {
if (typeof value.commandLine !== 'string') {
return undefined;
}
let result: TaskSystem.CommandConfiguration = {
name: value.commandLine,
runtime: TaskSystem.RuntimeType.Shell,
presentation: undefined
};
if (value.options) {
result.options = CommandOptions.from(value.options);
}
return result;
}
}
interface HandlerData {
provider: vscode.TaskProvider;
extension: IExtensionDescription;
}
export class ExtHostTask implements ExtHostTaskShape {
private _proxy: MainThreadTaskShape;
private _handleCounter: number;
private _handlers: Map<number, HandlerData>;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.get(MainContext.MainThreadTask);
this._handleCounter = 0;
this._handlers = new Map<number, HandlerData>();
};
public registerTaskProvider(extension: IExtensionDescription, provider: vscode.TaskProvider): vscode.Disposable {
if (!provider) {
return new types.Disposable(() => { });
}
let handle = this.nextHandle();
this._handlers.set(handle, { provider, extension });
this._proxy.$registerTaskProvider(handle);
return new types.Disposable(() => {
this._handlers.delete(handle);
this._proxy.$unregisterTaskProvider(handle);
});
}
public $provideTasks(handle: number): TPromise<TaskSystem.TaskSet> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<TaskSystem.TaskSet>(new Error('no handler found'));
}
return asWinJsPromise(token => handler.provider.provideTasks(token)).then(value => {
return {
tasks: Tasks.from(value, handler.extension),
extension: handler.extension
};
});
}
private nextHandle(): number {
return this._handleCounter++;
}
}

View File

@@ -0,0 +1,172 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import vscode = require('vscode');
import { TPromise, TValueCallback } from 'vs/base/common/winjs.base';
import Event, { Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext } from './extHost.protocol';
export class ExtHostTerminal implements vscode.Terminal {
private _name: string;
private _id: number;
private _proxy: MainThreadTerminalServiceShape;
private _disposed: boolean;
private _queuedRequests: ApiRequest[];
private _pidPromise: TPromise<number>;
private _pidPromiseComplete: TValueCallback<number>;
constructor(
proxy: MainThreadTerminalServiceShape,
name?: string,
shellPath?: string,
shellArgs?: string[],
waitOnExit?: boolean
) {
this._name = name;
this._queuedRequests = [];
this._proxy = proxy;
this._pidPromise = new TPromise<number>(c => {
this._pidPromiseComplete = c;
});
this._proxy.$createTerminal(name, shellPath, shellArgs, waitOnExit).then((id) => {
this._id = id;
this._queuedRequests.forEach((r) => {
r.run(this._proxy, this._id);
});
this._queuedRequests = [];
});
}
public get name(): string {
this._checkDisposed();
return this._name;
}
public get processId(): Thenable<number> {
this._checkDisposed();
return this._pidPromise;
}
public sendText(text: string, addNewLine: boolean = true): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$sendText, [text, addNewLine]);
}
public show(preserveFocus: boolean): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$show, [preserveFocus]);
}
public hide(): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$hide, []);
}
public dispose(): void {
if (!this._disposed) {
this._disposed = true;
this._queueApiRequest(this._proxy.$dispose, []);
}
}
public _setProcessId(processId: number): void {
this._pidPromiseComplete(processId);
this._pidPromiseComplete = null;
}
private _queueApiRequest(callback: (...args: any[]) => void, args: any[]) {
let request: ApiRequest = new ApiRequest(callback, args);
if (!this._id) {
this._queuedRequests.push(request);
return;
}
request.run(this._proxy, this._id);
}
private _checkDisposed() {
if (this._disposed) {
throw new Error('Terminal has already been disposed');
}
}
}
export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _onDidCloseTerminal: Emitter<vscode.Terminal>;
private _proxy: MainThreadTerminalServiceShape;
private _terminals: ExtHostTerminal[];
constructor(mainContext: IMainContext) {
this._onDidCloseTerminal = new Emitter<vscode.Terminal>();
this._proxy = mainContext.get(MainContext.MainThreadTerminalService);
this._terminals = [];
}
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
let terminal = new ExtHostTerminal(this._proxy, name, shellPath, shellArgs);
this._terminals.push(terminal);
return terminal;
}
public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal {
let terminal = new ExtHostTerminal(this._proxy, options.name, options.shellPath, options.shellArgs/*, options.waitOnExit*/);
this._terminals.push(terminal);
return terminal;
}
public get onDidCloseTerminal(): Event<vscode.Terminal> {
return this._onDidCloseTerminal && this._onDidCloseTerminal.event;
}
public $acceptTerminalClosed(id: number): void {
let index = this._getTerminalIndexById(id);
if (index === null) {
// The terminal was not created by the terminal API, ignore it
return;
}
let terminal = this._terminals.splice(index, 1)[0];
this._onDidCloseTerminal.fire(terminal);
}
public $acceptTerminalProcessId(id: number, processId: number): void {
let terminal = this._getTerminalById(id);
terminal._setProcessId(processId);
}
private _getTerminalById(id: number): ExtHostTerminal {
let index = this._getTerminalIndexById(id);
return index !== null ? this._terminals[index] : null;
}
private _getTerminalIndexById(id: number): number {
let index: number = null;
this._terminals.some((terminal, i) => {
let thisId = (<any>terminal)._id;
if (thisId === id) {
index = i;
return true;
}
return false;
});
return index;
}
}
class ApiRequest {
private _callback: (...args: any[]) => void;
private _args: any[];
constructor(callback: (...args: any[]) => void, args: any[]) {
this._callback = callback;
this._args = args;
}
public run(proxy: MainThreadTerminalServiceShape, id: number) {
this._callback.apply(proxy, [id].concat(this._args));
}
}

View File

@@ -0,0 +1,623 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ok } from 'vs/base/common/assert';
import { readonly, illegalArgument, V8CallSite } from 'vs/base/common/errors';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData';
import { Selection, Range, Position, EndOfLine, TextEditorRevealType, TextEditorLineNumbersStyle, SnippetString } from './extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/editorCommon';
import * as TypeConverters from './extHostTypeConverters';
import { MainThreadEditorsShape, MainThreadTelemetryShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from './extHost.protocol';
import * as vscode from 'vscode';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { IRange } from 'vs/editor/common/core/range';
import { containsCommandLink } from 'vs/base/common/htmlContent';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
export class TextEditorDecorationType implements vscode.TextEditorDecorationType {
private static _Keys = new IdGenerator('TextEditorDecorationType');
private _proxy: MainThreadEditorsShape;
public key: string;
constructor(proxy: MainThreadEditorsShape, options: vscode.DecorationRenderOptions) {
this.key = TextEditorDecorationType._Keys.nextId();
this._proxy = proxy;
this._proxy.$registerTextEditorDecorationType(this.key, <any>/* URI vs Uri */ options);
}
public dispose(): void {
this._proxy.$removeTextEditorDecorationType(this.key);
}
}
export interface ITextEditOperation {
range: vscode.Range;
text: string;
forceMoveMarkers: boolean;
}
export interface IEditData {
documentVersionId: number;
edits: ITextEditOperation[];
setEndOfLine: EndOfLine;
undoStopBefore: boolean;
undoStopAfter: boolean;
}
export class TextEditorEdit {
private readonly _document: vscode.TextDocument;
private readonly _documentVersionId: number;
private _collectedEdits: ITextEditOperation[];
private _setEndOfLine: EndOfLine;
private readonly _undoStopBefore: boolean;
private readonly _undoStopAfter: boolean;
constructor(document: vscode.TextDocument, options: { undoStopBefore: boolean; undoStopAfter: boolean; }) {
this._document = document;
this._documentVersionId = document.version;
this._collectedEdits = [];
this._setEndOfLine = 0;
this._undoStopBefore = options.undoStopBefore;
this._undoStopAfter = options.undoStopAfter;
}
finalize(): IEditData {
return {
documentVersionId: this._documentVersionId,
edits: this._collectedEdits,
setEndOfLine: this._setEndOfLine,
undoStopBefore: this._undoStopBefore,
undoStopAfter: this._undoStopAfter
};
}
replace(location: Position | Range | Selection, value: string): void {
let range: Range = null;
if (location instanceof Position) {
range = new Range(location, location);
} else if (location instanceof Range) {
range = location;
} else {
throw new Error('Unrecognized location');
}
this._pushEdit(range, value, false);
}
insert(location: Position, value: string): void {
this._pushEdit(new Range(location, location), value, true);
}
delete(location: Range | Selection): void {
let range: Range = null;
if (location instanceof Range) {
range = location;
} else {
throw new Error('Unrecognized location');
}
this._pushEdit(range, null, true);
}
private _pushEdit(range: Range, text: string, forceMoveMarkers: boolean): void {
let validRange = this._document.validateRange(range);
this._collectedEdits.push({
range: validRange,
text: text,
forceMoveMarkers: forceMoveMarkers
});
}
setEndOfLine(endOfLine: EndOfLine): void {
if (endOfLine !== EndOfLine.LF && endOfLine !== EndOfLine.CRLF) {
throw illegalArgument('endOfLine');
}
this._setEndOfLine = endOfLine;
}
}
function deprecated(name: string, message: string = 'Refer to the documentation for further details.') {
return (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) => {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.warn(`[Deprecation Warning] method '${name}' is deprecated and should no longer be used. ${message}`);
return originalMethod.apply(this, args);
};
return descriptor;
};
}
export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
private _proxy: MainThreadEditorsShape;
private _id: string;
private _tabSize: number;
private _insertSpaces: boolean;
private _cursorStyle: TextEditorCursorStyle;
private _lineNumbers: TextEditorLineNumbersStyle;
constructor(proxy: MainThreadEditorsShape, id: string, source: IResolvedTextEditorConfiguration) {
this._proxy = proxy;
this._id = id;
this._accept(source);
}
public _accept(source: IResolvedTextEditorConfiguration): void {
this._tabSize = source.tabSize;
this._insertSpaces = source.insertSpaces;
this._cursorStyle = source.cursorStyle;
this._lineNumbers = source.lineNumbers;
}
public get tabSize(): number | string {
return this._tabSize;
}
private _validateTabSize(value: number | string): number | 'auto' | null {
if (value === 'auto') {
return 'auto';
}
if (typeof value === 'number') {
let r = Math.floor(value);
return (r > 0 ? r : null);
}
if (typeof value === 'string') {
let r = parseInt(value, 10);
if (isNaN(r)) {
return null;
}
return (r > 0 ? r : null);
}
return null;
}
public set tabSize(value: number | string) {
let tabSize = this._validateTabSize(value);
if (tabSize === null) {
// ignore invalid call
return;
}
if (typeof tabSize === 'number') {
if (this._tabSize === tabSize) {
// nothing to do
return;
}
// reflect the new tabSize value immediately
this._tabSize = tabSize;
}
warnOnError(this._proxy.$trySetOptions(this._id, {
tabSize: tabSize
}));
}
public get insertSpaces(): boolean | string {
return this._insertSpaces;
}
private _validateInsertSpaces(value: boolean | string): boolean | 'auto' {
if (value === 'auto') {
return 'auto';
}
return (value === 'false' ? false : Boolean(value));
}
public set insertSpaces(value: boolean | string) {
let insertSpaces = this._validateInsertSpaces(value);
if (typeof insertSpaces === 'boolean') {
if (this._insertSpaces === insertSpaces) {
// nothing to do
return;
}
// reflect the new insertSpaces value immediately
this._insertSpaces = insertSpaces;
}
warnOnError(this._proxy.$trySetOptions(this._id, {
insertSpaces: insertSpaces
}));
}
public get cursorStyle(): TextEditorCursorStyle {
return this._cursorStyle;
}
public set cursorStyle(value: TextEditorCursorStyle) {
if (this._cursorStyle === value) {
// nothing to do
return;
}
this._cursorStyle = value;
warnOnError(this._proxy.$trySetOptions(this._id, {
cursorStyle: value
}));
}
public get lineNumbers(): TextEditorLineNumbersStyle {
return this._lineNumbers;
}
public set lineNumbers(value: TextEditorLineNumbersStyle) {
if (this._lineNumbers === value) {
// nothing to do
return;
}
this._lineNumbers = value;
warnOnError(this._proxy.$trySetOptions(this._id, {
lineNumbers: value
}));
}
public assign(newOptions: vscode.TextEditorOptions) {
let bulkConfigurationUpdate: ITextEditorConfigurationUpdate = {};
let hasUpdate = false;
if (typeof newOptions.tabSize !== 'undefined') {
let tabSize = this._validateTabSize(newOptions.tabSize);
if (tabSize === 'auto') {
hasUpdate = true;
bulkConfigurationUpdate.tabSize = tabSize;
} else if (typeof tabSize === 'number' && this._tabSize !== tabSize) {
// reflect the new tabSize value immediately
this._tabSize = tabSize;
hasUpdate = true;
bulkConfigurationUpdate.tabSize = tabSize;
}
}
if (typeof newOptions.insertSpaces !== 'undefined') {
let insertSpaces = this._validateInsertSpaces(newOptions.insertSpaces);
if (insertSpaces === 'auto') {
hasUpdate = true;
bulkConfigurationUpdate.insertSpaces = insertSpaces;
} else if (this._insertSpaces !== insertSpaces) {
// reflect the new insertSpaces value immediately
this._insertSpaces = insertSpaces;
hasUpdate = true;
bulkConfigurationUpdate.insertSpaces = insertSpaces;
}
}
if (typeof newOptions.cursorStyle !== 'undefined') {
if (this._cursorStyle !== newOptions.cursorStyle) {
this._cursorStyle = newOptions.cursorStyle;
hasUpdate = true;
bulkConfigurationUpdate.cursorStyle = newOptions.cursorStyle;
}
}
if (typeof newOptions.lineNumbers !== 'undefined') {
if (this._lineNumbers !== newOptions.lineNumbers) {
this._lineNumbers = newOptions.lineNumbers;
hasUpdate = true;
bulkConfigurationUpdate.lineNumbers = newOptions.lineNumbers;
}
}
if (hasUpdate) {
warnOnError(this._proxy.$trySetOptions(this._id, bulkConfigurationUpdate));
}
}
}
export class ExtHostTextEditor implements vscode.TextEditor {
private readonly _proxy: MainThreadEditorsShape;
private readonly _id: string;
private readonly _documentData: ExtHostDocumentData;
private _selections: Selection[];
private _options: ExtHostTextEditorOptions;
private _viewColumn: vscode.ViewColumn;
private _disposed: boolean = false;
get id(): string { return this._id; }
constructor(proxy: MainThreadEditorsShape, id: string, document: ExtHostDocumentData, selections: Selection[], options: IResolvedTextEditorConfiguration, viewColumn: vscode.ViewColumn) {
this._proxy = proxy;
this._id = id;
this._documentData = document;
this._selections = selections;
this._options = new ExtHostTextEditorOptions(this._proxy, this._id, options);
this._viewColumn = viewColumn;
}
dispose() {
ok(!this._disposed);
this._disposed = true;
}
@deprecated('TextEditor.show') show(column: vscode.ViewColumn) {
this._proxy.$tryShowEditor(this._id, TypeConverters.fromViewColumn(column));
}
@deprecated('TextEditor.hide') hide() {
this._proxy.$tryHideEditor(this._id);
}
// ---- the document
get document(): vscode.TextDocument {
return this._documentData.document;
}
set document(value) {
throw readonly('document');
}
// ---- options
get options(): vscode.TextEditorOptions {
return this._options;
}
set options(value: vscode.TextEditorOptions) {
if (!this._disposed) {
this._options.assign(value);
}
}
_acceptOptions(options: IResolvedTextEditorConfiguration): void {
ok(!this._disposed);
this._options._accept(options);
}
// ---- view column
get viewColumn(): vscode.ViewColumn {
return this._viewColumn;
}
set viewColumn(value) {
throw readonly('viewColumn');
}
_acceptViewColumn(value: vscode.ViewColumn) {
ok(!this._disposed);
this._viewColumn = value;
}
// ---- selections
get selection(): Selection {
return this._selections && this._selections[0];
}
set selection(value: Selection) {
if (!(value instanceof Selection)) {
throw illegalArgument('selection');
}
this._selections = [value];
this._trySetSelection();
}
get selections(): Selection[] {
return this._selections;
}
set selections(value: Selection[]) {
if (!Array.isArray(value) || value.some(a => !(a instanceof Selection))) {
throw illegalArgument('selections');
}
this._selections = value;
this._trySetSelection();
}
setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void {
this._runOnProxy(
() => this._proxy.$trySetDecorations(
this._id,
decorationType.key,
TypeConverters.fromRangeOrRangeWithMessage(ranges)
)
);
}
revealRange(range: Range, revealType: vscode.TextEditorRevealType): void {
this._runOnProxy(
() => this._proxy.$tryRevealRange(
this._id,
TypeConverters.fromRange(range),
(revealType || TextEditorRevealType.Default)
)
);
}
private _trySetSelection(): TPromise<vscode.TextEditor> {
let selection = this._selections.map(TypeConverters.fromSelection);
return this._runOnProxy(() => this._proxy.$trySetSelections(this._id, selection));
}
_acceptSelections(selections: Selection[]): void {
ok(!this._disposed);
this._selections = selections;
}
// ---- editing
edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Thenable<boolean> {
if (this._disposed) {
return TPromise.wrapError<boolean>(new Error('TextEditor#edit not possible on closed editors'));
}
let edit = new TextEditorEdit(this._documentData.document, options);
callback(edit);
return this._applyEdit(edit);
}
private _applyEdit(editBuilder: TextEditorEdit): TPromise<boolean> {
let editData = editBuilder.finalize();
// check that the edits are not overlapping (i.e. illegal)
let editRanges = editData.edits.map(edit => edit.range);
// sort ascending (by end and then by start)
editRanges.sort((a, b) => {
if (a.end.line === b.end.line) {
if (a.end.character === b.end.character) {
if (a.start.line === b.start.line) {
return a.start.character - b.start.character;
}
return a.start.line - b.start.line;
}
return a.end.character - b.end.character;
}
return a.end.line - b.end.line;
});
// check that no edits are overlapping
for (let i = 0, count = editRanges.length - 1; i < count; i++) {
const rangeEnd = editRanges[i].end;
const nextRangeStart = editRanges[i + 1].start;
if (nextRangeStart.isBefore(rangeEnd)) {
// overlapping ranges
return TPromise.wrapError<boolean>(
new Error('Overlapping ranges are not allowed!')
);
}
}
// prepare data for serialization
let edits: ISingleEditOperation[] = editData.edits.map((edit) => {
return {
range: TypeConverters.fromRange(edit.range),
text: edit.text,
forceMoveMarkers: edit.forceMoveMarkers
};
});
return this._proxy.$tryApplyEdits(this._id, editData.documentVersionId, edits, {
setEndOfLine: editData.setEndOfLine,
undoStopBefore: editData.undoStopBefore,
undoStopAfter: editData.undoStopAfter
});
}
insertSnippet(snippet: SnippetString, where?: Position | Position[] | Range | Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Thenable<boolean> {
if (this._disposed) {
return TPromise.wrapError<boolean>(new Error('TextEditor#insertSnippet not possible on closed editors'));
}
let ranges: IRange[];
if (!where || (Array.isArray(where) && where.length === 0)) {
ranges = this._selections.map(TypeConverters.fromRange);
} else if (where instanceof Position) {
const { lineNumber, column } = TypeConverters.fromPosition(where);
ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }];
} else if (where instanceof Range) {
ranges = [TypeConverters.fromRange(where)];
} else {
ranges = [];
for (const posOrRange of where) {
if (posOrRange instanceof Range) {
ranges.push(TypeConverters.fromRange(posOrRange));
} else {
const { lineNumber, column } = TypeConverters.fromPosition(posOrRange);
ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column });
}
}
}
return this._proxy.$tryInsertSnippet(this._id, snippet.value, ranges, options);
}
// ---- util
private _runOnProxy(callback: () => TPromise<any>): TPromise<ExtHostTextEditor> {
if (this._disposed) {
console.warn('TextEditor is closed/disposed');
return TPromise.as(undefined);
}
return callback().then(() => this, err => {
if (!(err instanceof Error && err.name === 'DISPOSED')) {
console.warn(err);
}
return null;
});
}
}
export class ExtHostTextEditor2 extends ExtHostTextEditor {
constructor(
private readonly _extHostExtensions: ExtHostExtensionService,
private readonly _mainThreadTelemetry: MainThreadTelemetryShape,
proxy: MainThreadEditorsShape,
id: string,
document: ExtHostDocumentData,
selections: Selection[],
options: IResolvedTextEditorConfiguration,
viewColumn: vscode.ViewColumn
) {
super(proxy, id, document, selections, options, viewColumn);
}
setDecorations(decorationType: vscode.TextEditorDecorationType, rangesOrOptions: Range[] | vscode.DecorationOptions[]): void {
// (1) find out if this decoration is important for us
let usesCommandLink = false;
outer: for (const rangeOrOption of rangesOrOptions) {
if (Range.isRange(rangeOrOption)) {
break;
}
if (typeof rangeOrOption.hoverMessage === 'string' && containsCommandLink(rangeOrOption.hoverMessage)) {
usesCommandLink = true;
break;
} else if (Array.isArray(rangeOrOption.hoverMessage)) {
for (const message of rangeOrOption.hoverMessage) {
if (typeof message === 'string' && containsCommandLink(message)) {
usesCommandLink = true;
break outer;
}
}
}
}
// (2) send event for important decorations
if (usesCommandLink) {
let tag = new Error();
this._extHostExtensions.getExtensionPathIndex().then(index => {
const oldHandler = (<any>Error).prepareStackTrace;
(<any>Error).prepareStackTrace = (error: Error, stackTrace: V8CallSite[]) => {
for (const call of stackTrace) {
const extension = index.findSubstr(call.getFileName());
if (extension) {
this._mainThreadTelemetry.$publicLog('usesCommandLink', {
extension: extension.id,
from: 'decoration',
});
return;
}
}
};
// it all happens here...
// tslint:disable-next-line:no-unused-expression
tag.stack;
(<any>Error).prepareStackTrace = oldHandler;
});
}
// (3) do it
super.setDecorations(decorationType, rangesOrOptions);
}
}
function warnOnError(promise: TPromise<any>): void {
promise.then(null, (err) => {
console.warn(err);
});
}

View File

@@ -0,0 +1,131 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import Event, { Emitter } from 'vs/base/common/event';
import { toThenable } from 'vs/base/common/async';
import { TPromise } from 'vs/base/common/winjs.base';
import { TextEditorSelectionChangeKind } from './extHostTypes';
import * as TypeConverters from './extHostTypeConverters';
import { TextEditorDecorationType, ExtHostTextEditor } from './extHostTextEditor';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
import { MainContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IResolvedTextEditorConfiguration, ISelectionChangeEvent, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
export class ExtHostEditors implements ExtHostEditorsShape {
private readonly _onDidChangeTextEditorSelection = new Emitter<vscode.TextEditorSelectionChangeEvent>();
private readonly _onDidChangeTextEditorOptions = new Emitter<vscode.TextEditorOptionsChangeEvent>();
private readonly _onDidChangeTextEditorViewColumn = new Emitter<vscode.TextEditorViewColumnChangeEvent>();
private readonly _onDidChangeActiveTextEditor = new Emitter<vscode.TextEditor>();
private readonly _onDidChangeVisibleTextEditors = new Emitter<vscode.TextEditor[]>();
readonly onDidChangeTextEditorSelection: Event<vscode.TextEditorSelectionChangeEvent> = this._onDidChangeTextEditorSelection.event;
readonly onDidChangeTextEditorOptions: Event<vscode.TextEditorOptionsChangeEvent> = this._onDidChangeTextEditorOptions.event;
readonly onDidChangeTextEditorViewColumn: Event<vscode.TextEditorViewColumnChangeEvent> = this._onDidChangeTextEditorViewColumn.event;
readonly onDidChangeActiveTextEditor: Event<vscode.TextEditor> = this._onDidChangeActiveTextEditor.event;
readonly onDidChangeVisibleTextEditors: Event<vscode.TextEditor[]> = this._onDidChangeVisibleTextEditors.event;
private _proxy: MainThreadEditorsShape;
private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors;
constructor(
mainContext: IMainContext,
extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
) {
this._proxy = mainContext.get(MainContext.MainThreadEditors);
this._extHostDocumentsAndEditors = extHostDocumentsAndEditors;
this._extHostDocumentsAndEditors.onDidChangeVisibleTextEditors(e => this._onDidChangeVisibleTextEditors.fire(e));
this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(e => this._onDidChangeActiveTextEditor.fire(e));
}
getActiveTextEditor(): ExtHostTextEditor {
return this._extHostDocumentsAndEditors.activeEditor();
}
getVisibleTextEditors(): vscode.TextEditor[] {
return this._extHostDocumentsAndEditors.allEditors();
}
showTextDocument(document: vscode.TextDocument, column: vscode.ViewColumn, preserveFocus: boolean): TPromise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, options: { column: vscode.ViewColumn, preserveFocus: boolean, pinned: boolean }): TPromise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): TPromise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): TPromise<vscode.TextEditor> {
let options: ITextDocumentShowOptions;
if (typeof columnOrOptions === 'number') {
options = {
position: TypeConverters.fromViewColumn(columnOrOptions),
preserveFocus
};
} else if (typeof columnOrOptions === 'object') {
options = {
position: TypeConverters.fromViewColumn(columnOrOptions.viewColumn),
preserveFocus: columnOrOptions.preserveFocus,
selection: typeof columnOrOptions.selection === 'object' ? TypeConverters.fromRange(columnOrOptions.selection) : undefined,
pinned: typeof columnOrOptions.preview === 'boolean' ? !columnOrOptions.preview : undefined
};
} else {
options = {
position: EditorPosition.ONE,
preserveFocus: false
};
}
return this._proxy.$tryShowTextDocument(<URI>document.uri, options).then(id => {
let editor = this._extHostDocumentsAndEditors.getEditor(id);
if (editor) {
return editor;
} else {
throw new Error(`Failed to show text document ${document.uri.toString()}, should show in editor #${id}`);
}
});
}
createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType {
return new TextEditorDecorationType(this._proxy, options);
}
// --- called from main thread
$acceptOptionsChanged(id: string, opts: IResolvedTextEditorConfiguration): void {
let editor = this._extHostDocumentsAndEditors.getEditor(id);
editor._acceptOptions(opts);
this._onDidChangeTextEditorOptions.fire({
textEditor: editor,
options: opts
});
}
$acceptSelectionsChanged(id: string, event: ISelectionChangeEvent): void {
const kind = TextEditorSelectionChangeKind.fromValue(event.source);
const selections = event.selections.map(TypeConverters.toSelection);
const textEditor = this._extHostDocumentsAndEditors.getEditor(id);
textEditor._acceptSelections(selections);
this._onDidChangeTextEditorSelection.fire({
textEditor,
selections,
kind
});
}
$acceptEditorPositionData(data: ITextEditorPositionData): void {
for (let id in data) {
let textEditor = this._extHostDocumentsAndEditors.getEditor(id);
let viewColumn = TypeConverters.toViewColumn(data[id]);
if (textEditor.viewColumn !== viewColumn) {
textEditor._acceptViewColumn(viewColumn);
this._onDidChangeTextEditorViewColumn.fire({ textEditor, viewColumn });
}
}
}
getDiffInformation(id: string): Thenable<vscode.LineChange[]> {
return toThenable(this._proxy.$getDiffInformation(id));
}
}

View File

@@ -0,0 +1,227 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { localize } from 'vs/nls';
import * as vscode from 'vscode';
import URI from 'vs/base/common/uri';
import { distinct } from 'vs/base/common/arrays';
import { debounceEvent } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol';
import { ITreeItem, TreeViewItemHandleArg } from 'vs/workbench/parts/views/common/views';
import { TreeItemCollapsibleState } from './extHostTypes';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { asWinJsPromise } from 'vs/base/common/async';
type TreeItemHandle = number;
export class ExtHostTreeViews implements ExtHostTreeViewsShape {
private treeViews: Map<string, ExtHostTreeView<any>> = new Map<string, ExtHostTreeView<any>>();
constructor(
private _proxy: MainThreadTreeViewsShape,
private commands: ExtHostCommands
) {
commands.registerArgumentProcessor({
processArgument: arg => {
if (arg && arg.$treeViewId && arg.$treeItemHandle) {
return this.convertArgument(arg);
}
return arg;
}
});
}
registerTreeDataProvider<T>(id: string, treeDataProvider: vscode.TreeDataProvider<T>): vscode.Disposable {
const treeView = new ExtHostTreeView<T>(id, treeDataProvider, this._proxy, this.commands.converter);
this.treeViews.set(id, treeView);
return {
dispose: () => {
this.treeViews.delete(id);
treeView.dispose();
}
};
}
$getElements(treeViewId: string): TPromise<ITreeItem[]> {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
return TPromise.wrapError<ITreeItem[]>(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)));
}
return treeView.getTreeItems();
}
$getChildren(treeViewId: string, treeItemHandle?: number): TPromise<ITreeItem[]> {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
return TPromise.wrapError<ITreeItem[]>(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)));
}
return treeView.getChildren(treeItemHandle);
}
private convertArgument(arg: TreeViewItemHandleArg): any {
const treeView = this.treeViews.get(arg.$treeViewId);
return treeView ? treeView.getExtensionElement(arg.$treeItemHandle) : null;
}
}
class ExtHostTreeView<T> extends Disposable {
private _itemHandlePool = 0;
private extElementsMap: Map<TreeItemHandle, T> = new Map<TreeItemHandle, T>();
private itemHandlesMap: Map<T, TreeItemHandle> = new Map<T, TreeItemHandle>();
private extChildrenElementsMap: Map<T, T[]> = new Map<T, T[]>();
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter) {
super();
this.proxy.$registerView(viewId);
if (dataProvider.onDidChangeTreeData) {
this._register(debounceEvent<T, T[]>(dataProvider.onDidChangeTreeData, (last, current) => last ? [...last, current] : [current], 200)(elements => this._refresh(elements)));
}
}
getTreeItems(): TPromise<ITreeItem[]> {
this.extChildrenElementsMap.clear();
this.extElementsMap.clear();
this.itemHandlesMap.clear();
return asWinJsPromise(() => this.dataProvider.getChildren())
.then(elements => this.processAndMapElements(elements));
}
getChildren(treeItemHandle: TreeItemHandle): TPromise<ITreeItem[]> {
let extElement = this.getExtensionElement(treeItemHandle);
if (extElement) {
this.clearChildren(extElement);
} else {
return TPromise.wrapError<ITreeItem[]>(new Error(localize('treeItem.notFound', 'No tree item with id \'{0}\' found.', treeItemHandle)));
}
return asWinJsPromise(() => this.dataProvider.getChildren(extElement))
.then(childrenElements => this.processAndMapElements(childrenElements));
}
getExtensionElement(treeItemHandle: TreeItemHandle): T {
return this.extElementsMap.get(treeItemHandle);
}
private _refresh(elements: T[]): void {
const hasRoot = elements.some(element => !element);
if (hasRoot) {
this.proxy.$refresh(this.viewId, []);
} else {
const itemHandles = distinct(elements.map(element => this.itemHandlesMap.get(element))
.filter(itemHandle => !!itemHandle));
if (itemHandles.length) {
this.proxy.$refresh(this.viewId, itemHandles);
}
}
}
private processAndMapElements(elements: T[]): TPromise<ITreeItem[]> {
if (elements && elements.length) {
return TPromise.join(
elements.filter(element => !!element)
.map(element => {
if (this.extChildrenElementsMap.has(element)) {
return TPromise.wrapError<ITreeItem>(new Error(localize('treeView.duplicateElement', 'Element {0} is already registered', element)));
}
return this.resolveElement(element);
}))
.then(treeItems => treeItems.filter(treeItem => !!treeItem));
}
return TPromise.as([]);
}
private resolveElement(element: T): TPromise<ITreeItem> {
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => {
const treeItem = this.massageTreeItem(extTreeItem);
if (treeItem) {
this.itemHandlesMap.set(element, treeItem.handle);
this.extElementsMap.set(treeItem.handle, element);
if (treeItem.collapsibleState === TreeItemCollapsibleState.Expanded) {
return this.getChildren(treeItem.handle).then(children => {
treeItem.children = children;
return treeItem;
});
} else {
return treeItem;
}
}
return null;
});
}
private massageTreeItem(extensionTreeItem: vscode.TreeItem): ITreeItem {
if (!extensionTreeItem) {
return null;
}
const icon = this.getLightIconPath(extensionTreeItem);
return {
handle: ++this._itemHandlePool,
label: extensionTreeItem.label,
command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command) : void 0,
contextValue: extensionTreeItem.contextValue,
icon,
iconDark: this.getDarkIconPath(extensionTreeItem) || icon,
collapsibleState: extensionTreeItem.collapsibleState,
};
}
private getLightIconPath(extensionTreeItem: vscode.TreeItem) {
if (extensionTreeItem.iconPath) {
if (typeof extensionTreeItem.iconPath === 'string' || extensionTreeItem.iconPath instanceof URI) {
return this.getIconPath(extensionTreeItem.iconPath);
}
return this.getIconPath(extensionTreeItem.iconPath['light']);
}
return void 0;
}
private getDarkIconPath(extensionTreeItem: vscode.TreeItem) {
if (extensionTreeItem.iconPath && extensionTreeItem.iconPath['dark']) {
return this.getIconPath(extensionTreeItem.iconPath['dark']);
}
return void 0;
}
private getIconPath(iconPath: string | URI): string {
if (iconPath instanceof URI) {
return iconPath.toString();
}
return URI.file(iconPath).toString();
}
private clearChildren(extElement: T): void {
const children = this.extChildrenElementsMap.get(extElement);
if (children) {
for (const child of children) {
this.clearElement(child);
}
this.extChildrenElementsMap.delete(extElement);
}
}
private clearElement(extElement: T): void {
this.clearChildren(extElement);
const treeItemhandle = this.itemHandlesMap.get(extElement);
this.itemHandlesMap.delete(extElement);
if (treeItemhandle) {
this.extElementsMap.delete(treeItemhandle);
}
}
dispose() {
this.extElementsMap.clear();
this.itemHandlesMap.clear();
this.extChildrenElementsMap.clear();
}
}

View File

@@ -0,0 +1,454 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Severity from 'vs/base/common/severity';
import * as modes from 'vs/editor/common/modes';
import * as types from './extHostTypes';
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
import { IDecorationOptions, EndOfLineSequence } from 'vs/editor/common/editorCommon';
import * as vscode from 'vscode';
import URI from 'vs/base/common/uri';
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection } from 'vs/editor/common/core/selection';
import * as htmlContent from 'vs/base/common/htmlContent';
export interface PositionLike {
line: number;
character: number;
}
export interface RangeLike {
start: PositionLike;
end: PositionLike;
}
export interface SelectionLike extends RangeLike {
anchor: PositionLike;
active: PositionLike;
}
export function toSelection(selection: ISelection): types.Selection {
let { selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn } = selection;
let start = new types.Position(selectionStartLineNumber - 1, selectionStartColumn - 1);
let end = new types.Position(positionLineNumber - 1, positionColumn - 1);
return new types.Selection(start, end);
}
export function fromSelection(selection: SelectionLike): ISelection {
let { anchor, active } = selection;
return {
selectionStartLineNumber: anchor.line + 1,
selectionStartColumn: anchor.character + 1,
positionLineNumber: active.line + 1,
positionColumn: active.character + 1
};
}
export function fromRange(range: RangeLike): IRange {
if (!range) {
return undefined;
}
let { start, end } = range;
return {
startLineNumber: start.line + 1,
startColumn: start.character + 1,
endLineNumber: end.line + 1,
endColumn: end.character + 1
};
}
export function toRange(range: IRange): types.Range {
if (!range) {
return undefined;
}
let { startLineNumber, startColumn, endLineNumber, endColumn } = range;
return new types.Range(startLineNumber - 1, startColumn - 1, endLineNumber - 1, endColumn - 1);
}
export function toPosition(position: IPosition): types.Position {
return new types.Position(position.lineNumber - 1, position.column - 1);
}
export function fromPosition(position: types.Position): IPosition {
return { lineNumber: position.line + 1, column: position.character + 1 };
}
export function fromDiagnosticSeverity(value: number): Severity {
switch (value) {
case types.DiagnosticSeverity.Error:
return Severity.Error;
case types.DiagnosticSeverity.Warning:
return Severity.Warning;
case types.DiagnosticSeverity.Information:
return Severity.Info;
case types.DiagnosticSeverity.Hint:
return Severity.Ignore;
}
return Severity.Error;
}
export function toDiagnosticSeverty(value: Severity): types.DiagnosticSeverity {
switch (value) {
case Severity.Info:
return types.DiagnosticSeverity.Information;
case Severity.Warning:
return types.DiagnosticSeverity.Warning;
case Severity.Error:
return types.DiagnosticSeverity.Error;
case Severity.Ignore:
return types.DiagnosticSeverity.Hint;
}
return types.DiagnosticSeverity.Error;
}
export function fromViewColumn(column?: vscode.ViewColumn): EditorPosition {
let editorColumn = EditorPosition.ONE;
if (typeof column !== 'number') {
// stick with ONE
} else if (column === <number>types.ViewColumn.Two) {
editorColumn = EditorPosition.TWO;
} else if (column === <number>types.ViewColumn.Three) {
editorColumn = EditorPosition.THREE;
}
return editorColumn;
}
export function toViewColumn(position?: EditorPosition): vscode.ViewColumn {
if (typeof position !== 'number') {
return undefined;
}
if (position === EditorPosition.ONE) {
return <number>types.ViewColumn.One;
} else if (position === EditorPosition.TWO) {
return <number>types.ViewColumn.Two;
} else if (position === EditorPosition.THREE) {
return <number>types.ViewColumn.Three;
}
return undefined;
}
function isDecorationOptions(something: any): something is vscode.DecorationOptions {
return (typeof something.range !== 'undefined');
}
function isDecorationOptionsArr(something: vscode.Range[] | vscode.DecorationOptions[]): something is vscode.DecorationOptions[] {
if (something.length === 0) {
return true;
}
return isDecorationOptions(something[0]) ? true : false;
}
export namespace MarkdownString {
export function fromMany(markup: (vscode.MarkdownString | vscode.MarkedString)[]): htmlContent.IMarkdownString[] {
return markup.map(MarkdownString.from);
}
interface Codeblock {
language: string;
value: string;
}
function isCodeblock(thing: any): thing is Codeblock {
return typeof thing === 'object'
&& typeof (<Codeblock>thing).language === 'string'
&& typeof (<Codeblock>thing).value === 'string';
}
export function from(markup: vscode.MarkdownString | vscode.MarkedString): htmlContent.IMarkdownString {
if (isCodeblock(markup)) {
const { language, value } = markup;
return { value: '```' + language + '\n' + value + '\n```\n' };
} else if (htmlContent.isMarkdownString(markup)) {
return markup;
} else if (typeof markup === 'string') {
return { value: <string>markup, isTrusted: true };
} else {
return { value: '' };
}
}
export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString {
const ret = new htmlContent.MarkdownString(value.value);
ret.isTrusted = value.isTrusted;
return ret;
}
}
export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.DecorationOptions[]): IDecorationOptions[] {
if (isDecorationOptionsArr(ranges)) {
return ranges.map(r => {
return {
range: fromRange(r.range),
hoverMessage: Array.isArray(r.hoverMessage) ? MarkdownString.fromMany(r.hoverMessage) : r.hoverMessage && MarkdownString.from(r.hoverMessage),
renderOptions: <any> /* URI vs Uri */r.renderOptions
};
});
} else {
return ranges.map((r): IDecorationOptions => {
return {
range: fromRange(r)
};
});
}
}
export const TextEdit = {
from(edit: vscode.TextEdit): modes.TextEdit {
return <modes.TextEdit>{
text: edit.newText,
eol: EndOfLine.from(edit.newEol),
range: fromRange(edit.range)
};
},
to(edit: modes.TextEdit): vscode.TextEdit {
let result = new types.TextEdit(toRange(edit.range), edit.text);
result.newEol = EndOfLine.to(edit.eol);
return result;
}
};
export namespace SymbolKind {
const _fromMapping: { [kind: number]: modes.SymbolKind } = Object.create(null);
_fromMapping[types.SymbolKind.File] = modes.SymbolKind.File;
_fromMapping[types.SymbolKind.Module] = modes.SymbolKind.Module;
_fromMapping[types.SymbolKind.Namespace] = modes.SymbolKind.Namespace;
_fromMapping[types.SymbolKind.Package] = modes.SymbolKind.Package;
_fromMapping[types.SymbolKind.Class] = modes.SymbolKind.Class;
_fromMapping[types.SymbolKind.Method] = modes.SymbolKind.Method;
_fromMapping[types.SymbolKind.Property] = modes.SymbolKind.Property;
_fromMapping[types.SymbolKind.Field] = modes.SymbolKind.Field;
_fromMapping[types.SymbolKind.Constructor] = modes.SymbolKind.Constructor;
_fromMapping[types.SymbolKind.Enum] = modes.SymbolKind.Enum;
_fromMapping[types.SymbolKind.Interface] = modes.SymbolKind.Interface;
_fromMapping[types.SymbolKind.Function] = modes.SymbolKind.Function;
_fromMapping[types.SymbolKind.Variable] = modes.SymbolKind.Variable;
_fromMapping[types.SymbolKind.Constant] = modes.SymbolKind.Constant;
_fromMapping[types.SymbolKind.String] = modes.SymbolKind.String;
_fromMapping[types.SymbolKind.Number] = modes.SymbolKind.Number;
_fromMapping[types.SymbolKind.Boolean] = modes.SymbolKind.Boolean;
_fromMapping[types.SymbolKind.Array] = modes.SymbolKind.Array;
_fromMapping[types.SymbolKind.Object] = modes.SymbolKind.Object;
_fromMapping[types.SymbolKind.Key] = modes.SymbolKind.Key;
_fromMapping[types.SymbolKind.Null] = modes.SymbolKind.Null;
_fromMapping[types.SymbolKind.EnumMember] = modes.SymbolKind.EnumMember;
_fromMapping[types.SymbolKind.Struct] = modes.SymbolKind.Struct;
_fromMapping[types.SymbolKind.Event] = modes.SymbolKind.Event;
_fromMapping[types.SymbolKind.Operator] = modes.SymbolKind.Operator;
_fromMapping[types.SymbolKind.TypeParameter] = modes.SymbolKind.TypeParameter;
export function from(kind: vscode.SymbolKind): modes.SymbolKind {
return _fromMapping[kind] || modes.SymbolKind.Property;
}
export function to(kind: modes.SymbolKind): vscode.SymbolKind {
for (let k in _fromMapping) {
if (_fromMapping[k] === kind) {
return Number(k);
}
}
return types.SymbolKind.Property;
}
}
export function fromSymbolInformation(info: vscode.SymbolInformation): modes.SymbolInformation {
return <modes.SymbolInformation>{
name: info.name,
kind: SymbolKind.from(info.kind),
containerName: info.containerName,
location: location.from(info.location)
};
}
export function toSymbolInformation(bearing: modes.SymbolInformation): types.SymbolInformation {
return new types.SymbolInformation(
bearing.name,
SymbolKind.to(bearing.kind),
bearing.containerName,
location.to(bearing.location)
);
}
export const location = {
from(value: vscode.Location): modes.Location {
return {
range: value.range && fromRange(value.range),
uri: <URI>value.uri
};
},
to(value: modes.Location): types.Location {
return new types.Location(value.uri, toRange(value.range));
}
};
export function fromHover(hover: vscode.Hover): modes.Hover {
return <modes.Hover>{
range: fromRange(hover.range),
contents: MarkdownString.fromMany(hover.contents)
};
}
export function toHover(info: modes.Hover): types.Hover {
return new types.Hover(info.contents.map(MarkdownString.to), toRange(info.range));
}
export function toDocumentHighlight(occurrence: modes.DocumentHighlight): types.DocumentHighlight {
return new types.DocumentHighlight(toRange(occurrence.range), occurrence.kind);
}
export const CompletionItemKind = {
from(kind: types.CompletionItemKind): modes.SuggestionType {
switch (kind) {
case types.CompletionItemKind.Method: return 'method';
case types.CompletionItemKind.Function: return 'function';
case types.CompletionItemKind.Constructor: return 'constructor';
case types.CompletionItemKind.Field: return 'field';
case types.CompletionItemKind.Variable: return 'variable';
case types.CompletionItemKind.Class: return 'class';
case types.CompletionItemKind.Interface: return 'interface';
case types.CompletionItemKind.Struct: return 'struct';
case types.CompletionItemKind.Module: return 'module';
case types.CompletionItemKind.Property: return 'property';
case types.CompletionItemKind.Unit: return 'unit';
case types.CompletionItemKind.Value: return 'value';
case types.CompletionItemKind.Constant: return 'constant';
case types.CompletionItemKind.Enum: return 'enum';
case types.CompletionItemKind.EnumMember: return 'enum-member';
case types.CompletionItemKind.Keyword: return 'keyword';
case types.CompletionItemKind.Snippet: return 'snippet';
case types.CompletionItemKind.Text: return 'text';
case types.CompletionItemKind.Color: return 'color';
case types.CompletionItemKind.File: return 'file';
case types.CompletionItemKind.Reference: return 'reference';
case types.CompletionItemKind.Folder: return 'folder';
case types.CompletionItemKind.Event: return 'event';
case types.CompletionItemKind.Operator: return 'operator';
case types.CompletionItemKind.TypeParameter: return 'type-parameter';
}
return 'property';
},
to(type: modes.SuggestionType): types.CompletionItemKind {
if (!type) {
return types.CompletionItemKind.Property;
} else {
return types.CompletionItemKind[type.charAt(0).toUpperCase() + type.substr(1)];
}
}
};
export namespace Suggest {
export function to(position: types.Position, suggestion: modes.ISuggestion): types.CompletionItem {
const result = new types.CompletionItem(suggestion.label);
result.insertText = suggestion.insertText;
result.kind = CompletionItemKind.to(suggestion.type);
result.detail = suggestion.detail;
result.documentation = suggestion.documentation;
result.sortText = suggestion.sortText;
result.filterText = suggestion.filterText;
// 'overwrite[Before|After]'-logic
let overwriteBefore = (typeof suggestion.overwriteBefore === 'number') ? suggestion.overwriteBefore : 0;
let startPosition = new types.Position(position.line, Math.max(0, position.character - overwriteBefore));
let endPosition = position;
if (typeof suggestion.overwriteAfter === 'number') {
endPosition = new types.Position(position.line, position.character + suggestion.overwriteAfter);
}
result.range = new types.Range(startPosition, endPosition);
// 'inserText'-logic
if (suggestion.snippetType === 'textmate') {
result.insertText = new types.SnippetString(suggestion.insertText);
} else {
result.insertText = suggestion.insertText;
result.textEdit = new types.TextEdit(result.range, result.insertText);
}
// TODO additionalEdits, command
return result;
}
};
export namespace SignatureHelp {
export function from(signatureHelp: types.SignatureHelp): modes.SignatureHelp {
return signatureHelp;
}
export function to(hints: modes.SignatureHelp): types.SignatureHelp {
return hints;
}
}
export namespace DocumentLink {
export function from(link: vscode.DocumentLink): modes.ILink {
return {
range: fromRange(link.range),
url: link.target && link.target.toString()
};
}
export function to(link: modes.ILink): vscode.DocumentLink {
return new types.DocumentLink(toRange(link.range), link.url && URI.parse(link.url));
}
}
export namespace TextDocumentSaveReason {
export function to(reason: SaveReason): vscode.TextDocumentSaveReason {
switch (reason) {
case SaveReason.AUTO:
return types.TextDocumentSaveReason.AfterDelay;
case SaveReason.EXPLICIT:
return types.TextDocumentSaveReason.Manual;
case SaveReason.FOCUS_CHANGE:
case SaveReason.WINDOW_CHANGE:
return types.TextDocumentSaveReason.FocusOut;
}
}
}
export namespace EndOfLine {
export function from(eol: vscode.EndOfLine): EndOfLineSequence {
if (eol === types.EndOfLine.CRLF) {
return EndOfLineSequence.CRLF;
} else if (eol === types.EndOfLine.LF) {
return EndOfLineSequence.LF;
}
return undefined;
}
export function to(eol: EndOfLineSequence): vscode.EndOfLine {
if (eol === EndOfLineSequence.CRLF) {
return types.EndOfLine.CRLF;
} else if (eol === EndOfLineSequence.LF) {
return types.EndOfLine.LF;
}
return undefined;
}
}
export namespace ProgressLocation {
export function from(loc: vscode.ProgressLocation): MainProgressLocation {
switch (loc) {
case types.ProgressLocation.SourceControl: return MainProgressLocation.Scm;
case types.ProgressLocation.Window: return MainProgressLocation.Window;
}
return undefined;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
import { ExtHostWindowShape, MainContext, MainThreadWindowShape } from './extHost.protocol';
import { WindowState } from 'vscode';
export class ExtHostWindow implements ExtHostWindowShape {
private static InitialState: WindowState = {
focused: true
};
private _proxy: MainThreadWindowShape;
private _onDidChangeWindowState = new Emitter<WindowState>();
readonly onDidChangeWindowState: Event<WindowState> = this._onDidChangeWindowState.event;
private _state = ExtHostWindow.InitialState;
get state(): WindowState { return this._state; }
constructor(threadService: IThreadService) {
this._proxy = threadService.get(MainContext.MainThreadWindow);
this._proxy.$getWindowVisibility().then(isFocused => this.$onDidChangeWindowFocus(isFocused));
}
$onDidChangeWindowFocus(focused: boolean): void {
if (focused === this._state.focused) {
return;
}
this._state = { ...this._state, focused };
this._onDidChangeWindowState.fire(this._state);
}
}

View File

@@ -0,0 +1,259 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import Event, { Emitter } from 'vs/base/common/event';
import { normalize } from 'vs/base/common/paths';
import { delta } from 'vs/base/common/arrays';
import { relative, basename } from 'path';
import { Workspace } from 'vs/platform/workspace/common/workspace';
import { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
import { TPromise } from 'vs/base/common/winjs.base';
import { fromRange, EndOfLine } from 'vs/workbench/api/node/extHostTypeConverters';
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
import { compare } from 'vs/base/common/strings';
import { asWinJsPromise } from 'vs/base/common/async';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { TrieMap } from 'vs/base/common/map';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Progress } from 'vs/platform/progress/common/progress';
class Workspace2 extends Workspace {
static fromData(data: IWorkspaceData) {
return data ? new Workspace2(data) : null;
}
private readonly _folder: vscode.WorkspaceFolder[] = [];
private readonly _structure = new TrieMap<vscode.WorkspaceFolder>(s => s.split('/'));
private constructor(data: IWorkspaceData) {
super(data.id, data.name, data.roots);
// setup the workspace folder data structure
this.roots.forEach((uri, index) => {
const folder = {
name: basename(uri.fsPath),
uri,
index
};
this._folder.push(folder);
this._structure.insert(folder.uri.toString(), folder);
});
}
get folders(): vscode.WorkspaceFolder[] {
return this._folder.slice(0);
}
getWorkspaceFolder(uri: URI): vscode.WorkspaceFolder {
let str = uri.toString();
let folder = this._structure.lookUp(str);
if (folder) {
// `uri` is a workspace folder so we
let parts = str.split('/');
while (parts.length) {
if (parts.pop()) {
break;
}
}
str = parts.join('/');
}
return this._structure.findSubstr(str);
}
}
export class ExtHostWorkspace implements ExtHostWorkspaceShape {
private static _requestIdPool = 0;
private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
private readonly _proxy: MainThreadWorkspaceShape;
private _workspace: Workspace2;
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
constructor(mainContext: IMainContext, data: IWorkspaceData) {
this._proxy = mainContext.get(MainContext.MainThreadWorkspace);
this._workspace = Workspace2.fromData(data);
}
// --- workspace ---
get workspace(): Workspace {
return this._workspace;
}
getWorkspaceFolders(): vscode.WorkspaceFolder[] {
if (!this._workspace) {
return undefined;
} else {
return this._workspace.folders.slice(0);
}
}
getWorkspaceFolder(uri: vscode.Uri): vscode.WorkspaceFolder {
if (!this._workspace) {
return undefined;
}
return this._workspace.getWorkspaceFolder(<URI>uri);
}
getPath(): string {
// this is legacy from the days before having
// multi-root and we keep it only alive if there
// is just one workspace folder.
if (!this._workspace) {
return undefined;
}
const { roots } = this._workspace;
if (roots.length === 0) {
return undefined;
}
return roots[0].fsPath;
}
getRelativePath(pathOrUri: string | vscode.Uri, includeWorkspace?: boolean): string {
let path: string;
if (typeof pathOrUri === 'string') {
path = pathOrUri;
} else if (typeof pathOrUri !== 'undefined') {
path = pathOrUri.fsPath;
}
if (!path) {
return path;
}
const folder = this.getWorkspaceFolder(typeof pathOrUri === 'string'
? URI.file(pathOrUri)
: pathOrUri
);
if (!folder) {
return normalize(path);
}
if (typeof includeWorkspace === 'undefined') {
includeWorkspace = this.workspace.roots.length > 1;
}
let result = relative(folder.uri.fsPath, path);
if (includeWorkspace) {
result = `${folder.name}/${result}`;
}
return normalize(result);
}
$acceptWorkspaceData(data: IWorkspaceData): void {
// keep old workspace folder, build new workspace, and
// capture new workspace folders. Compute delta between
// them send that as event
const oldRoots = this._workspace ? this._workspace.folders.sort(ExtHostWorkspace._compareWorkspaceFolder) : [];
this._workspace = Workspace2.fromData(data);
const newRoots = this._workspace ? this._workspace.folders.sort(ExtHostWorkspace._compareWorkspaceFolder) : [];
const { added, removed } = delta(oldRoots, newRoots, ExtHostWorkspace._compareWorkspaceFolder);
// {{SQL CARBON EDIT}} - fix build break Argument of type 'Readonly<...
this._onDidChangeWorkspace.fire({
added: added,
removed: removed
});
}
private static _compareWorkspaceFolder(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
return compare(a.uri.toString(), b.uri.toString());
}
// --- search ---
findFiles(include: string, exclude: string, maxResults?: number, token?: vscode.CancellationToken): Thenable<vscode.Uri[]> {
const requestId = ExtHostWorkspace._requestIdPool++;
const result = this._proxy.$startSearch(include, exclude, maxResults, requestId);
if (token) {
token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId));
}
return result;
}
saveAll(includeUntitled?: boolean): Thenable<boolean> {
return this._proxy.$saveAll(includeUntitled);
}
appyEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
let resourceEdits: IResourceEdit[] = [];
let entries = edit.entries();
for (let entry of entries) {
let [uri, edits] = entry;
for (let edit of edits) {
resourceEdits.push({
resource: <URI>uri,
newText: edit.newText,
newEol: EndOfLine.from(edit.newEol),
range: edit.range && fromRange(edit.range)
});
}
}
return this._proxy.$applyWorkspaceEdit(resourceEdits);
}
// --- EXPERIMENT: workspace resolver
private _handlePool = 0;
private readonly _fsProvider = new Map<number, vscode.FileSystemProvider>();
private readonly _searchSession = new Map<number, CancellationTokenSource>();
registerFileSystemProvider(authority: string, provider: vscode.FileSystemProvider): vscode.Disposable {
const handle = ++this._handlePool;
this._fsProvider.set(handle, provider);
const reg = provider.onDidChange(e => this._proxy.$onFileSystemChange(handle, <URI>e));
this._proxy.$registerFileSystemProvider(handle, authority);
return new Disposable(() => {
this._fsProvider.delete(handle);
reg.dispose();
});
}
$resolveFile(handle: number, resource: URI): TPromise<string> {
const provider = this._fsProvider.get(handle);
return asWinJsPromise(token => provider.resolveContents(resource));
}
$storeFile(handle: number, resource: URI, content: string): TPromise<any> {
const provider = this._fsProvider.get(handle);
return asWinJsPromise(token => provider.writeContents(resource, content));
}
$startSearch(handle: number, session: number, query: string): void {
const provider = this._fsProvider.get(handle);
const source = new CancellationTokenSource();
const progress = new Progress<any>(chunk => this._proxy.$updateSearchSession(session, chunk));
this._searchSession.set(session, source);
TPromise.wrap(provider.findFiles(query, progress, source.token)).then(() => {
this._proxy.$finishSearchSession(session);
}, err => {
this._proxy.$finishSearchSession(session, err);
});
}
$cancelSearch(handle: number, session: number): void {
if (this._searchSession.has(session)) {
this._searchSession.get(session).cancel();
this._searchSession.delete(session);
}
}
}

View File

@@ -0,0 +1,375 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
import types = require('vs/base/common/types');
import { Action, IAction } from 'vs/base/common/actions';
import { BaseActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
/**
* The action bar contributor allows to add actions to an actionbar in a given context.
*/
export class ActionBarContributor {
/**
* Returns true if this contributor has actions for the given context.
*/
public hasActions(context: any): boolean {
return false;
}
/**
* Returns an array of primary actions in the given context.
*/
public getActions(context: any): IAction[] {
return [];
}
/**
* Returns true if this contributor has secondary actions for the given context.
*/
public hasSecondaryActions(context: any): boolean {
return false;
}
/**
* Returns an array of secondary actions in the given context.
*/
public getSecondaryActions(context: any): IAction[] {
return [];
}
/**
* Can return a specific IActionItem to render the given action.
*/
public getActionItem(context: any, action: Action): BaseActionItem {
return null;
}
}
/**
* Some predefined scopes to contribute actions to
*/
export const Scope = {
/**
* Actions inside the global activity bar (DEPRECATED)
*/
GLOBAL: 'global',
/**
* Actions inside viewlets.
*/
VIEWLET: 'viewlet',
/**
* Actions inside panels.
*/
PANEL: 'panel',
/**
* Actions inside editors.
*/
EDITOR: 'editor',
/**
* Actions inside tree widgets.
*/
VIEWER: 'viewer'
};
/**
* The ContributableActionProvider leverages the actionbar contribution model to find actions.
*/
export class ContributableActionProvider implements IActionProvider {
private registry: IActionBarRegistry;
constructor() {
this.registry = Registry.as<IActionBarRegistry>(Extensions.Actionbar);
}
private toContext(tree: ITree, element: any): any {
return {
viewer: tree,
element: element
};
}
public hasActions(tree: ITree, element: any): boolean {
let context = this.toContext(tree, element);
let contributors = this.registry.getActionBarContributors(Scope.VIEWER);
for (let i = 0; i < contributors.length; i++) {
let contributor = contributors[i];
if (contributor.hasActions(context)) {
return true;
}
}
return false;
}
public getActions(tree: ITree, element: any): TPromise<IAction[]> {
let actions: IAction[] = [];
let context = this.toContext(tree, element);
// Collect Actions
let contributors = this.registry.getActionBarContributors(Scope.VIEWER);
for (let i = 0; i < contributors.length; i++) {
let contributor = contributors[i];
if (contributor.hasActions(context)) {
actions.push(...contributor.getActions(context));
}
}
return TPromise.as(prepareActions(actions));
}
public hasSecondaryActions(tree: ITree, element: any): boolean {
let context = this.toContext(tree, element);
let contributors = this.registry.getActionBarContributors(Scope.VIEWER);
for (let i = 0; i < contributors.length; i++) {
let contributor = contributors[i];
if (contributor.hasSecondaryActions(context)) {
return true;
}
}
return false;
}
public getSecondaryActions(tree: ITree, element: any): TPromise<IAction[]> {
let actions: IAction[] = [];
let context = this.toContext(tree, element);
// Collect Actions
let contributors = this.registry.getActionBarContributors(Scope.VIEWER);
for (let i = 0; i < contributors.length; i++) {
let contributor = contributors[i];
if (contributor.hasSecondaryActions(context)) {
actions.push(...contributor.getSecondaryActions(context));
}
}
return TPromise.as(prepareActions(actions));
}
public getActionItem(tree: ITree, element: any, action: Action): BaseActionItem {
let contributors = this.registry.getActionBarContributors(Scope.VIEWER);
let context = this.toContext(tree, element);
for (let i = contributors.length - 1; i >= 0; i--) {
let contributor = contributors[i];
let itemProvider = contributor.getActionItem(context, action);
if (itemProvider) {
return itemProvider;
}
}
return null;
}
}
// Helper function used in parts to massage actions before showing in action areas
export function prepareActions(actions: IAction[]): IAction[] {
if (!actions.length) {
return actions;
}
// Patch order if not provided
for (let l = 0; l < actions.length; l++) {
let a = <any>actions[l];
if (types.isUndefinedOrNull(a.order)) {
a.order = l;
}
}
// Sort by order
actions = actions.sort((first: Action, second: Action) => {
let firstOrder = first.order;
let secondOrder = second.order;
if (firstOrder < secondOrder) {
return -1;
} else if (firstOrder > secondOrder) {
return 1;
} else {
return 0;
}
});
// Clean up leading separators
let firstIndexOfAction = -1;
for (let i = 0; i < actions.length; i++) {
if (actions[i].id === Separator.ID) {
continue;
}
firstIndexOfAction = i;
break;
}
if (firstIndexOfAction === -1) {
return [];
}
actions = actions.slice(firstIndexOfAction);
// Clean up trailing separators
for (let h = actions.length - 1; h >= 0; h--) {
let isSeparator = actions[h].id === Separator.ID;
if (isSeparator) {
actions.splice(h, 1);
} else {
break;
}
}
// Clean up separator duplicates
let foundAction = false;
for (let k = actions.length - 1; k >= 0; k--) {
let isSeparator = actions[k].id === Separator.ID;
if (isSeparator && !foundAction) {
actions.splice(k, 1);
} else if (!isSeparator) {
foundAction = true;
} else if (isSeparator) {
foundAction = false;
}
}
return actions;
}
export const Extensions = {
Actionbar: 'workbench.contributions.actionbar'
};
export interface IActionBarRegistry {
/**
* Goes through all action bar contributors and asks them for contributed actions for
* the provided scope and context. Supports primary actions.
*/
getActionBarActionsForContext(scope: string, context: any): IAction[];
/**
* Goes through all action bar contributors and asks them for contributed actions for
* the provided scope and context. Supports secondary actions.
*/
getSecondaryActionBarActionsForContext(scope: string, context: any): IAction[];
/**
* Goes through all action bar contributors and asks them for contributed action item for
* the provided scope and context.
*/
getActionItemForContext(scope: string, context: any, action: Action): BaseActionItem;
/**
* Registers an Actionbar contributor. It will be called to contribute actions to all the action bars
* that are used in the Workbench in the given scope.
*/
registerActionBarContributor(scope: string, ctor: IConstructorSignature0<ActionBarContributor>): void;
/**
* Returns an array of registered action bar contributors known to the workbench for the given scope.
*/
getActionBarContributors(scope: string): ActionBarContributor[];
setInstantiationService(service: IInstantiationService): void;
}
class ActionBarRegistry implements IActionBarRegistry {
private actionBarContributorConstructors: { scope: string; ctor: IConstructorSignature0<ActionBarContributor>; }[] = [];
private actionBarContributorInstances: { [scope: string]: ActionBarContributor[] } = Object.create(null);
private instantiationService: IInstantiationService;
public setInstantiationService(service: IInstantiationService): void {
this.instantiationService = service;
while (this.actionBarContributorConstructors.length > 0) {
let entry = this.actionBarContributorConstructors.shift();
this.createActionBarContributor(entry.scope, entry.ctor);
}
}
private createActionBarContributor(scope: string, ctor: IConstructorSignature0<ActionBarContributor>): void {
const instance = this.instantiationService.createInstance(ctor);
let target = this.actionBarContributorInstances[scope];
if (!target) {
target = this.actionBarContributorInstances[scope] = [];
}
target.push(instance);
}
private getContributors(scope: string): ActionBarContributor[] {
return this.actionBarContributorInstances[scope] || [];
}
public getActionBarActionsForContext(scope: string, context: any): IAction[] {
let actions: IAction[] = [];
// Go through contributors for scope
this.getContributors(scope).forEach((contributor: ActionBarContributor) => {
// Primary Actions
if (contributor.hasActions(context)) {
actions.push(...contributor.getActions(context));
}
});
return actions;
}
public getSecondaryActionBarActionsForContext(scope: string, context: any): IAction[] {
let actions: IAction[] = [];
// Go through contributors
this.getContributors(scope).forEach((contributor: ActionBarContributor) => {
// Secondary Actions
if (contributor.hasSecondaryActions(context)) {
actions.push(...contributor.getSecondaryActions(context));
}
});
return actions;
}
public getActionItemForContext(scope: string, context: any, action: Action): BaseActionItem {
let contributors = this.getContributors(scope);
for (let i = 0; i < contributors.length; i++) {
let contributor = contributors[i];
let item = contributor.getActionItem(context, action);
if (item) {
return item;
}
}
return null;
}
public registerActionBarContributor(scope: string, ctor: IConstructorSignature0<ActionBarContributor>): void {
if (!this.instantiationService) {
this.actionBarContributorConstructors.push({
scope: scope,
ctor: ctor
});
} else {
this.createActionBarContributor(scope, ctor);
}
}
public getActionBarContributors(scope: string): ActionBarContributor[] {
return this.getContributors(scope).slice(0);
}
}
Registry.add(Extensions.Actionbar, new ActionBarRegistry());

View File

@@ -0,0 +1,93 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as Path from 'vs/base/common/paths';
import URI from 'vs/base/common/uri';
import * as Labels from 'vs/base/common/labels';
import * as Platform from 'vs/base/common/platform';
import { Action } from 'vs/base/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEditor } from 'vs/platform/editor/common/editor';
import { IFileService } from 'vs/platform/files/common/files';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
class ConfigureLocaleAction extends Action {
public static ID = 'workbench.action.configureLocale';
public static LABEL = nls.localize('configureLocale', "Configure Language");
private static DEFAULT_CONTENT: string = [
'{',
`\t// ${nls.localize('displayLanguage', 'Defines VSCode\'s display language.')}`,
`\t// ${nls.localize('doc', 'See {0} for a list of supported languages.', 'https://go.microsoft.com/fwlink/?LinkId=761051')}`,
`\t// ${nls.localize('restart', 'Changing the value requires restarting VSCode.')}`,
`\t"locale":"${Platform.language}"`,
'}'
].join('\n');
constructor(id, label,
@IFileService private fileService: IFileService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
) {
super(id, label);
}
public run(event?: any): TPromise<IEditor> {
const file = URI.file(Path.join(this.environmentService.appSettingsHome, 'locale.json'));
return this.fileService.resolveFile(file).then(null, (error) => {
return this.fileService.createFile(file, ConfigureLocaleAction.DEFAULT_CONTENT);
}).then((stat) => {
if (!stat) {
return undefined;
}
return this.editorService.openEditor({
resource: stat.resource,
options: {
forceOpen: true
}
});
}, (error) => {
throw new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", Labels.getPathLabel(file, this.contextService), error));
});
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ConfigureLocaleAction, ConfigureLocaleAction.ID, ConfigureLocaleAction.LABEL), 'Configure Language');
const schemaId = 'vscode://schemas/locale';
// Keep en-US since we generated files with that content.
const schema: IJSONSchema =
{
id: schemaId,
description: 'Locale Definition file',
type: 'object',
default: {
'locale': 'en'
},
required: ['locale'],
properties: {
locale: {
type: 'string',
enum: ['de', 'en', 'en-US', 'es', 'fr', 'it', 'ja', 'ko', 'ru', 'zh-CN', 'zh-TW'],
description: nls.localize('JsonSchema.locale', 'The UI Language to use.')
}
}
};
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
jsonRegistry.registerSchema(schemaId, schema);

View File

@@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.vs .monaco-workbench .toggle-editor-layout {
background-image: url('editor-layout.svg');
}
.vs-dark .monaco-workbench .toggle-editor-layout,
.hc-black .monaco-workbench .toggle-editor-layout {
background-image: url('editor-layout-inverse.svg') !important;
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2b282e}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 16V0h11v6h5v10H0z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M4 14H2V4h7v3H4v7zm10 0H5v-4h9v4z" id="iconFg" style="display: none;"/><path class="icon-vs-bg" d="M10 7V1H1v14h14V7h-5zm-6 7H2V4h7v3H4v7zm10 0H5v-4h9v4z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 562 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 16V0h11v6h5v10H0z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M4 14H2V4h7v3H4v7zm10 0H5v-4h9v4z" id="iconFg" style="display: none;"/><path class="icon-vs-bg" d="M10 7V1H1v14h14V7h-5zm-6 7H2V4h7v3H4v7zm10 0H5v-4h9v4z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 562 B

View File

@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
export class ToggleActivityBarVisibilityAction extends Action {
public static ID = 'workbench.action.toggleActivityBarVisibility';
public static LABEL = nls.localize('toggleActivityBar', "Toggle Activity Bar Visibility");
private static activityBarVisibleKey = 'workbench.activityBar.visible';
constructor(
id: string,
label: string,
@IPartService private partService: IPartService,
@IConfigurationEditingService private configurationEditingService: IConfigurationEditingService
) {
super(id, label);
this.enabled = !!this.partService;
}
public run(): TPromise<any> {
const visibility = this.partService.isVisible(Parts.ACTIVITYBAR_PART);
const newVisibilityValue = !visibility;
this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleActivityBarVisibilityAction.activityBarVisibleKey, value: newVisibilityValue });
return TPromise.as(null);
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, ToggleActivityBarVisibilityAction.LABEL), 'View: Toggle Activity Bar Visibility', nls.localize('view', "View"));

View File

@@ -0,0 +1,84 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { IEditorGroupService, GroupOrientation } from 'vs/workbench/services/group/common/groupService';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
export class ToggleEditorLayoutAction extends Action {
public static ID = 'workbench.action.toggleEditorGroupLayout';
public static LABEL = nls.localize('toggleEditorGroupLayout', "Toggle Editor Group Vertical/Horizontal Layout");
private toDispose: IDisposable[];
constructor(
id: string,
label: string,
@IEditorGroupService private editorGroupService: IEditorGroupService
) {
super(id, label);
this.toDispose = [];
this.class = 'toggle-editor-layout';
this.updateEnablement();
this.updateLabel();
this.registerListeners();
}
private registerListeners(): void {
this.toDispose.push(this.editorGroupService.onEditorsChanged(() => this.updateEnablement()));
this.toDispose.push(this.editorGroupService.onGroupOrientationChanged(() => this.updateLabel()));
}
private updateLabel(): void {
const editorGroupLayoutVertical = (this.editorGroupService.getGroupOrientation() !== 'horizontal');
this.label = editorGroupLayoutVertical ? nls.localize('horizontalLayout', "Horizontal Editor Group Layout") : nls.localize('verticalLayout', "Vertical Editor Group Layout");
}
private updateEnablement(): void {
this.enabled = this.editorGroupService.getStacksModel().groups.length > 0;
}
public run(): TPromise<any> {
const groupOrientiation = this.editorGroupService.getGroupOrientation();
const newGroupOrientation: GroupOrientation = (groupOrientiation === 'vertical') ? 'horizontal' : 'vertical';
this.editorGroupService.setGroupOrientation(newGroupOrientation);
return TPromise.as(null);
}
public dispose(): void {
this.toDispose = dispose(this.toDispose);
super.dispose();
}
}
CommandsRegistry.registerCommand('_workbench.editor.setGroupOrientation', function (accessor: ServicesAccessor, args: [GroupOrientation]) {
const editorGroupService = accessor.get(IEditorGroupService);
const [orientation] = args;
editorGroupService.setGroupOrientation(orientation);
return TPromise.as<void>(null);
});
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
const group = nls.localize('view', "View");
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_1, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_1 } }), 'View: Toggle Editor Group Vertical/Horizontal Layout', group);

View File

@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IPartService, Position } from 'vs/workbench/services/part/common/partService';
export class ToggleSidebarPositionAction extends Action {
public static ID = 'workbench.action.toggleSidebarPosition';
public static LABEL = nls.localize('toggleLocation', "Toggle Side Bar Location");
private static sidebarPositionConfigurationKey = 'workbench.sideBar.location';
constructor(
id: string,
label: string,
@IPartService private partService: IPartService,
@IConfigurationEditingService private configurationEditingService: IConfigurationEditingService
) {
super(id, label);
this.enabled = !!this.partService && !!this.configurationEditingService;
}
public run(): TPromise<any> {
const position = this.partService.getSideBarPosition();
const newPositionValue = (position === Position.LEFT) ? 'right' : 'left';
this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleSidebarPositionAction.sidebarPositionConfigurationKey, value: newPositionValue });
return TPromise.as(null);
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.LABEL), 'View: Toggle Side Bar Location', nls.localize('view', "View"));

View File

@@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
export class ToggleSidebarVisibilityAction extends Action {
public static ID = 'workbench.action.toggleSidebarVisibility';
public static LABEL = nls.localize('toggleSidebar', "Toggle Side Bar Visibility");
constructor(
id: string,
label: string,
@IPartService private partService: IPartService
) {
super(id, label);
this.enabled = !!this.partService;
}
public run(): TPromise<any> {
const hideSidebar = this.partService.isVisible(Parts.SIDEBAR_PART);
return this.partService.setSideBarHidden(hideSidebar);
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSidebarVisibilityAction, ToggleSidebarVisibilityAction.ID, ToggleSidebarVisibilityAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_B }), 'View: Toggle Side Bar Visibility', nls.localize('view', "View"));

View File

@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import { Registry } from 'vs/platform/registry/common/platform';
import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
export class ToggleStatusbarVisibilityAction extends Action {
public static ID = 'workbench.action.toggleStatusbarVisibility';
public static LABEL = nls.localize('toggleStatusbar', "Toggle Status Bar Visibility");
private static statusbarVisibleKey = 'workbench.statusBar.visible';
constructor(
id: string,
label: string,
@IPartService private partService: IPartService,
@IConfigurationEditingService private configurationEditingService: IConfigurationEditingService
) {
super(id, label);
this.enabled = !!this.partService;
}
public run(): TPromise<any> {
const visibility = this.partService.isVisible(Parts.STATUSBAR_PART);
const newVisibilityValue = !visibility;
this.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: ToggleStatusbarVisibilityAction.statusbarVisibleKey, value: newVisibilityValue });
return TPromise.as(null);
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, ToggleStatusbarVisibilityAction.LABEL), 'View: Toggle Status Bar Visibility', nls.localize('view', "View"));

View File

@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import { Action } from 'vs/base/common/actions';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actionRegistry';
import { IPartService } from 'vs/workbench/services/part/common/partService';
class ToggleZenMode extends Action {
public static ID = 'workbench.action.toggleZenMode';
public static LABEL = nls.localize('toggleZenMode', "Toggle Zen Mode");
constructor(
id: string,
label: string,
@IPartService private partService: IPartService
) {
super(id, label);
this.enabled = !!this.partService;
}
public run(): TPromise<any> {
this.partService.toggleZenMode();
return TPromise.as(null);
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleZenMode, ToggleZenMode.ID, ToggleZenMode.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_Z) }), 'View: Toggle Zen Mode', nls.localize('view', "View"));

View File

@@ -0,0 +1,290 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import nls = require('vs/nls');
import { distinct } from 'vs/base/common/arrays';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import URI from 'vs/base/common/uri';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspacesService, WORKSPACE_FILTER } from 'vs/platform/workspaces/common/workspaces';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { isLinux } from 'vs/base/common/platform';
import { dirname } from 'vs/base/common/paths';
import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { isParent } from 'vs/platform/files/common/files';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
export class OpenFolderAction extends Action {
static ID = 'workbench.action.files.openFolder';
static LABEL = nls.localize('openFolder', "Open Folder...");
constructor(
id: string,
label: string,
@IWindowService private windowService: IWindowService
) {
super(id, label);
}
run(event?: any, data?: ITelemetryData): TPromise<any> {
return this.windowService.pickFolderAndOpen({ telemetryExtraData: data });
}
}
export class OpenFileFolderAction extends Action {
static ID = 'workbench.action.files.openFileFolder';
static LABEL = nls.localize('openFileFolder', "Open...");
constructor(
id: string,
label: string,
@IWindowService private windowService: IWindowService
) {
super(id, label);
}
run(event?: any, data?: ITelemetryData): TPromise<any> {
return this.windowService.pickFileFolderAndOpen({ telemetryExtraData: data });
}
}
export abstract class BaseWorkspacesAction extends Action {
constructor(
id: string,
label: string,
protected windowService: IWindowService,
protected environmentService: IEnvironmentService,
protected contextService: IWorkspaceContextService
) {
super(id, label);
}
protected pickFolders(buttonLabel: string, title: string): string[] {
const workspace = this.contextService.getWorkspace();
let defaultPath: string;
if (workspace && workspace.roots.length > 0) {
defaultPath = dirname(workspace.roots[0].fsPath); // pick the parent of the first root by default
}
return this.windowService.showOpenDialog({
buttonLabel,
title,
properties: ['multiSelections', 'openDirectory', 'createDirectory'],
defaultPath
});
}
}
export class AddRootFolderAction extends BaseWorkspacesAction {
static ID = 'workbench.action.addRootFolder';
static LABEL = nls.localize('addFolderToWorkspace', "Add Folder to Workspace...");
constructor(
id: string,
label: string,
@IWindowService windowService: IWindowService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IEnvironmentService environmentService: IEnvironmentService,
@IInstantiationService private instantiationService: IInstantiationService,
@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService,
@IViewletService private viewletService: IViewletService
) {
super(id, label, windowService, environmentService, contextService);
}
public run(): TPromise<any> {
if (!this.contextService.hasWorkspace()) {
return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, []).run();
}
if (this.contextService.hasFolderWorkspace()) {
return this.instantiationService.createInstance(NewWorkspaceAction, NewWorkspaceAction.ID, NewWorkspaceAction.LABEL, this.contextService.getWorkspace().roots).run();
}
const folders = super.pickFolders(mnemonicButtonLabel(nls.localize({ key: 'add', comment: ['&& denotes a mnemonic'] }, "&&Add")), nls.localize('addFolderToWorkspaceTitle', "Add Folder to Workspace"));
if (!folders || !folders.length) {
return TPromise.as(null);
}
return this.workspaceEditingService.addRoots(folders.map(folder => URI.file(folder))).then(() => {
return this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true);
});
}
}
class NewWorkspaceAction extends BaseWorkspacesAction {
static ID = 'workbench.action.newWorkspace';
static LABEL = nls.localize('newWorkspace', "New Workspace...");
constructor(
id: string,
label: string,
private presetRoots: URI[],
@IWindowService windowService: IWindowService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IEnvironmentService environmentService: IEnvironmentService,
@IWorkspacesService protected workspacesService: IWorkspacesService,
@IWindowsService protected windowsService: IWindowsService,
) {
super(id, label, windowService, environmentService, contextService);
}
public run(): TPromise<any> {
const folders = super.pickFolders(mnemonicButtonLabel(nls.localize({ key: 'select', comment: ['&& denotes a mnemonic'] }, "&&Select")), nls.localize('selectWorkspace', "Select Folders for Workspace"));
if (folders && folders.length) {
return this.createWorkspace([...this.presetRoots, ...folders.map(folder => URI.file(folder))]);
}
return TPromise.as(null);
}
private createWorkspace(folders: URI[]): TPromise<void> {
const workspaceFolders = distinct(folders.map(folder => folder.fsPath), folder => isLinux ? folder : folder.toLowerCase());
return this.windowService.createAndOpenWorkspace(workspaceFolders);
}
}
export class RemoveRootFolderAction extends Action {
static ID = 'workbench.action.removeRootFolder';
static LABEL = nls.localize('removeFolderFromWorkspace', "Remove Folder from Workspace");
constructor(
private rootUri: URI,
id: string,
label: string,
@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService
) {
super(id, label);
}
public run(): TPromise<any> {
return this.workspaceEditingService.removeRoots([this.rootUri]);
}
}
export class SaveWorkspaceAsAction extends BaseWorkspacesAction {
static ID = 'workbench.action.saveWorkspaceAs';
static LABEL = nls.localize('saveWorkspaceAsAction', "Save Workspace As...");
constructor(
id: string,
label: string,
@IWindowService windowService: IWindowService,
@IEnvironmentService environmentService: IEnvironmentService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IWorkspacesService protected workspacesService: IWorkspacesService,
@IWindowsService private windowsService: IWindowsService,
@IMessageService private messageService: IMessageService
) {
super(id, label, windowService, environmentService, contextService);
}
public run(): TPromise<any> {
if (!this.contextService.hasWorkspace()) {
this.messageService.show(Severity.Info, nls.localize('saveEmptyWorkspaceNotSupported', "Please open a workspace first to save."));
return TPromise.as(null);
}
const configPath = this.getNewWorkspaceConfigPath();
if (configPath) {
if (this.contextService.hasFolderWorkspace()) {
return this.saveFolderWorkspace(configPath);
}
if (this.contextService.hasMultiFolderWorkspace()) {
return this.saveWorkspace(configPath);
}
}
return TPromise.as(null);
}
private saveWorkspace(configPath: string): TPromise<void> {
return this.windowService.saveAndOpenWorkspace(configPath);
}
private saveFolderWorkspace(configPath: string): TPromise<void> {
const workspaceFolders = this.contextService.getWorkspace().roots.map(root => root.fsPath);
return this.windowService.createAndOpenWorkspace(workspaceFolders, configPath);
}
private getNewWorkspaceConfigPath(): string {
const workspace = this.contextService.getWorkspace();
let defaultPath: string;
if (this.contextService.hasMultiFolderWorkspace() && !this.isUntitledWorkspace(workspace.configuration.fsPath)) {
defaultPath = workspace.configuration.fsPath;
} else if (workspace && workspace.roots.length > 0) {
defaultPath = dirname(workspace.roots[0].fsPath); // pick the parent of the first root by default
}
return this.windowService.showSaveDialog({
buttonLabel: mnemonicButtonLabel(nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")),
title: nls.localize('saveWorkspace', "Save Workspace"),
filters: WORKSPACE_FILTER,
defaultPath
});
}
private isUntitledWorkspace(path: string): boolean {
return isParent(path, this.environmentService.workspacesHome, !isLinux /* ignore case */);
}
}
export class OpenWorkspaceAction extends Action {
static ID = 'workbench.action.openWorkspace';
static LABEL = nls.localize('openWorkspaceAction', "Open Workspace...");
constructor(
id: string,
label: string,
@IWindowService private windowService: IWindowService,
) {
super(id, label);
}
public run(): TPromise<any> {
return this.windowService.openWorkspace();
}
}
export class OpenWorkspaceConfigFileAction extends Action {
public static ID = 'workbench.action.openWorkspaceConfigFile';
public static LABEL = nls.localize('openWorkspaceConfigFile', "Open Workspace Configuration File");
constructor(
id: string,
label: string,
@IWorkspaceContextService private workspaceContextService: IWorkspaceContextService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
) {
super(id, label);
this.enabled = this.workspaceContextService.hasMultiFolderWorkspace();
}
public run(): TPromise<any> {
return this.editorService.openEditor({ resource: this.workspaceContextService.getWorkspace().configuration });
}
}

View File

@@ -0,0 +1,42 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Registry } from 'vs/platform/registry/common/platform';
import { IAction } from 'vs/base/common/actions';
import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
export interface IActivity {
id: string;
name: string;
cssClass: string;
}
export interface IGlobalActivity extends IActivity {
getActions(): IAction[];
}
export const GlobalActivityExtensions = 'workbench.contributions.globalActivities';
export interface IGlobalActivityRegistry {
registerActivity(descriptor: IConstructorSignature0<IGlobalActivity>): void;
getActivities(): IConstructorSignature0<IGlobalActivity>[];
}
export class GlobalActivityRegistry implements IGlobalActivityRegistry {
private activityDescriptors = new Set<IConstructorSignature0<IGlobalActivity>>();
registerActivity(descriptor: IConstructorSignature0<IGlobalActivity>): void {
this.activityDescriptors.add(descriptor);
}
getActivities(): IConstructorSignature0<IGlobalActivity>[] {
const result: IConstructorSignature0<IGlobalActivity>[] = [];
this.activityDescriptors.forEach(d => result.push(d));
return result;
}
}
Registry.add(GlobalActivityExtensions, new GlobalActivityRegistry());

View File

@@ -0,0 +1,273 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions';
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { Component } from 'vs/workbench/common/component';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { AsyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IComposite } from 'vs/workbench/common/composite';
import { IEditorControl } from 'vs/platform/editor/common/editor';
import Event, { Emitter } from 'vs/base/common/event';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/**
* Composites are layed out in the sidebar and panel part of the workbench. At a time only one composite
* can be open in the sidebar, and only one composite can be open in the panel.
* Each composite has a minimized representation that is good enough to provide some
* information about the state of the composite data.
* The workbench will keep a composite alive after it has been created and show/hide it based on
* user interaction. The lifecycle of a composite goes in the order create(), setVisible(true|false),
* layout(), focus(), dispose(). During use of the workbench, a composite will often receive a setVisible,
* layout and focus call, but only one create and dispose call.
*/
export abstract class Composite extends Component implements IComposite {
private _telemetryData: any = {};
private visible: boolean;
private parent: Builder;
private _onTitleAreaUpdate: Emitter<void>;
protected actionRunner: IActionRunner;
/**
* Create a new composite with the given ID and context.
*/
constructor(
id: string,
private _telemetryService: ITelemetryService,
themeService: IThemeService
) {
super(id, themeService);
this.visible = false;
this._onTitleAreaUpdate = new Emitter<void>();
}
public getTitle(): string {
return null;
}
protected get telemetryService(): ITelemetryService {
return this._telemetryService;
}
public get onTitleAreaUpdate(): Event<void> {
return this._onTitleAreaUpdate.event;
}
/**
* Note: Clients should not call this method, the workbench calls this
* method. Calling it otherwise may result in unexpected behavior.
*
* Called to create this composite on the provided builder. This method is only
* called once during the lifetime of the workbench.
* Note that DOM-dependent calculations should be performed from the setVisible()
* call. Only then the composite will be part of the DOM.
*/
public create(parent: Builder): TPromise<void> {
this.parent = parent;
return TPromise.as(null);
}
public updateStyles(): void {
super.updateStyles();
}
/**
* Returns the container this composite is being build in.
*/
public getContainer(): Builder {
return this.parent;
}
/**
* Note: Clients should not call this method, the workbench calls this
* method. Calling it otherwise may result in unexpected behavior.
*
* Called to indicate that the composite has become visible or hidden. This method
* is called more than once during workbench lifecycle depending on the user interaction.
* The composite will be on-DOM if visible is set to true and off-DOM otherwise.
*
* The returned promise is complete when the composite is visible. As such it is valid
* to do a long running operation from this call. Typically this operation should be
* fast though because setVisible might be called many times during a session.
*/
public setVisible(visible: boolean): TPromise<void> {
this.visible = visible;
// Reset telemetry data when composite becomes visible
if (visible) {
this._telemetryData = {};
this._telemetryData.startTime = new Date();
// Only submit telemetry data when not running from an integration test
if (this._telemetryService && this._telemetryService.publicLog) {
const eventName: string = 'compositeOpen';
this._telemetryService.publicLog(eventName, { composite: this.getId() });
}
}
// Send telemetry data when composite hides
else {
this._telemetryData.timeSpent = (Date.now() - this._telemetryData.startTime) / 1000;
delete this._telemetryData.startTime;
// Only submit telemetry data when not running from an integration test
if (this._telemetryService && this._telemetryService.publicLog) {
const eventName: string = 'compositeShown';
this._telemetryData.composite = this.getId();
this._telemetryService.publicLog(eventName, this._telemetryData);
}
}
return TPromise.as(null);
}
/**
* Called when this composite should receive keyboard focus.
*/
public focus(): void {
// Subclasses can implement
}
/**
* Layout the contents of this composite using the provided dimensions.
*/
public abstract layout(dimension: Dimension): void;
/**
* Returns an array of actions to show in the action bar of the composite.
*/
public getActions(): IAction[] {
return [];
}
/**
* Returns an array of actions to show in the action bar of the composite
* in a less prominent way then action from getActions.
*/
public getSecondaryActions(): IAction[] {
return [];
}
/**
* Returns an array of actions to show in the context menu of the composite
*/
public getContextMenuActions(): IAction[] {
return [];
}
/**
* For any of the actions returned by this composite, provide an IActionItem in
* cases where the implementor of the composite wants to override the presentation
* of an action. Returns null to indicate that the action is not rendered through
* an action item.
*/
public getActionItem(action: IAction): IActionItem {
return null;
}
/**
* Returns the instance of IActionRunner to use with this composite for the
* composite tool bar.
*/
public getActionRunner(): IActionRunner {
if (!this.actionRunner) {
this.actionRunner = new ActionRunner();
}
return this.actionRunner;
}
/**
* Method for composite implementors to indicate to the composite container that the title or the actions
* of the composite have changed. Calling this method will cause the container to ask for title (getTitle())
* and actions (getActions(), getSecondaryActions()) if the composite is visible or the next time the composite
* gets visible.
*/
protected updateTitleArea(): void {
this._onTitleAreaUpdate.fire();
}
/**
* Returns true if this composite is currently visible and false otherwise.
*/
public isVisible(): boolean {
return this.visible;
}
/**
* Returns the underlying composite control or null if it is not accessible.
*/
public getControl(): IEditorControl {
return null;
}
public dispose(): void {
this._onTitleAreaUpdate.dispose();
super.dispose();
}
}
/**
* A composite descriptor is a leightweight descriptor of a composite in the workbench.
*/
export abstract class CompositeDescriptor<T extends Composite> extends AsyncDescriptor<T> {
public id: string;
public name: string;
public cssClass: string;
public order: number;
constructor(moduleId: string, ctorName: string, id: string, name: string, cssClass?: string, order?: number) {
super(moduleId, ctorName);
this.id = id;
this.name = name;
this.cssClass = cssClass;
this.order = order;
}
}
export abstract class CompositeRegistry<T extends Composite> {
private composites: CompositeDescriptor<T>[];
constructor() {
this.composites = [];
}
protected registerComposite(descriptor: CompositeDescriptor<T>): void {
if (this.compositeById(descriptor.id) !== null) {
return;
}
this.composites.push(descriptor);
}
public getComposite(id: string): CompositeDescriptor<T> {
return this.compositeById(id);
}
protected getComposites(): CompositeDescriptor<T>[] {
return this.composites.slice(0);
}
protected setComposites(compositesToSet: CompositeDescriptor<T>[]): void {
this.composites = compositesToSet;
}
private compositeById(id: string): CompositeDescriptor<T> {
for (let i = 0; i < this.composites.length; i++) {
if (this.composites[i].id === id) {
return this.composites[i];
}
}
return null;
}
}

View File

@@ -0,0 +1,262 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import uri from 'vs/base/common/uri';
import paths = require('vs/base/common/paths');
import { IconLabel, IIconLabelOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IEditorInput } from 'vs/platform/editor/common/editor';
import { toResource } from 'vs/workbench/common/editor';
import { getPathLabel, IRootProvider } from 'vs/base/common/labels';
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { Schemas } from 'vs/base/common/network';
import { FileKind } from 'vs/platform/files/common/files';
export interface IEditorLabel {
name: string;
description?: string;
resource?: uri;
}
export interface IResourceLabelOptions extends IIconLabelOptions {
fileKind?: FileKind;
}
export class ResourceLabel extends IconLabel {
private toDispose: IDisposable[];
private label: IEditorLabel;
private options: IResourceLabelOptions;
private computedIconClasses: string[];
private lastKnownConfiguredLangId: string;
constructor(
container: HTMLElement,
options: IIconLabelCreationOptions,
@IExtensionService private extensionService: IExtensionService,
@IWorkspaceContextService protected contextService: IWorkspaceContextService,
@IConfigurationService private configurationService: IConfigurationService,
@IModeService private modeService: IModeService,
@IModelService private modelService: IModelService,
@IEnvironmentService protected environmentService: IEnvironmentService
) {
super(container, options);
this.toDispose = [];
this.registerListeners();
}
private registerListeners(): void {
this.extensionService.onReady().then(() => this.render(true /* clear cache */)); // update when extensions are loaded with potentially new languages
this.toDispose.push(this.configurationService.onDidUpdateConfiguration(() => this.render(true /* clear cache */))); // update when file.associations change
}
public setLabel(label: IEditorLabel, options?: IResourceLabelOptions): void {
const hasResourceChanged = this.hasResourceChanged(label, options);
this.label = label;
this.options = options;
this.render(hasResourceChanged);
}
private hasResourceChanged(label: IEditorLabel, options: IResourceLabelOptions): boolean {
const newResource = label ? label.resource : void 0;
const oldResource = this.label ? this.label.resource : void 0;
const newFileKind = options ? options.fileKind : void 0;
const oldFileKind = this.options ? this.options.fileKind : void 0;
if (newFileKind !== oldFileKind) {
return true; // same resource but different kind (file, folder)
}
if (newResource && oldResource) {
return newResource.toString() !== oldResource.toString();
}
if (!newResource && !oldResource) {
return false;
}
return true;
}
public clear(): void {
this.label = void 0;
this.options = void 0;
this.lastKnownConfiguredLangId = void 0;
this.computedIconClasses = void 0;
this.setValue();
}
private render(clearIconCache: boolean): void {
if (this.label) {
const configuredLangId = getConfiguredLangId(this.modelService, this.label.resource);
if (this.lastKnownConfiguredLangId !== configuredLangId) {
clearIconCache = true;
this.lastKnownConfiguredLangId = configuredLangId;
}
}
if (clearIconCache) {
this.computedIconClasses = void 0;
}
if (!this.label) {
return;
}
const resource = this.label.resource;
let title = '';
if (this.options && typeof this.options.title === 'string') {
title = this.options.title;
} else if (resource) {
title = getPathLabel(resource.fsPath, void 0, this.environmentService);
}
if (!this.computedIconClasses) {
this.computedIconClasses = getIconClasses(this.modelService, this.modeService, resource, this.options && this.options.fileKind);
}
let extraClasses = this.computedIconClasses.slice(0);
if (this.options && this.options.extraClasses) {
extraClasses.push(...this.options.extraClasses);
}
const italic = this.options && this.options.italic;
const matches = this.options && this.options.matches;
this.setValue(this.label.name, this.label.description, { title, extraClasses, italic, matches });
}
public dispose(): void {
super.dispose();
this.toDispose = dispose(this.toDispose);
this.label = void 0;
this.options = void 0;
this.lastKnownConfiguredLangId = void 0;
this.computedIconClasses = void 0;
}
}
export class EditorLabel extends ResourceLabel {
public setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void {
this.setLabel({
resource: toResource(editor, { supportSideBySide: true }),
name: editor.getName(),
description: editor.getDescription()
}, options);
}
}
export interface IFileLabelOptions extends IResourceLabelOptions {
hideLabel?: boolean;
hidePath?: boolean;
root?: uri;
}
export class FileLabel extends ResourceLabel {
constructor(
container: HTMLElement,
options: IIconLabelCreationOptions,
@IExtensionService extensionService: IExtensionService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IConfigurationService configurationService: IConfigurationService,
@IModeService modeService: IModeService,
@IModelService modelService: IModelService,
@IEnvironmentService environmentService: IEnvironmentService,
@IUntitledEditorService private untitledEditorService: IUntitledEditorService
) {
super(container, options, extensionService, contextService, configurationService, modeService, modelService, environmentService);
}
public setFile(resource: uri, options: IFileLabelOptions = Object.create(null)): void {
const hidePath = options.hidePath || (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource));
const rootProvider: IRootProvider = options.root ? {
getRoot(): uri { return options.root; },
getWorkspace(): { roots: uri[]; } { return { roots: [options.root] }; },
} : this.contextService;
this.setLabel({
resource,
name: !options.hideLabel ? paths.basename(resource.fsPath) : void 0,
description: !hidePath ? getPathLabel(paths.dirname(resource.fsPath), rootProvider, this.environmentService) : void 0
}, options);
}
}
export function getIconClasses(modelService: IModelService, modeService: IModeService, resource: uri, fileKind?: FileKind): string[] {
// we always set these base classes even if we do not have a path
const classes = fileKind === FileKind.ROOT_FOLDER ? ['rootfolder-icon'] : fileKind === FileKind.FOLDER ? ['folder-icon'] : ['file-icon'];
let path: string;
if (resource) {
path = resource.fsPath;
}
if (path) {
const basename = cssEscape(paths.basename(path).toLowerCase());
// Folders
if (fileKind === FileKind.FOLDER) {
classes.push(`${basename}-name-folder-icon`);
}
// Files
else {
// Name
classes.push(`${basename}-name-file-icon`);
// Extension(s)
const dotSegments = basename.split('.');
for (let i = 1; i < dotSegments.length; i++) {
classes.push(`${dotSegments.slice(i).join('.')}-ext-file-icon`); // add each combination of all found extensions if more than one
}
// Configured Language
let configuredLangId = getConfiguredLangId(modelService, resource);
configuredLangId = configuredLangId || modeService.getModeIdByFilenameOrFirstLine(path);
if (configuredLangId) {
classes.push(`${cssEscape(configuredLangId)}-lang-file-icon`);
}
}
}
return classes;
}
function getConfiguredLangId(modelService: IModelService, resource: uri): string {
let configuredLangId: string;
if (resource) {
const model = modelService.getModel(resource);
if (model) {
const modeId = model.getLanguageIdentifier().language;
if (modeId && modeId !== PLAINTEXT_MODE_ID) {
configuredLangId = modeId; // only take if the mode is specific (aka no just plain text)
}
}
}
return configuredLangId;
}
function cssEscape(val: string): string {
return val.replace(/\s/g, '\\$&'); // make sure to not introduce CSS classes from files that contain whitespace
}

View File

@@ -0,0 +1,595 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { TPromise } from 'vs/base/common/winjs.base';
import * as errors from 'vs/base/common/errors';
import { Part } from 'vs/workbench/browser/part';
import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController';
import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider, Orientation } from 'vs/base/browser/ui/sash/sash';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IPartService, Position, ILayoutOptions, Parts } from 'vs/workbench/services/part/common/partService';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { getZoomFactor } from 'vs/base/browser/browser';
import { IThemeService } from 'vs/platform/theme/common/themeService';
const MIN_SIDEBAR_PART_WIDTH = 170;
const MIN_EDITOR_PART_HEIGHT = 70;
const MIN_EDITOR_PART_WIDTH = 220;
const MIN_PANEL_PART_HEIGHT = 77;
const DEFAULT_PANEL_HEIGHT_COEFFICIENT = 0.4;
const HIDE_SIDEBAR_WIDTH_THRESHOLD = 50;
const HIDE_PANEL_HEIGHT_THRESHOLD = 50;
const TITLE_BAR_HEIGHT = 22;
const STATUS_BAR_HEIGHT = 22;
const ACTIVITY_BAR_WIDTH = 50;
interface PartLayoutInfo {
titlebar: { height: number; };
activitybar: { width: number; };
sidebar: { minWidth: number; };
panel: { minHeight: number; };
editor: { minWidth: number; minHeight: number; };
statusbar: { height: number; };
}
/**
* The workbench layout is responsible to lay out all parts that make the Workbench.
*/
export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider {
private static sashXWidthSettingsKey = 'workbench.sidebar.width';
private static sashYHeightSettingsKey = 'workbench.panel.height';
private parent: Builder;
private workbenchContainer: Builder;
private titlebar: Part;
private activitybar: Part;
private editor: Part;
private sidebar: Part;
private panel: Part;
private statusbar: Part;
private quickopen: QuickOpenController;
private toUnbind: IDisposable[];
private partLayoutInfo: PartLayoutInfo;
private workbenchSize: Dimension;
private sashX: Sash;
private sashY: Sash;
private startSidebarWidth: number;
private sidebarWidth: number;
private sidebarHeight: number;
private titlebarHeight: number;
private activitybarWidth: number;
private statusbarHeight: number;
private startPanelHeight: number;
private panelHeight: number;
private panelHeightBeforeMaximized: number;
private panelMaximized: boolean;
private panelWidth: number;
private layoutEditorGroupsVertically: boolean;
// Take parts as an object bag since instatation service does not have typings for constructors with 9+ arguments
constructor(
parent: Builder,
workbenchContainer: Builder,
parts: {
titlebar: Part,
activitybar: Part,
editor: Part,
sidebar: Part,
panel: Part,
statusbar: Part
},
quickopen: QuickOpenController,
@IStorageService private storageService: IStorageService,
@IContextViewService private contextViewService: IContextViewService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService,
@IPartService private partService: IPartService,
@IViewletService private viewletService: IViewletService,
@IThemeService themeService: IThemeService
) {
this.parent = parent;
this.workbenchContainer = workbenchContainer;
this.titlebar = parts.titlebar;
this.activitybar = parts.activitybar;
this.editor = parts.editor;
this.sidebar = parts.sidebar;
this.panel = parts.panel;
this.statusbar = parts.statusbar;
this.quickopen = quickopen;
this.toUnbind = [];
this.partLayoutInfo = this.getPartLayoutInfo();
this.panelHeightBeforeMaximized = 0;
this.panelMaximized = false;
this.sashX = new Sash(this.workbenchContainer.getHTMLElement(), this, {
baseSize: 5
});
this.sashY = new Sash(this.workbenchContainer.getHTMLElement(), this, {
baseSize: 4,
orientation: Orientation.HORIZONTAL
});
this.sidebarWidth = this.storageService.getInteger(WorkbenchLayout.sashXWidthSettingsKey, StorageScope.GLOBAL, -1);
this.panelHeight = this.storageService.getInteger(WorkbenchLayout.sashYHeightSettingsKey, StorageScope.GLOBAL, 0);
this.layoutEditorGroupsVertically = (this.editorGroupService.getGroupOrientation() !== 'horizontal');
this.toUnbind.push(themeService.onThemeChange(_ => this.layout()));
this.toUnbind.push(editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
this.toUnbind.push(editorGroupService.onGroupOrientationChanged(e => this.onGroupOrientationChanged()));
this.registerSashListeners();
}
private getPartLayoutInfo(): PartLayoutInfo {
return {
titlebar: {
height: TITLE_BAR_HEIGHT
},
activitybar: {
width: ACTIVITY_BAR_WIDTH
},
sidebar: {
minWidth: MIN_SIDEBAR_PART_WIDTH
},
panel: {
minHeight: MIN_PANEL_PART_HEIGHT
},
editor: {
minWidth: MIN_EDITOR_PART_WIDTH,
minHeight: MIN_EDITOR_PART_HEIGHT
},
statusbar: {
height: STATUS_BAR_HEIGHT
}
};
}
private registerSashListeners(): void {
let startX: number = 0;
let startY: number = 0;
this.sashX.addListener('start', (e: ISashEvent) => {
this.startSidebarWidth = this.sidebarWidth;
startX = e.startX;
});
this.sashY.addListener('start', (e: ISashEvent) => {
this.startPanelHeight = this.panelHeight;
startY = e.startY;
});
this.sashX.addListener('change', (e: ISashEvent) => {
let doLayout = false;
let sidebarPosition = this.partService.getSideBarPosition();
let isSidebarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
let newSashWidth = (sidebarPosition === Position.LEFT) ? this.startSidebarWidth + e.currentX - startX : this.startSidebarWidth - e.currentX + startX;
let promise = TPromise.as<void>(null);
// Sidebar visible
if (isSidebarVisible) {
// Automatically hide side bar when a certain threshold is met
if (newSashWidth + HIDE_SIDEBAR_WIDTH_THRESHOLD < this.partLayoutInfo.sidebar.minWidth) {
let dragCompensation = MIN_SIDEBAR_PART_WIDTH - HIDE_SIDEBAR_WIDTH_THRESHOLD;
promise = this.partService.setSideBarHidden(true);
startX = (sidebarPosition === Position.LEFT) ? Math.max(this.activitybarWidth, e.currentX - dragCompensation) : Math.min(e.currentX + dragCompensation, this.workbenchSize.width - this.activitybarWidth);
this.sidebarWidth = this.startSidebarWidth; // when restoring sidebar, restore to the sidebar width we started from
}
// Otherwise size the sidebar accordingly
else {
this.sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, newSashWidth); // Sidebar can not become smaller than MIN_PART_WIDTH
doLayout = newSashWidth >= this.partLayoutInfo.sidebar.minWidth;
}
}
// Sidebar hidden
else {
if ((sidebarPosition === Position.LEFT && e.currentX - startX >= this.partLayoutInfo.sidebar.minWidth) ||
(sidebarPosition === Position.RIGHT && startX - e.currentX >= this.partLayoutInfo.sidebar.minWidth)) {
this.startSidebarWidth = this.partLayoutInfo.sidebar.minWidth - (sidebarPosition === Position.LEFT ? e.currentX - startX : startX - e.currentX);
this.sidebarWidth = this.partLayoutInfo.sidebar.minWidth;
promise = this.partService.setSideBarHidden(false);
}
}
if (doLayout) {
promise.done(() => this.layout(), errors.onUnexpectedError);
}
});
this.sashY.addListener('change', (e: ISashEvent) => {
let doLayout = false;
let isPanelVisible = this.partService.isVisible(Parts.PANEL_PART);
let newSashHeight = this.startPanelHeight - (e.currentY - startY);
let promise = TPromise.as<void>(null);
// Panel visible
if (isPanelVisible) {
// Automatically hide panel when a certain threshold is met
if (newSashHeight + HIDE_PANEL_HEIGHT_THRESHOLD < this.partLayoutInfo.panel.minHeight) {
let dragCompensation = MIN_PANEL_PART_HEIGHT - HIDE_PANEL_HEIGHT_THRESHOLD;
promise = this.partService.setPanelHidden(true);
startY = Math.min(this.sidebarHeight - this.statusbarHeight - this.titlebarHeight, e.currentY + dragCompensation);
this.panelHeight = this.startPanelHeight; // when restoring panel, restore to the panel height we started from
}
// Otherwise size the panel accordingly
else {
this.panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, newSashHeight); // Panel can not become smaller than MIN_PART_HEIGHT
doLayout = newSashHeight >= this.partLayoutInfo.panel.minHeight;
}
}
// Panel hidden
else {
if (startY - e.currentY >= this.partLayoutInfo.panel.minHeight) {
this.startPanelHeight = 0;
this.panelHeight = this.partLayoutInfo.panel.minHeight;
promise = this.partService.setPanelHidden(false);
}
}
if (doLayout) {
promise.done(() => this.layout(), errors.onUnexpectedError);
}
});
this.sashX.addListener('end', () => {
this.storageService.store(WorkbenchLayout.sashXWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
});
this.sashY.addListener('end', () => {
this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
});
this.sashY.addListener('reset', () => {
this.panelHeight = this.sidebarHeight * DEFAULT_PANEL_HEIGHT_COEFFICIENT;
this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
this.partService.setPanelHidden(false).done(() => this.layout(), errors.onUnexpectedError);
});
this.sashX.addListener('reset', () => {
let activeViewlet = this.viewletService.getActiveViewlet();
let optimalWidth = activeViewlet && activeViewlet.getOptimalWidth();
this.sidebarWidth = Math.max(MIN_SIDEBAR_PART_WIDTH, optimalWidth || 0);
this.storageService.store(WorkbenchLayout.sashXWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
this.partService.setSideBarHidden(false).done(() => this.layout(), errors.onUnexpectedError);
});
}
private onEditorsChanged(): void {
// Make sure that we layout properly in case we detect that the sidebar or panel is large enought to cause
// multiple opened editors to go below minimal size. The fix is to trigger a layout for any editor
// input change that falls into this category.
if (this.workbenchSize && (this.sidebarWidth || this.panelHeight)) {
let visibleEditors = this.editorService.getVisibleEditors().length;
if (visibleEditors > 1) {
const sidebarOverflow = this.layoutEditorGroupsVertically && (this.workbenchSize.width - this.sidebarWidth < visibleEditors * MIN_EDITOR_PART_WIDTH);
const panelOverflow = !this.layoutEditorGroupsVertically && (this.workbenchSize.height - this.panelHeight < visibleEditors * MIN_EDITOR_PART_HEIGHT);
if (sidebarOverflow || panelOverflow) {
this.layout();
}
}
}
}
private onGroupOrientationChanged(): void {
const newLayoutEditorGroupsVertically = (this.editorGroupService.getGroupOrientation() !== 'horizontal');
const doLayout = this.layoutEditorGroupsVertically !== newLayoutEditorGroupsVertically;
this.layoutEditorGroupsVertically = newLayoutEditorGroupsVertically;
if (doLayout) {
this.layout();
}
}
public layout(options?: ILayoutOptions): void {
this.workbenchSize = this.parent.getClientArea();
const isActivityBarHidden = !this.partService.isVisible(Parts.ACTIVITYBAR_PART);
const isTitlebarHidden = !this.partService.isVisible(Parts.TITLEBAR_PART);
const isPanelHidden = !this.partService.isVisible(Parts.PANEL_PART);
const isStatusbarHidden = !this.partService.isVisible(Parts.STATUSBAR_PART);
const isSidebarHidden = !this.partService.isVisible(Parts.SIDEBAR_PART);
const sidebarPosition = this.partService.getSideBarPosition();
// Sidebar
let sidebarWidth: number;
if (isSidebarHidden) {
sidebarWidth = 0;
} else if (this.sidebarWidth !== -1) {
sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, this.sidebarWidth);
} else {
sidebarWidth = this.workbenchSize.width / 5;
this.sidebarWidth = sidebarWidth;
}
this.statusbarHeight = isStatusbarHidden ? 0 : this.partLayoutInfo.statusbar.height;
this.titlebarHeight = isTitlebarHidden ? 0 : this.partLayoutInfo.titlebar.height / getZoomFactor(); // adjust for zoom prevention
const previousMaxPanelHeight = this.sidebarHeight - MIN_EDITOR_PART_HEIGHT;
this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.titlebarHeight;
let sidebarSize = new Dimension(sidebarWidth, this.sidebarHeight);
// Activity Bar
this.activitybarWidth = isActivityBarHidden ? 0 : this.partLayoutInfo.activitybar.width;
let activityBarSize = new Dimension(this.activitybarWidth, sidebarSize.height);
// Panel part
let panelHeight: number;
const editorCountForHeight = this.editorGroupService.getGroupOrientation() === 'horizontal' ? this.editorGroupService.getStacksModel().groups.length : 1;
const maxPanelHeight = sidebarSize.height - editorCountForHeight * MIN_EDITOR_PART_HEIGHT;
if (isPanelHidden) {
panelHeight = 0;
} else if (this.panelHeight === previousMaxPanelHeight) {
panelHeight = maxPanelHeight;
} else if (this.panelHeight > 0) {
panelHeight = Math.min(maxPanelHeight, Math.max(this.partLayoutInfo.panel.minHeight, this.panelHeight));
} else {
panelHeight = sidebarSize.height * DEFAULT_PANEL_HEIGHT_COEFFICIENT;
}
if (options && options.toggleMaximizedPanel) {
panelHeight = this.panelMaximized ? Math.max(this.partLayoutInfo.panel.minHeight, Math.min(this.panelHeightBeforeMaximized, maxPanelHeight)) : maxPanelHeight;
}
this.panelMaximized = panelHeight === maxPanelHeight;
if (panelHeight / maxPanelHeight < 0.7) {
// Remember the previous height only if the panel size is not too large.
// To get a nice minimize effect even if a user dragged the panel sash to maximum.
this.panelHeightBeforeMaximized = panelHeight;
}
const panelDimension = new Dimension(this.workbenchSize.width - sidebarSize.width - activityBarSize.width, panelHeight);
this.panelWidth = panelDimension.width;
// Editor
let editorSize = {
width: 0,
height: 0,
remainderLeft: 0,
remainderRight: 0
};
editorSize.width = panelDimension.width;
editorSize.height = sidebarSize.height - panelDimension.height;
// Sidebar hidden
if (isSidebarHidden) {
editorSize.width = this.workbenchSize.width - activityBarSize.width;
if (sidebarPosition === Position.LEFT) {
editorSize.remainderLeft = Math.round((this.workbenchSize.width - editorSize.width + activityBarSize.width) / 2);
editorSize.remainderRight = this.workbenchSize.width - editorSize.width - editorSize.remainderLeft;
} else {
editorSize.remainderRight = Math.round((this.workbenchSize.width - editorSize.width + activityBarSize.width) / 2);
editorSize.remainderLeft = this.workbenchSize.width - editorSize.width - editorSize.remainderRight;
}
}
// Assert Sidebar and Editor Size to not overflow
let editorMinWidth = this.partLayoutInfo.editor.minWidth;
let editorMinHeight = this.partLayoutInfo.editor.minHeight;
let visibleEditorCount = this.editorService.getVisibleEditors().length;
if (visibleEditorCount > 1) {
if (this.layoutEditorGroupsVertically) {
editorMinWidth *= visibleEditorCount; // when editors layout vertically, multiply the min editor width by number of visible editors
} else {
editorMinHeight *= visibleEditorCount; // when editors layout horizontally, multiply the min editor height by number of visible editors
}
}
if (editorSize.width < editorMinWidth) {
let diff = editorMinWidth - editorSize.width;
editorSize.width = editorMinWidth;
panelDimension.width = editorMinWidth;
sidebarSize.width -= diff;
sidebarSize.width = Math.max(MIN_SIDEBAR_PART_WIDTH, sidebarSize.width);
}
if (editorSize.height < editorMinHeight) {
let diff = editorMinHeight - editorSize.height;
editorSize.height = editorMinHeight;
panelDimension.height -= diff;
panelDimension.height = Math.max(MIN_PANEL_PART_HEIGHT, panelDimension.height);
}
if (!isSidebarHidden) {
this.sidebarWidth = sidebarSize.width;
this.storageService.store(WorkbenchLayout.sashXWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
}
if (!isPanelHidden) {
this.panelHeight = panelDimension.height;
this.storageService.store(WorkbenchLayout.sashYHeightSettingsKey, this.panelHeight, StorageScope.GLOBAL);
}
// Workbench
this.workbenchContainer
.position(0, 0, 0, 0, 'relative')
.size(this.workbenchSize.width, this.workbenchSize.height);
// Bug on Chrome: Sometimes Chrome wants to scroll the workbench container on layout changes. The fix is to reset scrolling in this case.
const workbenchContainer = this.workbenchContainer.getHTMLElement();
if (workbenchContainer.scrollTop > 0) {
workbenchContainer.scrollTop = 0;
}
if (workbenchContainer.scrollLeft > 0) {
workbenchContainer.scrollLeft = 0;
}
// Title Part
if (isTitlebarHidden) {
this.titlebar.getContainer().hide();
} else {
this.titlebar.getContainer().show();
}
// Editor Part and Panel part
this.editor.getContainer().size(editorSize.width, editorSize.height);
this.panel.getContainer().size(panelDimension.width, panelDimension.height);
const editorBottom = this.statusbarHeight + panelDimension.height;
if (isSidebarHidden) {
this.editor.getContainer().position(this.titlebarHeight, editorSize.remainderRight, editorBottom, editorSize.remainderLeft);
this.panel.getContainer().position(editorSize.height + this.titlebarHeight, editorSize.remainderRight, this.statusbarHeight, editorSize.remainderLeft);
} else if (sidebarPosition === Position.LEFT) {
this.editor.getContainer().position(this.titlebarHeight, 0, editorBottom, sidebarSize.width + activityBarSize.width);
this.panel.getContainer().position(editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
} else {
this.editor.getContainer().position(this.titlebarHeight, sidebarSize.width, editorBottom, 0);
this.panel.getContainer().position(editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0);
}
// Activity Bar Part
this.activitybar.getContainer().size(null, activityBarSize.height);
if (sidebarPosition === Position.LEFT) {
this.activitybar.getContainer().getHTMLElement().style.right = '';
this.activitybar.getContainer().position(this.titlebarHeight, null, 0, 0);
} else {
this.activitybar.getContainer().getHTMLElement().style.left = '';
this.activitybar.getContainer().position(this.titlebarHeight, 0, 0, null);
}
if (isActivityBarHidden) {
this.activitybar.getContainer().hide();
} else {
this.activitybar.getContainer().show();
}
// Sidebar Part
this.sidebar.getContainer().size(sidebarSize.width, sidebarSize.height);
if (sidebarPosition === Position.LEFT) {
this.sidebar.getContainer().position(this.titlebarHeight, editorSize.width, 0, activityBarSize.width);
} else {
this.sidebar.getContainer().position(this.titlebarHeight, null, 0, editorSize.width);
}
// Statusbar Part
this.statusbar.getContainer().position(this.workbenchSize.height - this.statusbarHeight);
if (isStatusbarHidden) {
this.statusbar.getContainer().hide();
} else {
this.statusbar.getContainer().show();
}
// Quick open
this.quickopen.layout(this.workbenchSize);
// Sashes
this.sashX.layout();
this.sashY.layout();
// Propagate to Part Layouts
this.titlebar.layout(new Dimension(this.workbenchSize.width, this.titlebarHeight));
this.editor.layout(new Dimension(editorSize.width, editorSize.height));
this.sidebar.layout(sidebarSize);
this.panel.layout(panelDimension);
this.activitybar.layout(activityBarSize);
// Propagate to Context View
this.contextViewService.layout();
}
public getVerticalSashTop(sash: Sash): number {
return this.titlebarHeight;
}
public getVerticalSashLeft(sash: Sash): number {
let isSidebarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
let sidebarPosition = this.partService.getSideBarPosition();
if (sidebarPosition === Position.LEFT) {
return isSidebarVisible ? this.sidebarWidth + this.activitybarWidth : this.activitybarWidth;
}
return isSidebarVisible ? this.workbenchSize.width - this.sidebarWidth - this.activitybarWidth : this.workbenchSize.width - this.activitybarWidth;
}
public getVerticalSashHeight(sash: Sash): number {
return this.sidebarHeight;
}
public getHorizontalSashTop(sash: Sash): number {
// Horizontal sash should be a bit lower than the editor area, thus add 2px #5524
return 2 + (this.partService.isVisible(Parts.PANEL_PART) ? this.sidebarHeight - this.panelHeight + this.titlebarHeight : this.sidebarHeight + this.titlebarHeight);
}
public getHorizontalSashLeft(sash: Sash): number {
return this.partService.getSideBarPosition() === Position.LEFT ? this.getVerticalSashLeft(sash) : 0;
}
public getHorizontalSashWidth(sash: Sash): number {
return this.panelWidth;
}
public isPanelMaximized(): boolean {
return this.panelMaximized;
}
// change part size along the main axis
public resizePart(part: Parts, sizeChange: number): void {
const visibleEditors = this.editorService.getVisibleEditors().length;
const sizeChangePxWidth = this.workbenchSize.width * (sizeChange / 100);
const sizeChangePxHeight = this.workbenchSize.height * (sizeChange / 100);
let doLayout = false;
let newSashSize: number = 0;
switch (part) {
case Parts.SIDEBAR_PART:
newSashSize = this.sidebarWidth + sizeChangePxWidth;
this.sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, newSashSize); // Sidebar can not become smaller than MIN_PART_WIDTH
if (this.layoutEditorGroupsVertically && (this.workbenchSize.width - this.sidebarWidth < visibleEditors * MIN_EDITOR_PART_WIDTH)) {
this.sidebarWidth = (this.workbenchSize.width - visibleEditors * MIN_EDITOR_PART_WIDTH);
}
doLayout = true;
break;
case Parts.PANEL_PART:
newSashSize = this.panelHeight + sizeChangePxHeight;
this.panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, newSashSize);
doLayout = true;
break;
case Parts.EDITOR_PART:
// If we have one editor we can cheat and resize sidebar with the negative delta
const visibleEditorCount = this.editorService.getVisibleEditors().length;
if (visibleEditorCount === 1) {
this.sidebarWidth = this.sidebarWidth - sizeChangePxWidth;
doLayout = true;
} else {
const stacks = this.editorGroupService.getStacksModel();
const activeGroup = stacks.positionOfGroup(stacks.activeGroup);
this.editorGroupService.resizeGroup(activeGroup, sizeChangePxWidth);
doLayout = false;
}
}
if (doLayout) {
this.layout();
}
}
public dispose(): void {
if (this.toUnbind) {
dispose(this.toUnbind);
this.toUnbind = null;
}
}
}

View File

@@ -0,0 +1,81 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench > .part > .title {
display: none; /* Parts have to opt in to show title area */
}
/* title styles are defined for two classes because the editor puts the title into the content */
.monaco-workbench > .part > .title,
.monaco-workbench > .part.editor > .content > .one-editor-silo > .container > .title {
height: 35px;
display: flex;
box-sizing: border-box;
overflow: hidden;
}
.monaco-workbench > .part > .title {
padding-left: 8px;
padding-right: 8px;
}
.monaco-workbench > .part > .title > .title-label {
line-height: 35px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.monaco-workbench > .part > .title > .title-label {
padding-left: 12px;
}
.monaco-workbench > .part > .title > .title-label span {
font-size: 11px;
cursor: default;
}
.monaco-workbench > .part > .title > .title-label a {
text-decoration: none;
font-size: 13px;
cursor: default;
}
.monaco-workbench > .part > .title > .title-actions {
height: 35px;
flex: 1;
padding-left: 5px;
}
.monaco-workbench > .part > .title > .title-actions .action-label {
display: block;
height: 35px;
line-height: 35px;
min-width: 28px;
background-size: 16px;
background-position: center center;
background-repeat: no-repeat;
}
.monaco-workbench > .part > .title > .title-actions .action-label .label {
display: none;
}
.monaco-workbench > .part > .content {
font-size: 13px;
}
.monaco-workbench > .part > .content .progress-container {
position: absolute;
left: 0;
top: 33px; /* at the bottom of the 35px height title container */
z-index: 5; /* on top of things */
height: 2px;
}
.monaco-workbench > .part > .content .progress-container .progress-bit {
height: 2px;
}

View File

@@ -0,0 +1,116 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as DOM from 'vs/base/browser/dom';
import { Registry } from 'vs/platform/registry/common/platform';
import { TPromise } from 'vs/base/common/winjs.base';
import { IPanel } from 'vs/workbench/common/panel';
import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite';
import { Action } from 'vs/base/common/actions';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IPartService } from 'vs/workbench/services/part/common/partService';
export abstract class Panel extends Composite implements IPanel { }
/**
* A panel descriptor is a leightweight descriptor of a panel in the workbench.
*/
export class PanelDescriptor extends CompositeDescriptor<Panel> {
constructor(moduleId: string, ctorName: string, id: string, name: string, cssClass?: string, order?: number, private _commandId?: string) {
super(moduleId, ctorName, id, name, cssClass, order);
}
public get commandId(): string {
return this._commandId;
}
}
export class PanelRegistry extends CompositeRegistry<Panel> {
private defaultPanelId: string;
/**
* Registers a panel to the platform.
*/
public registerPanel(descriptor: PanelDescriptor): void {
super.registerComposite(descriptor);
}
/**
* Returns the panel descriptor for the given id or null if none.
*/
public getPanel(id: string): PanelDescriptor {
return this.getComposite(id) as PanelDescriptor;
}
/**
* Returns an array of registered panels known to the platform.
*/
public getPanels(): PanelDescriptor[] {
return this.getComposites() as PanelDescriptor[];
}
/**
* Sets the id of the panel that should open on startup by default.
*/
public setDefaultPanelId(id: string): void {
this.defaultPanelId = id;
}
/**
* Gets the id of the panel that should open on startup by default.
*/
public getDefaultPanelId(): string {
return this.defaultPanelId;
}
}
/**
* A reusable action to toggle a panel with a specific id.
*/
export abstract class TogglePanelAction extends Action {
private panelId: string;
constructor(
id: string,
label: string,
panelId: string,
protected panelService: IPanelService,
private partService: IPartService,
cssClass?: string
) {
super(id, label, cssClass);
this.panelId = panelId;
}
public run(): TPromise<any> {
if (this.isPanelShowing()) {
return this.partService.setPanelHidden(true);
}
return this.panelService.openPanel(this.panelId, true);
}
private isPanelShowing(): boolean {
const panel = this.panelService.getActivePanel();
return panel && panel.getId() === this.panelId;
}
protected isPanelFocused(): boolean {
const activePanel = this.panelService.getActivePanel();
const activeElement = document.activeElement;
return activePanel && activeElement && DOM.isAncestor(activeElement, (<Panel>activePanel).getContainer().getHTMLElement());
}
}
export const Extensions = {
Panels: 'workbench.contributions.panels'
};
Registry.add(Extensions.Panels, new PanelRegistry());

View File

@@ -0,0 +1,148 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/part';
import { Dimension, Builder } from 'vs/base/browser/builder';
import { Component } from 'vs/workbench/common/component';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
export interface IPartOptions {
hasTitle?: boolean;
borderWidth?: () => number;
}
/**
* Parts are layed out in the workbench and have their own layout that arranges an optional title
* and mandatory content area to show content.
*/
export abstract class Part extends Component {
private parent: Builder;
private titleArea: Builder;
private contentArea: Builder;
private partLayout: PartLayout;
constructor(
id: string,
private options: IPartOptions,
themeService: IThemeService
) {
super(id, themeService);
}
protected onThemeChange(theme: ITheme): void {
// only call if our create() method has been called
if (this.parent) {
super.onThemeChange(theme);
}
}
/**
* Note: Clients should not call this method, the workbench calls this
* method. Calling it otherwise may result in unexpected behavior.
*
* Called to create title and content area of the part.
*/
public create(parent: Builder): void {
this.parent = parent;
this.titleArea = this.createTitleArea(parent);
this.contentArea = this.createContentArea(parent);
this.partLayout = new PartLayout(this.parent, this.options, this.titleArea, this.contentArea);
this.updateStyles();
}
/**
* Returns the overall part container.
*/
public getContainer(): Builder {
return this.parent;
}
/**
* Subclasses override to provide a title area implementation.
*/
protected createTitleArea(parent: Builder): Builder {
return null;
}
/**
* Returns the title area container.
*/
protected getTitleArea(): Builder {
return this.titleArea;
}
/**
* Subclasses override to provide a content area implementation.
*/
protected createContentArea(parent: Builder): Builder {
return null;
}
/**
* Returns the content area container.
*/
protected getContentArea(): Builder {
return this.contentArea;
}
/**
* Layout title and content area in the given dimension.
*/
public layout(dimension: Dimension): Dimension[] {
return this.partLayout.layout(dimension);
}
/**
* Returns the part layout implementation.
*/
public getLayout(): PartLayout {
return this.partLayout;
}
}
const TITLE_HEIGHT = 35;
export class PartLayout {
constructor(private container: Builder, private options: IPartOptions, private titleArea: Builder, private contentArea: Builder) {
}
public layout(dimension: Dimension): Dimension[] {
const { width, height } = dimension;
// Return the applied sizes to title and content
const sizes: Dimension[] = [];
// Title Size: Width (Fill), Height (Variable)
let titleSize: Dimension;
if (this.options && this.options.hasTitle) {
titleSize = new Dimension(width, Math.min(height, TITLE_HEIGHT));
} else {
titleSize = new Dimension(0, 0);
}
// Content Size: Width (Fill), Height (Variable)
const contentSize = new Dimension(width, height - titleSize.height);
if (this.options && typeof this.options.borderWidth === 'function') {
contentSize.width -= this.options.borderWidth(); // adjust for border size
}
sizes.push(titleSize);
sizes.push(contentSize);
// Content
if (this.contentArea) {
this.contentArea.size(contentSize.width, contentSize.height);
}
return sizes;
}
}

View File

@@ -0,0 +1,757 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/activityaction';
import nls = require('vs/nls');
import DOM = require('vs/base/browser/dom');
import { TPromise } from 'vs/base/common/winjs.base';
import { Builder, $ } from 'vs/base/browser/builder';
import { DelayedDragHandler } from 'vs/base/browser/dnd';
import { Action } from 'vs/base/common/actions';
import { BaseActionItem, Separator, IBaseActionItemOptions } from 'vs/base/browser/ui/actionbar/actionbar';
import { IActivityBarService, ProgressBadge, TextBadge, NumberBadge, IconBadge, IBadge } from 'vs/workbench/services/activity/common/activityBarService';
import Event, { Emitter } from 'vs/base/common/event';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { IActivity, IGlobalActivity } from 'vs/workbench/browser/activity';
import { dispose } from 'vs/base/common/lifecycle';
import { IViewletService, } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import { IThemeService, ITheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_FOREGROUND } from 'vs/workbench/common/theme';
import { contrastBorder, activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
export interface IViewletActivity {
badge: IBadge;
clazz: string;
}
export class ActivityAction extends Action {
private badge: IBadge;
private _onDidChangeBadge = new Emitter<this>();
constructor(private _activity: IActivity) {
super(_activity.id, _activity.name, _activity.cssClass);
this.badge = null;
}
public get activity(): IActivity {
return this._activity;
}
public get onDidChangeBadge(): Event<this> {
return this._onDidChangeBadge.event;
}
public activate(): void {
if (!this.checked) {
this._setChecked(true);
}
}
public deactivate(): void {
if (this.checked) {
this._setChecked(false);
}
}
public getBadge(): IBadge {
return this.badge;
}
public setBadge(badge: IBadge): void {
this.badge = badge;
this._onDidChangeBadge.fire(this);
}
}
export class ViewletActivityAction extends ActivityAction {
private static preventDoubleClickDelay = 300;
private lastRun: number = 0;
constructor(
private viewlet: ViewletDescriptor,
@IViewletService private viewletService: IViewletService,
@IPartService private partService: IPartService
) {
super(viewlet);
}
public run(event): TPromise<any> {
if (event instanceof MouseEvent && event.button === 2) {
return TPromise.as(false); // do not run on right click
}
// prevent accident trigger on a doubleclick (to help nervous people)
const now = Date.now();
if (now > this.lastRun /* https://github.com/Microsoft/vscode/issues/25830 */ && now - this.lastRun < ViewletActivityAction.preventDoubleClickDelay) {
return TPromise.as(true);
}
this.lastRun = now;
const sideBarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
const activeViewlet = this.viewletService.getActiveViewlet();
// Hide sidebar if selected viewlet already visible
if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.viewlet.id) {
return this.partService.setSideBarHidden(true);
}
return this.viewletService.openViewlet(this.viewlet.id, true)
.then(() => this.activate());
}
}
export class ActivityActionItem extends BaseActionItem {
protected $container: Builder;
protected $label: Builder;
protected $badge: Builder;
private $badgeContent: Builder;
private mouseUpTimeout: number;
constructor(
action: ActivityAction,
options: IBaseActionItemOptions,
@IThemeService protected themeService: IThemeService
) {
super(null, action, options);
this.themeService.onThemeChange(this.onThemeChange, this, this._callOnDispose);
action.onDidChangeBadge(this.handleBadgeChangeEvenet, this, this._callOnDispose);
}
protected get activity(): IActivity {
return (this._action as ActivityAction).activity;
}
protected updateStyles(): void {
const theme = this.themeService.getTheme();
// Label
if (this.$label) {
const background = theme.getColor(ACTIVITY_BAR_FOREGROUND);
this.$label.style('background-color', background ? background.toString() : null);
}
// Badge
if (this.$badgeContent) {
const badgeForeground = theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND);
const badgeBackground = theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND);
const contrastBorderColor = theme.getColor(contrastBorder);
this.$badgeContent.style('color', badgeForeground ? badgeForeground.toString() : null);
this.$badgeContent.style('background-color', badgeBackground ? badgeBackground.toString() : null);
this.$badgeContent.style('border-style', contrastBorderColor ? 'solid' : null);
this.$badgeContent.style('border-width', contrastBorderColor ? '1px' : null);
this.$badgeContent.style('border-color', contrastBorderColor ? contrastBorderColor.toString() : null);
}
}
public render(container: HTMLElement): void {
super.render(container);
// Make the container tab-able for keyboard navigation
this.$container = $(container).attr({
tabIndex: '0',
role: 'button',
title: this.activity.name
});
// Try hard to prevent keyboard only focus feedback when using mouse
this.$container.on(DOM.EventType.MOUSE_DOWN, () => {
this.$container.addClass('clicked');
});
this.$container.on(DOM.EventType.MOUSE_UP, () => {
if (this.mouseUpTimeout) {
clearTimeout(this.mouseUpTimeout);
}
this.mouseUpTimeout = setTimeout(() => {
this.$container.removeClass('clicked');
}, 800); // delayed to prevent focus feedback from showing on mouse up
});
// Label
this.$label = $('a.action-label').appendTo(this.builder);
if (this.activity.cssClass) {
this.$label.addClass(this.activity.cssClass);
}
this.$badge = this.builder.clone().div({ 'class': 'badge' }, (badge: Builder) => {
this.$badgeContent = badge.div({ 'class': 'badge-content' });
});
this.$badge.hide();
this.updateStyles();
}
private onThemeChange(theme: ITheme): void {
this.updateStyles();
}
public setBadge(badge: IBadge): void {
this.updateBadge(badge);
}
protected updateBadge(badge: IBadge): void {
this.$badgeContent.empty();
this.$badge.hide();
if (badge) {
// Number
if (badge instanceof NumberBadge) {
if (badge.number) {
this.$badgeContent.text(badge.number > 99 ? '99+' : badge.number.toString());
this.$badge.show();
}
}
// Text
else if (badge instanceof TextBadge) {
this.$badgeContent.text(badge.text);
this.$badge.show();
}
// Text
else if (badge instanceof IconBadge) {
this.$badge.show();
}
// Progress
else if (badge instanceof ProgressBadge) {
this.$badge.show();
}
const description = badge.getDescription();
this.$label.attr('aria-label', `${this.activity.name} - ${description}`);
this.$label.title(description);
}
}
private handleBadgeChangeEvenet(): void {
const action = this.getAction();
if (action instanceof ActivityAction) {
this.updateBadge(action.getBadge());
}
}
public dispose(): void {
super.dispose();
if (this.mouseUpTimeout) {
clearTimeout(this.mouseUpTimeout);
}
this.$badge.destroy();
}
}
export class ViewletActionItem extends ActivityActionItem {
private static manageExtensionAction: ManageExtensionAction;
private static toggleViewletPinnedAction: ToggleViewletPinnedAction;
private static draggedViewlet: ViewletDescriptor;
private _keybinding: string;
private cssClass: string;
constructor(
private action: ViewletActivityAction,
@IContextMenuService private contextMenuService: IContextMenuService,
@IActivityBarService private activityBarService: IActivityBarService,
@IKeybindingService private keybindingService: IKeybindingService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService
) {
super(action, { draggable: true }, themeService);
this.cssClass = action.class;
this._keybinding = this.getKeybindingLabel(this.viewlet.id);
if (!ViewletActionItem.manageExtensionAction) {
ViewletActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction);
}
if (!ViewletActionItem.toggleViewletPinnedAction) {
ViewletActionItem.toggleViewletPinnedAction = instantiationService.createInstance(ToggleViewletPinnedAction, void 0);
}
}
private get viewlet(): ViewletDescriptor {
return this.action.activity as ViewletDescriptor;
}
private getKeybindingLabel(id: string): string {
const kb = this.keybindingService.lookupKeybinding(id);
if (kb) {
return kb.getLabel();
}
return null;
}
public render(container: HTMLElement): void {
super.render(container);
this.$container.on('contextmenu', e => {
DOM.EventHelper.stop(e, true);
this.showContextMenu(container);
});
// Allow to drag
this.$container.on(DOM.EventType.DRAG_START, (e: DragEvent) => {
e.dataTransfer.effectAllowed = 'move';
this.setDraggedViewlet(this.viewlet);
// Trigger the action even on drag start to prevent clicks from failing that started a drag
if (!this.getAction().checked) {
this.getAction().run();
}
});
// Drag enter
let counter = 0; // see https://github.com/Microsoft/vscode/issues/14470
this.$container.on(DOM.EventType.DRAG_ENTER, (e: DragEvent) => {
const draggedViewlet = ViewletActionItem.getDraggedViewlet();
if (draggedViewlet && draggedViewlet.id !== this.viewlet.id) {
counter++;
this.updateFromDragging(container, true);
}
});
// Drag leave
this.$container.on(DOM.EventType.DRAG_LEAVE, (e: DragEvent) => {
const draggedViewlet = ViewletActionItem.getDraggedViewlet();
if (draggedViewlet) {
counter--;
if (counter === 0) {
this.updateFromDragging(container, false);
}
}
});
// Drag end
this.$container.on(DOM.EventType.DRAG_END, (e: DragEvent) => {
const draggedViewlet = ViewletActionItem.getDraggedViewlet();
if (draggedViewlet) {
counter = 0;
this.updateFromDragging(container, false);
ViewletActionItem.clearDraggedViewlet();
}
});
// Drop
this.$container.on(DOM.EventType.DROP, (e: DragEvent) => {
DOM.EventHelper.stop(e, true);
const draggedViewlet = ViewletActionItem.getDraggedViewlet();
if (draggedViewlet && draggedViewlet.id !== this.viewlet.id) {
this.updateFromDragging(container, false);
ViewletActionItem.clearDraggedViewlet();
this.activityBarService.move(draggedViewlet.id, this.viewlet.id);
}
});
// Keybinding
this.keybinding = this._keybinding; // force update
// Activate on drag over to reveal targets
[this.$badge, this.$label].forEach(b => new DelayedDragHandler(b.getHTMLElement(), () => {
if (!ViewletActionItem.getDraggedViewlet() && !this.getAction().checked) {
this.getAction().run();
}
}));
this.updateStyles();
}
private updateFromDragging(element: HTMLElement, isDragging: boolean): void {
const theme = this.themeService.getTheme();
const dragBackground = theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND);
element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : null;
}
public static getDraggedViewlet(): ViewletDescriptor {
return ViewletActionItem.draggedViewlet;
}
private setDraggedViewlet(viewlet: ViewletDescriptor): void {
ViewletActionItem.draggedViewlet = viewlet;
}
public static clearDraggedViewlet(): void {
ViewletActionItem.draggedViewlet = void 0;
}
private showContextMenu(container: HTMLElement): void {
const actions: Action[] = [ViewletActionItem.toggleViewletPinnedAction];
if (this.viewlet.extensionId) {
actions.push(new Separator());
actions.push(ViewletActionItem.manageExtensionAction);
}
const isPinned = this.activityBarService.isPinned(this.viewlet.id);
if (isPinned) {
ViewletActionItem.toggleViewletPinnedAction.label = nls.localize('removeFromActivityBar', "Remove from Activity Bar");
} else {
ViewletActionItem.toggleViewletPinnedAction.label = nls.localize('keepInActivityBar', "Keep in Activity Bar");
}
this.contextMenuService.showContextMenu({
getAnchor: () => container,
getActionsContext: () => this.viewlet,
getActions: () => TPromise.as(actions)
});
}
public focus(): void {
this.$container.domFocus();
}
public set keybinding(keybinding: string) {
this._keybinding = keybinding;
if (!this.$label) {
return;
}
let title: string;
if (keybinding) {
title = nls.localize('titleKeybinding', "{0} ({1})", this.activity.name, keybinding);
} else {
title = this.activity.name;
}
this.$label.title(title);
this.$badge.title(title);
this.$container.title(title);
}
protected _updateClass(): void {
if (this.cssClass) {
this.$badge.removeClass(this.cssClass);
}
this.cssClass = this.getAction().class;
this.$badge.addClass(this.cssClass);
}
protected _updateChecked(): void {
if (this.getAction().checked) {
this.$container.addClass('checked');
} else {
this.$container.removeClass('checked');
}
}
protected _updateEnabled(): void {
if (this.getAction().enabled) {
this.builder.removeClass('disabled');
} else {
this.builder.addClass('disabled');
}
}
public dispose(): void {
super.dispose();
ViewletActionItem.clearDraggedViewlet();
this.$label.destroy();
}
}
export class ViewletOverflowActivityAction extends ActivityAction {
constructor(
private showMenu: () => void
) {
super({
id: 'activitybar.additionalViewlets.action',
name: nls.localize('additionalViews', "Additional Views"),
cssClass: 'toggle-more'
});
}
public run(event): TPromise<any> {
this.showMenu();
return TPromise.as(true);
}
}
export class ViewletOverflowActivityActionItem extends ActivityActionItem {
private name: string;
private cssClass: string;
private actions: OpenViewletAction[];
constructor(
action: ActivityAction,
private getOverflowingViewlets: () => ViewletDescriptor[],
private getBadge: (viewlet: ViewletDescriptor) => IBadge,
@IInstantiationService private instantiationService: IInstantiationService,
@IViewletService private viewletService: IViewletService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IThemeService themeService: IThemeService
) {
super(action, null, themeService);
this.cssClass = action.class;
this.name = action.label;
}
public showMenu(): void {
if (this.actions) {
dispose(this.actions);
}
this.actions = this.getActions();
this.contextMenuService.showContextMenu({
getAnchor: () => this.builder.getHTMLElement(),
getActions: () => TPromise.as(this.actions),
onHide: () => dispose(this.actions)
});
}
private getActions(): OpenViewletAction[] {
const activeViewlet = this.viewletService.getActiveViewlet();
return this.getOverflowingViewlets().map(viewlet => {
const action = this.instantiationService.createInstance(OpenViewletAction, viewlet);
action.radio = activeViewlet && activeViewlet.getId() === action.id;
const badge = this.getBadge(action.viewlet);
let suffix: string | number;
if (badge instanceof NumberBadge) {
suffix = badge.number;
} else if (badge instanceof TextBadge) {
suffix = badge.text;
}
if (suffix) {
action.label = nls.localize('numberBadge', "{0} ({1})", action.viewlet.name, suffix);
} else {
action.label = action.viewlet.name;
}
return action;
});
}
public dispose(): void {
super.dispose();
this.actions = dispose(this.actions);
}
}
class ManageExtensionAction extends Action {
constructor(
@ICommandService private commandService: ICommandService
) {
super('activitybar.manage.extension', nls.localize('manageExtension', "Manage Extension"));
}
public run(viewlet: ViewletDescriptor): TPromise<any> {
return this.commandService.executeCommand('_extensions.manage', viewlet.extensionId);
}
}
class OpenViewletAction extends Action {
constructor(
private _viewlet: ViewletDescriptor,
@IPartService private partService: IPartService,
@IViewletService private viewletService: IViewletService
) {
super(_viewlet.id, _viewlet.name);
}
public get viewlet(): ViewletDescriptor {
return this._viewlet;
}
public run(): TPromise<any> {
const sideBarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
const activeViewlet = this.viewletService.getActiveViewlet();
// Hide sidebar if selected viewlet already visible
if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.viewlet.id) {
return this.partService.setSideBarHidden(true);
}
return this.viewletService.openViewlet(this.viewlet.id, true);
}
}
export class ToggleViewletPinnedAction extends Action {
constructor(
private viewlet: ViewletDescriptor,
@IActivityBarService private activityBarService: IActivityBarService
) {
super('activitybar.show.toggleViewletPinned', viewlet ? viewlet.name : nls.localize('toggle', "Toggle View Pinned"));
this.checked = this.viewlet && this.activityBarService.isPinned(this.viewlet.id);
}
public run(context?: ViewletDescriptor): TPromise<any> {
const viewlet = this.viewlet || context;
if (this.activityBarService.isPinned(viewlet.id)) {
this.activityBarService.unpin(viewlet.id);
} else {
this.activityBarService.pin(viewlet.id);
}
return TPromise.as(true);
}
}
export class GlobalActivityAction extends ActivityAction {
constructor(activity: IGlobalActivity) {
super(activity);
}
}
export class GlobalActivityActionItem extends ActivityActionItem {
constructor(
action: GlobalActivityAction,
@IThemeService themeService: IThemeService,
@IContextMenuService protected contextMenuService: IContextMenuService
) {
super(action, { draggable: false }, themeService);
}
public render(container: HTMLElement): void {
super.render(container);
// Context menus are triggered on mouse down so that an item can be picked
// and executed with releasing the mouse over it
this.$container.on(DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
DOM.EventHelper.stop(e, true);
const event = new StandardMouseEvent(e);
this.showContextMenu({ x: event.posx, y: event.posy });
});
this.$container.on(DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
DOM.EventHelper.stop(e, true);
this.showContextMenu(this.$container.getHTMLElement());
}
});
}
private showContextMenu(location: HTMLElement | { x: number, y: number }): void {
const globalAction = this._action as GlobalActivityAction;
const activity = globalAction.activity as IGlobalActivity;
const actions = activity.getActions();
this.contextMenuService.showContextMenu({
getAnchor: () => location,
getActions: () => TPromise.as(actions),
onHide: () => dispose(actions)
});
}
}
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
// Styling with Outline color (e.g. high contrast theme)
const outline = theme.getColor(activeContrastBorder);
if (outline) {
collector.addRule(`
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:before {
content: "";
position: absolute;
top: 9px;
left: 9px;
height: 32px;
width: 32px;
opacity: 0.6;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:before,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:hover:before,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:before,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:hover:before {
outline: 1px solid;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover:before {
outline: 1px dashed;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:before,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:before,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover:before {
opacity: 1;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus:before {
border-left-color: ${outline};
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:before,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:hover:before,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:before,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:hover:before,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover:before {
outline-color: ${outline};
}
`);
}
// Styling without outline color
else {
const focusBorderColor = theme.getColor(focusBorder);
if (focusBorderColor) {
collector.addRule(`
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active .action-label,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked .action-label,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus .action-label,
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover .action-label {
opacity: 1;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item .action-label {
opacity: 0.6;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus:before {
border-left-color: ${focusBorderColor};
}
`);
}
}
});

View File

@@ -0,0 +1,562 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/activitybarpart';
import nls = require('vs/nls');
import { TPromise } from 'vs/base/common/winjs.base';
import DOM = require('vs/base/browser/dom');
import * as arrays from 'vs/base/common/arrays';
import { illegalArgument } from 'vs/base/common/errors';
import { Builder, $, Dimension } from 'vs/base/browser/builder';
import { Action } from 'vs/base/common/actions';
import { ActionsOrientation, ActionBar, IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/browser/activity';
import { Registry } from 'vs/platform/registry/common/platform';
import { Part } from 'vs/workbench/browser/part';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { ToggleViewletPinnedAction, ViewletActivityAction, ActivityAction, GlobalActivityActionItem, ViewletActionItem, ViewletOverflowActivityAction, ViewletOverflowActivityActionItem, GlobalActivityAction, IViewletActivity } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IActivityBarService, IBadge } from 'vs/workbench/services/activity/common/activityBarService';
import { IPartService, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Scope as MementoScope } from 'vs/workbench/common/memento';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/toggleActivityBarVisibility';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER } from 'vs/workbench/common/theme';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
export class ActivitybarPart extends Part implements IActivityBarService {
private static readonly ACTIVITY_ACTION_HEIGHT = 50;
private static readonly PINNED_VIEWLETS = 'workbench.activity.pinnedViewlets';
public _serviceBrand: any;
private dimension: Dimension;
private globalActionBar: ActionBar;
private globalActivityIdToActions: { [globalActivityId: string]: GlobalActivityAction; };
private viewletSwitcherBar: ActionBar;
private viewletOverflowAction: ViewletOverflowActivityAction;
private viewletOverflowActionItem: ViewletOverflowActivityActionItem;
private viewletIdToActions: { [viewletId: string]: ActivityAction; };
private viewletIdToActionItems: { [viewletId: string]: IActionItem; };
private viewletIdToActivityStack: { [viewletId: string]: IViewletActivity[]; };
private memento: object;
private pinnedViewlets: string[];
private activeUnpinnedViewlet: ViewletDescriptor;
constructor(
id: string,
@IViewletService private viewletService: IViewletService,
@IExtensionService private extensionService: IExtensionService,
@IStorageService private storageService: IStorageService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IInstantiationService private instantiationService: IInstantiationService,
@IPartService private partService: IPartService,
@IThemeService themeService: IThemeService
) {
super(id, { hasTitle: false }, themeService);
this.globalActivityIdToActions = Object.create(null);
this.viewletIdToActionItems = Object.create(null);
this.viewletIdToActions = Object.create(null);
this.viewletIdToActivityStack = Object.create(null);
this.memento = this.getMemento(this.storageService, MementoScope.GLOBAL);
const pinnedViewlets = this.memento[ActivitybarPart.PINNED_VIEWLETS] as string[];
if (pinnedViewlets) {
this.pinnedViewlets = pinnedViewlets
// TODO@Ben: Migrate git => scm viewlet
.map(id => id === 'workbench.view.git' ? 'workbench.view.scm' : id)
.filter(arrays.uniqueFilter<string>(str => str));
} else {
this.pinnedViewlets = this.viewletService.getViewlets().map(v => v.id);
}
this.registerListeners();
}
private registerListeners(): void {
// Activate viewlet action on opening of a viewlet
this.toUnbind.push(this.viewletService.onDidViewletOpen(viewlet => this.onDidViewletOpen(viewlet)));
// Deactivate viewlet action on close
this.toUnbind.push(this.viewletService.onDidViewletClose(viewlet => this.onDidViewletClose(viewlet)));
}
private onDidViewletOpen(viewlet: IViewlet): void {
const id = viewlet.getId();
if (this.viewletIdToActions[id]) {
this.viewletIdToActions[id].activate();
}
const activeUnpinnedViewletShouldClose = this.activeUnpinnedViewlet && this.activeUnpinnedViewlet.id !== viewlet.getId();
const activeUnpinnedViewletShouldShow = !this.getPinnedViewlets().some(v => v.id === viewlet.getId());
if (activeUnpinnedViewletShouldShow || activeUnpinnedViewletShouldClose) {
this.updateViewletSwitcher();
}
}
private onDidViewletClose(viewlet: IViewlet): void {
const id = viewlet.getId();
if (this.viewletIdToActions[id]) {
this.viewletIdToActions[id].deactivate();
}
}
public showGlobalActivity(globalActivityId: string, badge: IBadge): IDisposable {
if (!badge) {
throw illegalArgument('badge');
}
const action = this.globalActivityIdToActions[globalActivityId];
if (!action) {
throw illegalArgument('globalActivityId');
}
action.setBadge(badge);
return toDisposable(() => action.setBadge(undefined));
}
public showActivity(viewletId: string, badge: IBadge, clazz?: string): IDisposable {
if (!badge) {
throw illegalArgument('badge');
}
const activity = <IViewletActivity>{ badge, clazz };
const stack = this.viewletIdToActivityStack[viewletId] || (this.viewletIdToActivityStack[viewletId] = []);
stack.unshift(activity);
this.updateActivity(viewletId);
return {
dispose: () => {
const stack = this.viewletIdToActivityStack[viewletId];
if (!stack) {
return;
}
const idx = stack.indexOf(activity);
if (idx < 0) {
return;
}
stack.splice(idx, 1);
if (stack.length === 0) {
delete this.viewletIdToActivityStack[viewletId];
}
this.updateActivity(viewletId);
}
};
}
private updateActivity(viewletId: string) {
const action = this.viewletIdToActions[viewletId];
if (!action) {
return;
}
const stack = this.viewletIdToActivityStack[viewletId];
if (!stack || !stack.length) {
// reset
action.setBadge(undefined);
} else {
// update
const [{ badge, clazz }] = stack;
action.setBadge(badge);
if (clazz) {
action.class = clazz;
}
}
}
public createContentArea(parent: Builder): Builder {
const $el = $(parent);
const $result = $('.content').appendTo($el);
// Top Actionbar with action items for each viewlet action
this.createViewletSwitcher($result.clone());
// Top Actionbar with action items for each viewlet action
this.createGlobalActivityActionBar($result.getHTMLElement());
// Contextmenu for viewlets
$(parent).on('contextmenu', (e: MouseEvent) => {
DOM.EventHelper.stop(e, true);
this.showContextMenu(e);
}, this.toUnbind);
// Allow to drop at the end to move viewlet to the end
$(parent).on(DOM.EventType.DROP, (e: DragEvent) => {
const draggedViewlet = ViewletActionItem.getDraggedViewlet();
if (draggedViewlet) {
DOM.EventHelper.stop(e, true);
ViewletActionItem.clearDraggedViewlet();
const targetId = this.pinnedViewlets[this.pinnedViewlets.length - 1];
if (targetId !== draggedViewlet.id) {
this.move(draggedViewlet.id, this.pinnedViewlets[this.pinnedViewlets.length - 1]);
}
}
});
return $result;
}
public updateStyles(): void {
super.updateStyles();
// Part container
const container = this.getContainer();
const background = this.getColor(ACTIVITY_BAR_BACKGROUND);
container.style('background-color', background);
const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder);
const isPositionLeft = this.partService.getSideBarPosition() === SideBarPosition.LEFT;
container.style('box-sizing', borderColor && isPositionLeft ? 'border-box' : null);
container.style('border-right-width', borderColor && isPositionLeft ? '1px' : null);
container.style('border-right-style', borderColor && isPositionLeft ? 'solid' : null);
container.style('border-right-color', isPositionLeft ? borderColor : null);
container.style('border-left-width', borderColor && !isPositionLeft ? '1px' : null);
container.style('border-left-style', borderColor && !isPositionLeft ? 'solid' : null);
container.style('border-left-color', !isPositionLeft ? borderColor : null);
}
private showContextMenu(e: MouseEvent): void {
const event = new StandardMouseEvent(e);
const actions: Action[] = this.viewletService.getViewlets().map(viewlet => this.instantiationService.createInstance(ToggleViewletPinnedAction, viewlet));
actions.push(new Separator());
actions.push(this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar")));
this.contextMenuService.showContextMenu({
getAnchor: () => { return { x: event.posx + 1, y: event.posy }; },
getActions: () => TPromise.as(actions),
onHide: () => dispose(actions)
});
}
private createViewletSwitcher(div: Builder): void {
this.viewletSwitcherBar = new ActionBar(div, {
actionItemProvider: (action: Action) => action instanceof ViewletOverflowActivityAction ? this.viewletOverflowActionItem : this.viewletIdToActionItems[action.id],
orientation: ActionsOrientation.VERTICAL,
ariaLabel: nls.localize('activityBarAriaLabel', "Active View Switcher"),
animated: false
});
this.updateViewletSwitcher();
// Update viewlet switcher when external viewlets become ready
this.extensionService.onReady().then(() => this.updateViewletSwitcher());
}
private createGlobalActivityActionBar(container: HTMLElement): void {
const activityRegistry = Registry.as<IGlobalActivityRegistry>(GlobalActivityExtensions);
const descriptors = activityRegistry.getActivities();
const actions = descriptors
.map(d => this.instantiationService.createInstance(d))
.map(a => new GlobalActivityAction(a));
this.globalActionBar = new ActionBar(container, {
actionItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionItem, a),
orientation: ActionsOrientation.VERTICAL,
ariaLabel: nls.localize('globalActions', "Global Actions"),
animated: false
});
actions.forEach(a => {
this.globalActivityIdToActions[a.id] = a;
this.globalActionBar.push(a);
});
}
private updateViewletSwitcher() {
if (!this.viewletSwitcherBar) {
return; // We have not been rendered yet so there is nothing to update.
}
let viewletsToShow = this.getPinnedViewlets();
// Always show the active viewlet even if it is marked to be hidden
const activeViewlet = this.viewletService.getActiveViewlet();
if (activeViewlet && !viewletsToShow.some(viewlet => viewlet.id === activeViewlet.getId())) {
this.activeUnpinnedViewlet = this.viewletService.getViewlet(activeViewlet.getId());
viewletsToShow.push(this.activeUnpinnedViewlet);
} else {
this.activeUnpinnedViewlet = void 0;
}
// Ensure we are not showing more viewlets than we have height for
let overflows = false;
if (this.dimension) {
let availableHeight = this.dimension.height;
if (this.globalActionBar) {
availableHeight -= (this.globalActionBar.items.length * ActivitybarPart.ACTIVITY_ACTION_HEIGHT); // adjust for global actions showing
}
const maxVisible = Math.floor(availableHeight / ActivitybarPart.ACTIVITY_ACTION_HEIGHT);
overflows = viewletsToShow.length > maxVisible;
if (overflows) {
viewletsToShow = viewletsToShow.slice(0, maxVisible - 1 /* make room for overflow action */);
}
}
const visibleViewlets = Object.keys(this.viewletIdToActions);
const visibleViewletsChange = !arrays.equals(viewletsToShow.map(viewlet => viewlet.id), visibleViewlets);
// Pull out overflow action if there is a viewlet change so that we can add it to the end later
if (this.viewletOverflowAction && visibleViewletsChange) {
this.viewletSwitcherBar.pull(this.viewletSwitcherBar.length() - 1);
this.viewletOverflowAction.dispose();
this.viewletOverflowAction = null;
this.viewletOverflowActionItem.dispose();
this.viewletOverflowActionItem = null;
}
// Pull out viewlets that overflow or got hidden
const viewletIdsToShow = viewletsToShow.map(v => v.id);
visibleViewlets.forEach(viewletId => {
if (viewletIdsToShow.indexOf(viewletId) === -1) {
this.pullViewlet(viewletId);
}
});
// Built actions for viewlets to show
const newViewletsToShow = viewletsToShow
.filter(viewlet => !this.viewletIdToActions[viewlet.id])
.map(viewlet => this.toAction(viewlet));
// Update when we have new viewlets to show
if (newViewletsToShow.length) {
// Add to viewlet switcher
this.viewletSwitcherBar.push(newViewletsToShow, { label: true, icon: true });
// Make sure to activate the active one
const activeViewlet = this.viewletService.getActiveViewlet();
if (activeViewlet) {
const activeViewletEntry = this.viewletIdToActions[activeViewlet.getId()];
if (activeViewletEntry) {
activeViewletEntry.activate();
}
}
// Make sure to restore activity
Object.keys(this.viewletIdToActions).forEach(viewletId => {
this.updateActivity(viewletId);
});
}
// Add overflow action as needed
if (visibleViewletsChange && overflows) {
this.viewletOverflowAction = this.instantiationService.createInstance(ViewletOverflowActivityAction, () => this.viewletOverflowActionItem.showMenu());
this.viewletOverflowActionItem = this.instantiationService.createInstance(ViewletOverflowActivityActionItem, this.viewletOverflowAction, () => this.getOverflowingViewlets(), (viewlet: ViewletDescriptor) => this.viewletIdToActivityStack[viewlet.id] && this.viewletIdToActivityStack[viewlet.id][0].badge);
this.viewletSwitcherBar.push(this.viewletOverflowAction, { label: true, icon: true });
}
}
private getOverflowingViewlets(): ViewletDescriptor[] {
const viewlets = this.getPinnedViewlets();
if (this.activeUnpinnedViewlet) {
viewlets.push(this.activeUnpinnedViewlet);
}
const visibleViewlets = Object.keys(this.viewletIdToActions);
return viewlets.filter(viewlet => visibleViewlets.indexOf(viewlet.id) === -1);
}
private getVisibleViewlets(): ViewletDescriptor[] {
const viewlets = this.viewletService.getViewlets();
const visibleViewlets = Object.keys(this.viewletIdToActions);
return viewlets.filter(viewlet => visibleViewlets.indexOf(viewlet.id) >= 0);
}
private getPinnedViewlets(): ViewletDescriptor[] {
return this.pinnedViewlets.map(viewletId => this.viewletService.getViewlet(viewletId)).filter(v => !!v); // ensure to remove those that might no longer exist
}
private pullViewlet(viewletId: string): void {
const index = Object.keys(this.viewletIdToActions).indexOf(viewletId);
if (index >= 0) {
this.viewletSwitcherBar.pull(index);
const action = this.viewletIdToActions[viewletId];
action.dispose();
delete this.viewletIdToActions[viewletId];
const actionItem = this.viewletIdToActionItems[action.id];
actionItem.dispose();
delete this.viewletIdToActionItems[action.id];
}
}
private toAction(viewlet: ViewletDescriptor): ActivityAction {
const action = this.instantiationService.createInstance(ViewletActivityAction, viewlet);
this.viewletIdToActionItems[action.id] = this.instantiationService.createInstance(ViewletActionItem, action);
this.viewletIdToActions[viewlet.id] = action;
return action;
}
public getPinned(): string[] {
return this.pinnedViewlets;
}
public unpin(viewletId: string): void {
if (!this.isPinned(viewletId)) {
return;
}
const activeViewlet = this.viewletService.getActiveViewlet();
const defaultViewletId = this.viewletService.getDefaultViewletId();
const visibleViewlets = this.getVisibleViewlets();
let unpinPromise: TPromise<any>;
// Case: viewlet is not the active one or the active one is a different one
// Solv: we do nothing
if (!activeViewlet || activeViewlet.getId() !== viewletId) {
unpinPromise = TPromise.as(null);
}
// Case: viewlet is not the default viewlet and default viewlet is still showing
// Solv: we open the default viewlet
else if (defaultViewletId !== viewletId && this.isPinned(defaultViewletId)) {
unpinPromise = this.viewletService.openViewlet(defaultViewletId, true);
}
// Case: we closed the last visible viewlet
// Solv: we hide the sidebar
else if (visibleViewlets.length === 1) {
unpinPromise = this.partService.setSideBarHidden(true);
}
// Case: we closed the default viewlet
// Solv: we open the next visible viewlet from top
else {
unpinPromise = this.viewletService.openViewlet(visibleViewlets.filter(viewlet => viewlet.id !== viewletId)[0].id, true);
}
unpinPromise.then(() => {
// then remove from pinned and update switcher
const index = this.pinnedViewlets.indexOf(viewletId);
this.pinnedViewlets.splice(index, 1);
this.updateViewletSwitcher();
});
}
public isPinned(viewletId: string): boolean {
return this.pinnedViewlets.indexOf(viewletId) >= 0;
}
public pin(viewletId: string, update = true): void {
if (this.isPinned(viewletId)) {
return;
}
// first open that viewlet
this.viewletService.openViewlet(viewletId, true).then(() => {
// then update
this.pinnedViewlets.push(viewletId);
this.pinnedViewlets = arrays.distinct(this.pinnedViewlets);
if (update) {
this.updateViewletSwitcher();
}
});
}
public move(viewletId: string, toViewletId: string): void {
// Make sure a moved viewlet gets pinned
if (!this.isPinned(viewletId)) {
this.pin(viewletId, false /* defer update, we take care of it */);
}
const fromIndex = this.pinnedViewlets.indexOf(viewletId);
const toIndex = this.pinnedViewlets.indexOf(toViewletId);
this.pinnedViewlets.splice(fromIndex, 1);
this.pinnedViewlets.splice(toIndex, 0, viewletId);
// Clear viewlets that are impacted by the move
const visibleViewlets = Object.keys(this.viewletIdToActions);
for (let i = Math.min(fromIndex, toIndex); i < visibleViewlets.length; i++) {
this.pullViewlet(visibleViewlets[i]);
}
// timeout helps to prevent artifacts from showing up
setTimeout(() => {
this.updateViewletSwitcher();
}, 0);
}
/**
* Layout title, content and status area in the given dimension.
*/
public layout(dimension: Dimension): Dimension[] {
// Pass to super
const sizes = super.layout(dimension);
this.dimension = sizes[1];
// Update switcher to handle overflow issues
this.updateViewletSwitcher();
return sizes;
}
public dispose(): void {
if (this.viewletSwitcherBar) {
this.viewletSwitcherBar.dispose();
this.viewletSwitcherBar = null;
}
if (this.globalActionBar) {
this.globalActionBar.dispose();
this.globalActionBar = null;
}
super.dispose();
}
public shutdown(): void {
// Persist Hidden State
this.memento[ActivitybarPart.PINNED_VIEWLETS] = this.pinnedViewlets;
// Pass to super
super.shutdown();
}
}

View File

@@ -0,0 +1,81 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item {
display: block;
position: relative;
padding: 5px 0;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-label {
display: flex;
overflow: hidden;
height: 40px;
line-height: 40px;
margin-right: 0;
padding: 0 0 0 50px;
box-sizing: border-box;
font-size: 15px;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus:before {
content: "";
position: absolute;
top: 9px;
height: 32px;
width: 0;
border-left: 2px solid;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.clicked:focus:before {
border-left: none !important; /* no focus feedback when using mouse */
}
.monaco-workbench > .activitybar.left > .content .monaco-action-bar .action-item:focus:before {
left: 1px;
}
.monaco-workbench > .activitybar.right > .content .monaco-action-bar .action-item:focus:before {
right: 1px;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .action-label.toggle-more {
-webkit-mask: url('ellipsis-global.svg') no-repeat 50% 50%;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .badge {
position: absolute;
top: 5px;
left: 0;
overflow: hidden;
width: 50px;
height: 40px;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar .badge .badge-content {
position: absolute;
top: 20px;
right: 8px;
font-size: 11px;
min-width: 8px;
height: 18px;
line-height: 18px;
padding: 0 5px;
border-radius: 20px;
text-align: center;
}
/* Right aligned */
.monaco-workbench > .activitybar.right > .content .monaco-action-bar .action-label {
margin-left: 0;
padding: 0 50px 0 0;
background-position: calc(100% - 9px) center;
}
.monaco-workbench > .activitybar.right > .content .monaco-action-bar .badge {
left: auto;
right: 0;
}

View File

@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench > .part.activitybar {
width: 50px;
}
.monaco-workbench > .activitybar > .content {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar {
text-align: left;
background-color: inherit;
}
.monaco-workbench > .activitybar [tabindex="0"]:focus {
outline: 0 !important; /* activity bar indicates focus custom */
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32"><defs><style>.icon-canvas-transparent{fill:#f6f6f6;opacity:0;}.cls-1{fill:#fff;}</style></defs><title>Ellipsis_32x</title><g id="canvas"><path class="icon-canvas-transparent" d="M32,32H0V0H32Z" transform="translate(0 0)"/></g><g id="iconBg"><path class="cls-1" d="M9,16a3,3,0,1,1-3-3A3,3,0,0,1,9,16Zm7-3a3,3,0,1,0,3,3A3,3,0,0,0,16,13Zm10,0a3,3,0,1,0,3,3A3,3,0,0,0,26,13Z" transform="translate(0 0)"/></g></svg>

After

Width:  |  Height:  |  Size: 493 B

View File

@@ -0,0 +1,581 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/compositepart';
import nls = require('vs/nls');
import { defaultGenerator } from 'vs/base/common/idGenerator';
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import events = require('vs/base/common/events');
import strings = require('vs/base/common/strings');
import { Emitter } from 'vs/base/common/event';
import types = require('vs/base/common/types');
import errors = require('vs/base/common/errors');
import * as DOM from 'vs/base/browser/dom';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { CONTEXT as ToolBarContext, ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { IActionBarRegistry, Extensions, prepareActions } from 'vs/workbench/browser/actions';
import { Action, IAction } from 'vs/base/common/actions';
import { Part, IPartOptions } from 'vs/workbench/browser/part';
import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite';
import { IComposite } from 'vs/workbench/common/composite';
import { WorkbenchProgressService } from 'vs/workbench/services/progress/browser/progressService';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
export interface ICompositeTitleLabel {
/**
* Asks to update the title for the composite with the given ID.
*/
updateTitle(id: string, title: string, keybinding?: string): void;
/**
* Called when theming information changes.
*/
updateStyles(): void;
}
export abstract class CompositePart<T extends Composite> extends Part {
private instantiatedCompositeListeners: IDisposable[];
private mapCompositeToCompositeContainer: { [compositeId: string]: Builder; };
private mapActionsBindingToComposite: { [compositeId: string]: () => void; };
private mapProgressServiceToComposite: { [compositeId: string]: IProgressService; };
private activeComposite: Composite;
private lastActiveCompositeId: string;
private instantiatedComposites: Composite[];
private titleLabel: ICompositeTitleLabel;
private toolBar: ToolBar;
private compositeLoaderPromises: { [compositeId: string]: TPromise<Composite>; };
private progressBar: ProgressBar;
private contentAreaSize: Dimension;
private telemetryActionsListener: IDisposable;
private currentCompositeOpenToken: string;
protected _onDidCompositeOpen = new Emitter<IComposite>();
protected _onDidCompositeClose = new Emitter<IComposite>();
constructor(
private messageService: IMessageService,
private storageService: IStorageService,
private telemetryService: ITelemetryService,
private contextMenuService: IContextMenuService,
protected partService: IPartService,
private keybindingService: IKeybindingService,
protected instantiationService: IInstantiationService,
themeService: IThemeService,
private registry: CompositeRegistry<T>,
private activeCompositeSettingsKey: string,
private defaultCompositeId: string,
private nameForTelemetry: string,
private compositeCSSClass: string,
private actionContributionScope: string,
private titleForegroundColor: string,
id: string,
options: IPartOptions
) {
super(id, options, themeService);
this.instantiatedCompositeListeners = [];
this.mapCompositeToCompositeContainer = {};
this.mapActionsBindingToComposite = {};
this.mapProgressServiceToComposite = {};
this.activeComposite = null;
this.instantiatedComposites = [];
this.compositeLoaderPromises = {};
this.lastActiveCompositeId = storageService.get(activeCompositeSettingsKey, StorageScope.WORKSPACE, this.defaultCompositeId);
}
protected openComposite(id: string, focus?: boolean): TPromise<Composite> {
// Check if composite already visible and just focus in that case
if (this.activeComposite && this.activeComposite.getId() === id) {
if (focus) {
this.activeComposite.focus();
}
// Fullfill promise with composite that is being opened
return TPromise.as(this.activeComposite);
}
// Open
return this.doOpenComposite(id, focus);
}
private doOpenComposite(id: string, focus?: boolean): TPromise<Composite> {
// Use a generated token to avoid race conditions from long running promises
let currentCompositeOpenToken = defaultGenerator.nextId();
this.currentCompositeOpenToken = currentCompositeOpenToken;
// Hide current
let hidePromise: TPromise<Composite>;
if (this.activeComposite) {
hidePromise = this.hideActiveComposite();
} else {
hidePromise = TPromise.as(null);
}
return hidePromise.then(() => {
// Update Title
this.updateTitle(id);
// Create composite
return this.createComposite(id, true).then(composite => {
// Check if another composite opened meanwhile and return in that case
if ((this.currentCompositeOpenToken !== currentCompositeOpenToken) || (this.activeComposite && this.activeComposite.getId() !== composite.getId())) {
return TPromise.as(null);
}
// Check if composite already visible and just focus in that case
if (this.activeComposite && this.activeComposite.getId() === composite.getId()) {
if (focus) {
composite.focus();
}
// Fullfill promise with composite that is being opened
return TPromise.as(composite);
}
// Show Composite and Focus
return this.showComposite(composite).then(() => {
if (focus) {
composite.focus();
}
// Fullfill promise with composite that is being opened
return composite;
});
});
}).then(composite => {
if (composite) {
this._onDidCompositeOpen.fire(composite);
}
return composite;
});
}
protected createComposite(id: string, isActive?: boolean): TPromise<Composite> {
// Check if composite is already created
for (let i = 0; i < this.instantiatedComposites.length; i++) {
if (this.instantiatedComposites[i].getId() === id) {
return TPromise.as(this.instantiatedComposites[i]);
}
}
// Instantiate composite from registry otherwise
let compositeDescriptor = this.registry.getComposite(id);
if (compositeDescriptor) {
let loaderPromise = this.compositeLoaderPromises[id];
if (!loaderPromise) {
let progressService = this.instantiationService.createInstance(WorkbenchProgressService, this.progressBar, compositeDescriptor.id, isActive);
let compositeInstantiationService = this.instantiationService.createChild(new ServiceCollection([IProgressService, progressService]));
loaderPromise = compositeInstantiationService.createInstance(compositeDescriptor).then((composite: Composite) => {
this.mapProgressServiceToComposite[composite.getId()] = progressService;
// Remember as Instantiated
this.instantiatedComposites.push(composite);
// Register to title area update events from the composite
this.instantiatedCompositeListeners.push(composite.onTitleAreaUpdate(() => this.onTitleAreaUpdate(composite.getId())));
// Remove from Promises Cache since Loaded
delete this.compositeLoaderPromises[id];
return composite;
});
// Report progress for slow loading composites
progressService.showWhile(loaderPromise, this.partService.isCreated() ? 800 : 3200 /* less ugly initial startup */);
// Add to Promise Cache until Loaded
this.compositeLoaderPromises[id] = loaderPromise;
}
return loaderPromise;
}
throw new Error(strings.format('Unable to find composite with id {0}', id));
}
protected showComposite(composite: Composite): TPromise<void> {
// Remember Composite
this.activeComposite = composite;
// Store in preferences
const id = this.activeComposite.getId();
if (id !== this.defaultCompositeId) {
this.storageService.store(this.activeCompositeSettingsKey, id, StorageScope.WORKSPACE);
} else {
this.storageService.remove(this.activeCompositeSettingsKey, StorageScope.WORKSPACE);
}
// Remember
this.lastActiveCompositeId = this.activeComposite.getId();
let createCompositePromise: TPromise<void>;
// Composites created for the first time
let compositeContainer = this.mapCompositeToCompositeContainer[composite.getId()];
if (!compositeContainer) {
// Build Container off-DOM
compositeContainer = $().div({
'class': ['composite', this.compositeCSSClass],
id: composite.getId()
}, (div: Builder) => {
createCompositePromise = composite.create(div).then(() => {
composite.updateStyles();
});
});
// Remember composite container
this.mapCompositeToCompositeContainer[composite.getId()] = compositeContainer;
}
// Composite already exists but is hidden
else {
createCompositePromise = TPromise.as(null);
}
// Report progress for slow loading composites (but only if we did not create the composites before already)
let progressService = this.mapProgressServiceToComposite[composite.getId()];
if (progressService && !compositeContainer) {
this.mapProgressServiceToComposite[composite.getId()].showWhile(createCompositePromise, this.partService.isCreated() ? 800 : 3200 /* less ugly initial startup */);
}
// Fill Content and Actions
return createCompositePromise.then(() => {
// Make sure that the user meanwhile did not open another composite or closed the part containing the composite
if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) {
return void 0;
}
// Take Composite on-DOM and show
compositeContainer.build(this.getContentArea());
compositeContainer.show();
// Setup action runner
this.toolBar.actionRunner = composite.getActionRunner();
// Update title with composite title if it differs from descriptor
let descriptor = this.registry.getComposite(composite.getId());
if (descriptor && descriptor.name !== composite.getTitle()) {
this.updateTitle(composite.getId(), composite.getTitle());
}
// Handle Composite Actions
let actionsBinding = this.mapActionsBindingToComposite[composite.getId()];
if (!actionsBinding) {
actionsBinding = this.collectCompositeActions(composite);
this.mapActionsBindingToComposite[composite.getId()] = actionsBinding;
}
actionsBinding();
if (this.telemetryActionsListener) {
this.telemetryActionsListener.dispose();
this.telemetryActionsListener = null;
}
// Action Run Handling
this.telemetryActionsListener = this.toolBar.actionRunner.addListener(events.EventType.RUN, (e: any) => {
// Check for Error
if (e.error && !errors.isPromiseCanceledError(e.error)) {
this.messageService.show(Severity.Error, e.error);
}
// Log in telemetry
if (this.telemetryService) {
this.telemetryService.publicLog('workbenchActionExecuted', { id: e.action.id, from: this.nameForTelemetry });
}
});
// Indicate to composite that it is now visible
return composite.setVisible(true).then(() => {
// Make sure that the user meanwhile did not open another composite or closed the part containing the composite
if (!this.activeComposite || composite.getId() !== this.activeComposite.getId()) {
return;
}
// Make sure the composite is layed out
if (this.contentAreaSize) {
composite.layout(this.contentAreaSize);
}
});
}, (error: any) => this.onError(error));
}
protected onTitleAreaUpdate(compositeId: string): void {
// Active Composite
if (this.activeComposite && this.activeComposite.getId() === compositeId) {
// Title
this.updateTitle(this.activeComposite.getId(), this.activeComposite.getTitle());
// Actions
let actionsBinding = this.collectCompositeActions(this.activeComposite);
this.mapActionsBindingToComposite[this.activeComposite.getId()] = actionsBinding;
actionsBinding();
}
// Otherwise invalidate actions binding for next time when the composite becomes visible
else {
delete this.mapActionsBindingToComposite[compositeId];
}
}
private updateTitle(compositeId: string, compositeTitle?: string): void {
let compositeDescriptor = this.registry.getComposite(compositeId);
if (!compositeDescriptor) {
return;
}
if (!compositeTitle) {
compositeTitle = compositeDescriptor.name;
}
const keybinding = this.keybindingService.lookupKeybinding(compositeId);
this.titleLabel.updateTitle(compositeId, compositeTitle, keybinding ? keybinding.getLabel() : undefined);
this.toolBar.setAriaLabel(nls.localize('ariaCompositeToolbarLabel', "{0} actions", compositeTitle));
}
private collectCompositeActions(composite: Composite): () => void {
// From Composite
let primaryActions: IAction[] = composite.getActions().slice(0);
let secondaryActions: IAction[] = composite.getSecondaryActions().slice(0);
// From Part
primaryActions.push(...this.getActions());
secondaryActions.push(...this.getSecondaryActions());
// From Contributions
let actionBarRegistry = Registry.as<IActionBarRegistry>(Extensions.Actionbar);
primaryActions.push(...actionBarRegistry.getActionBarActionsForContext(this.actionContributionScope, composite));
secondaryActions.push(...actionBarRegistry.getSecondaryActionBarActionsForContext(this.actionContributionScope, composite));
// Return fn to set into toolbar
return this.toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions));
}
protected getActiveComposite(): IComposite {
return this.activeComposite;
}
protected getLastActiveCompositetId(): string {
return this.lastActiveCompositeId;
}
protected hideActiveComposite(): TPromise<Composite> {
if (!this.activeComposite) {
return TPromise.as(null); // Nothing to do
}
let composite = this.activeComposite;
this.activeComposite = null;
let compositeContainer = this.mapCompositeToCompositeContainer[composite.getId()];
// Indicate to Composite
return composite.setVisible(false).then(() => {
// Take Container Off-DOM and hide
compositeContainer.offDOM();
compositeContainer.hide();
// Clear any running Progress
this.progressBar.stop().getContainer().hide();
// Empty Actions
this.toolBar.setActions([])();
this._onDidCompositeClose.fire(composite);
return composite;
});
}
public createTitleArea(parent: Builder): Builder {
// Title Area Container
let titleArea = $(parent).div({
'class': ['composite', 'title']
});
$(titleArea).on(DOM.EventType.CONTEXT_MENU, (e: MouseEvent) => this.onTitleAreaContextMenu(new StandardMouseEvent(e)));
// Left Title Label
this.titleLabel = this.createTitleLabel(titleArea);
// Right Actions Container
$(titleArea).div({
'class': 'title-actions'
}, (div) => {
// Toolbar
this.toolBar = new ToolBar(div.getHTMLElement(), this.contextMenuService, {
actionItemProvider: (action: Action) => this.actionItemProvider(action),
orientation: ActionsOrientation.HORIZONTAL,
getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id)
});
});
return titleArea;
}
protected createTitleLabel(parent: Builder): ICompositeTitleLabel {
let titleLabel: Builder;
$(parent).div({
'class': 'title-label'
}, (div) => {
titleLabel = div.span();
});
const $this = this;
return {
updateTitle: (id, title, keybinding) => {
titleLabel.safeInnerHtml(title);
titleLabel.title(keybinding ? nls.localize('titleTooltip', "{0} ({1})", title, keybinding) : title);
},
updateStyles: () => {
titleLabel.style('color', $this.getColor($this.titleForegroundColor));
}
};
}
protected updateStyles(): void {
super.updateStyles();
// Forward to title label
this.titleLabel.updateStyles();
}
private onTitleAreaContextMenu(event: StandardMouseEvent): void {
if (this.activeComposite) {
const contextMenuActions = this.getTitleAreaContextMenuActions();
if (contextMenuActions.length) {
let anchor: { x: number, y: number } = { x: event.posx, y: event.posy };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.as(contextMenuActions),
getActionItem: (action: Action) => this.actionItemProvider(action),
actionRunner: this.activeComposite.getActionRunner(),
getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id)
});
}
}
}
protected getTitleAreaContextMenuActions(): IAction[] {
return this.activeComposite ? this.activeComposite.getContextMenuActions() : [];
}
private actionItemProvider(action: Action): IActionItem {
let actionItem: IActionItem;
// Check Active Composite
if (this.activeComposite) {
actionItem = this.activeComposite.getActionItem(action);
}
// Check Registry
if (!actionItem) {
let actionBarRegistry = Registry.as<IActionBarRegistry>(Extensions.Actionbar);
actionItem = actionBarRegistry.getActionItemForContext(this.actionContributionScope, ToolBarContext, action);
}
return actionItem;
}
public createContentArea(parent: Builder): Builder {
return $(parent).div({
'class': 'content'
}, (div: Builder) => {
this.progressBar = new ProgressBar(div);
this.toUnbind.push(attachProgressBarStyler(this.progressBar, this.themeService));
this.progressBar.getContainer().hide();
});
}
private onError(error: any): void {
this.messageService.show(Severity.Error, types.isString(error) ? new Error(error) : error);
}
public getProgressIndicator(id: string): IProgressService {
return this.mapProgressServiceToComposite[id];
}
protected getActions(): IAction[] {
return [];
}
protected getSecondaryActions(): IAction[] {
return [];
}
public layout(dimension: Dimension): Dimension[] {
// Pass to super
let sizes = super.layout(dimension);
// Pass Contentsize to composite
this.contentAreaSize = sizes[1];
if (this.activeComposite) {
this.activeComposite.layout(this.contentAreaSize);
}
return sizes;
}
public shutdown(): void {
this.instantiatedComposites.forEach(i => i.shutdown());
super.shutdown();
}
public dispose(): void {
this.mapCompositeToCompositeContainer = null;
this.mapProgressServiceToComposite = null;
this.mapActionsBindingToComposite = null;
for (let i = 0; i < this.instantiatedComposites.length; i++) {
this.instantiatedComposites[i].dispose();
}
this.instantiatedComposites = [];
this.instantiatedCompositeListeners = dispose(this.instantiatedCompositeListeners);
this.progressBar.dispose();
this.toolBar.dispose();
// Super Dispose
super.dispose();
}
}

View File

@@ -0,0 +1,305 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import types = require('vs/base/common/types');
import { Builder } from 'vs/base/browser/builder';
import { Registry } from 'vs/platform/registry/common/platform';
import { Panel } from 'vs/workbench/browser/panel';
import { EditorInput, EditorOptions, IEditorDescriptor, IEditorInputFactory, IEditorRegistry, Extensions, IFileInputFactory } from 'vs/workbench/common/editor';
import { IEditor, Position } from 'vs/platform/editor/common/editor';
import { IInstantiationService, IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
import { SyncDescriptor, AsyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/**
* The base class of editors in the workbench. Editors register themselves for specific editor inputs.
* Editors are layed out in the editor part of the workbench. Only one editor can be open at a time.
* Each editor has a minimized representation that is good enough to provide some information about the
* state of the editor data.
* The workbench will keep an editor alive after it has been created and show/hide it based on
* user interaction. The lifecycle of a editor goes in the order create(), setVisible(true|false),
* layout(), setInput(), focus(), dispose(). During use of the workbench, a editor will often receive a
* clearInput, setVisible, layout and focus call, but only one create and dispose call.
*
* This class is only intended to be subclassed and not instantiated.
*/
export abstract class BaseEditor extends Panel implements IEditor {
protected _input: EditorInput;
private _options: EditorOptions;
private _position: Position;
constructor(id: string, telemetryService: ITelemetryService, themeService: IThemeService) {
super(id, telemetryService, themeService);
}
public get input(): EditorInput {
return this._input;
}
public get options(): EditorOptions {
return this._options;
}
/**
* Note: Clients should not call this method, the workbench calls this
* method. Calling it otherwise may result in unexpected behavior.
*
* Sets the given input with the options to the part. An editor has to deal with the
* situation that the same input is being set with different options.
*/
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
this._input = input;
this._options = options;
return TPromise.as<void>(null);
}
/**
* Called to indicate to the editor that the input should be cleared and resources associated with the
* input should be freed.
*/
public clearInput(): void {
this._input = null;
this._options = null;
}
public create(parent: Builder): void; // create is sync for editors
public create(parent: Builder): TPromise<void>;
public create(parent: Builder): TPromise<void> {
const res = super.create(parent);
// Create Editor
this.createEditor(parent);
return res;
}
/**
* Called to create the editor in the parent builder.
*/
protected abstract createEditor(parent: Builder): void;
/**
* Overload this function to allow for passing in a position argument.
*/
public setVisible(visible: boolean, position?: Position): void; // setVisible is sync for editors
public setVisible(visible: boolean, position?: Position): TPromise<void>;
public setVisible(visible: boolean, position: Position = null): TPromise<void> {
const promise = super.setVisible(visible);
// Propagate to Editor
this.setEditorVisible(visible, position);
return promise;
}
protected setEditorVisible(visible: boolean, position: Position = null): void {
this._position = position;
}
/**
* Called when the position of the editor changes while it is visible.
*/
public changePosition(position: Position): void {
this._position = position;
}
/**
* The position this editor is showing in or null if none.
*/
public get position(): Position {
return this._position;
}
public dispose(): void {
this._input = null;
this._options = null;
// Super Dispose
super.dispose();
}
}
/**
* A lightweight descriptor of an editor. The descriptor is deferred so that heavy editors
* can load lazily in the workbench.
*/
export class EditorDescriptor extends AsyncDescriptor<BaseEditor> implements IEditorDescriptor {
private id: string;
private name: string;
constructor(id: string, name: string, moduleId: string, ctorName: string) {
super(moduleId, ctorName);
this.id = id;
this.name = name;
}
public getId(): string {
return this.id;
}
public getName(): string {
return this.name;
}
public describes(obj: any): boolean {
return obj instanceof BaseEditor && (<BaseEditor>obj).getId() === this.id;
}
}
const INPUT_DESCRIPTORS_PROPERTY = '__$inputDescriptors';
class EditorRegistry implements IEditorRegistry {
private editors: EditorDescriptor[];
private instantiationService: IInstantiationService;
private fileInputFactory: IFileInputFactory;
private editorInputFactoryConstructors: { [editorInputId: string]: IConstructorSignature0<IEditorInputFactory> } = Object.create(null);
private editorInputFactoryInstances: { [editorInputId: string]: IEditorInputFactory } = Object.create(null);
constructor() {
this.editors = [];
}
public setInstantiationService(service: IInstantiationService): void {
this.instantiationService = service;
for (let key in this.editorInputFactoryConstructors) {
const element = this.editorInputFactoryConstructors[key];
this.createEditorInputFactory(key, element);
}
this.editorInputFactoryConstructors = {};
}
private createEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0<IEditorInputFactory>): void {
const instance = this.instantiationService.createInstance(ctor);
this.editorInputFactoryInstances[editorInputId] = instance;
}
public registerEditor(descriptor: EditorDescriptor, editorInputDescriptor: SyncDescriptor<EditorInput>): void;
public registerEditor(descriptor: EditorDescriptor, editorInputDescriptor: SyncDescriptor<EditorInput>[]): void;
public registerEditor(descriptor: EditorDescriptor, editorInputDescriptor: any): void {
// Support both non-array and array parameter
let inputDescriptors: SyncDescriptor<EditorInput>[] = [];
if (!types.isArray(editorInputDescriptor)) {
inputDescriptors.push(editorInputDescriptor);
} else {
inputDescriptors = editorInputDescriptor;
}
// Register (Support multiple Editors per Input)
descriptor[INPUT_DESCRIPTORS_PROPERTY] = inputDescriptors;
this.editors.push(descriptor);
}
public getEditor(input: EditorInput): EditorDescriptor {
const findEditorDescriptors = (input: EditorInput, byInstanceOf?: boolean): EditorDescriptor[] => {
const matchingDescriptors: EditorDescriptor[] = [];
for (let i = 0; i < this.editors.length; i++) {
const editor = this.editors[i];
const inputDescriptors = <SyncDescriptor<EditorInput>[]>editor[INPUT_DESCRIPTORS_PROPERTY];
for (let j = 0; j < inputDescriptors.length; j++) {
const inputClass = inputDescriptors[j].ctor;
// Direct check on constructor type (ignores prototype chain)
if (!byInstanceOf && input.constructor === inputClass) {
matchingDescriptors.push(editor);
break;
}
// Normal instanceof check
else if (byInstanceOf && input instanceof inputClass) {
matchingDescriptors.push(editor);
break;
}
}
}
// If no descriptors found, continue search using instanceof and prototype chain
if (!byInstanceOf && matchingDescriptors.length === 0) {
return findEditorDescriptors(input, true);
}
if (byInstanceOf) {
return matchingDescriptors;
}
return matchingDescriptors;
};
const descriptors = findEditorDescriptors(input);
if (descriptors && descriptors.length > 0) {
// Ask the input for its preferred Editor
const preferredEditorId = input.getPreferredEditorId(descriptors.map(d => d.getId()));
if (preferredEditorId) {
return this.getEditorById(preferredEditorId);
}
// Otherwise, first come first serve
return descriptors[0];
}
return null;
}
public getEditorById(editorId: string): EditorDescriptor {
for (let i = 0; i < this.editors.length; i++) {
const editor = this.editors[i];
if (editor.getId() === editorId) {
return editor;
}
}
return null;
}
public getEditors(): EditorDescriptor[] {
return this.editors.slice(0);
}
public setEditors(editorsToSet: EditorDescriptor[]): void {
this.editors = editorsToSet;
}
public getEditorInputs(): any[] {
const inputClasses: any[] = [];
for (let i = 0; i < this.editors.length; i++) {
const editor = this.editors[i];
const editorInputDescriptors = <SyncDescriptor<EditorInput>[]>editor[INPUT_DESCRIPTORS_PROPERTY];
inputClasses.push(...editorInputDescriptors.map(descriptor => descriptor.ctor));
}
return inputClasses;
}
public registerFileInputFactory(factory: IFileInputFactory): void {
this.fileInputFactory = factory;
}
public getFileInputFactory(): IFileInputFactory {
return this.fileInputFactory;
}
public registerEditorInputFactory(editorInputId: string, ctor: IConstructorSignature0<IEditorInputFactory>): void {
if (!this.instantiationService) {
this.editorInputFactoryConstructors[editorInputId] = ctor;
} else {
this.createEditorInputFactory(editorInputId, ctor);
}
}
public getEditorInputFactory(editorInputId: string): IEditorInputFactory {
return this.editorInputFactoryInstances[editorInputId];
}
}
Registry.add(Extensions.Editors, new EditorRegistry());

View File

@@ -0,0 +1,41 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import nls = require('vs/nls');
import { BINARY_DIFF_EDITOR_ID } from 'vs/workbench/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor';
/**
* An implementation of editor for diffing binary files like images or videos.
*/
export class BinaryResourceDiffEditor extends SideBySideEditor {
public static ID = BINARY_DIFF_EDITOR_ID;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService
) {
super(telemetryService, instantiationService, themeService);
}
public getMetadata(): string {
const master = this.masterEditor;
const details = this.detailsEditor;
if (master instanceof BaseBinaryResourceEditor && details instanceof BaseBinaryResourceEditor) {
return nls.localize('metadataDiff', "{0} ↔ {1}", details.getMetadata(), master.getMetadata());
}
return null;
}
}

View File

@@ -0,0 +1,151 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import nls = require('vs/nls');
import Event, { Emitter } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import { ResourceViewer } from 'vs/base/browser/ui/resourceviewer/resourceViewer';
import { EditorModel, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWindowsService } from 'vs/platform/windows/common/windows';
/*
* This class is only intended to be subclassed and not instantiated.
*/
export abstract class BaseBinaryResourceEditor extends BaseEditor {
private _onMetadataChanged: Emitter<void>;
private metadata: string;
private binaryContainer: Builder;
private scrollbar: DomScrollableElement;
constructor(
id: string,
telemetryService: ITelemetryService,
themeService: IThemeService,
private windowsService: IWindowsService
) {
super(id, telemetryService, themeService);
this._onMetadataChanged = new Emitter<void>();
}
public get onMetadataChanged(): Event<void> {
return this._onMetadataChanged.event;
}
public getTitle(): string {
return this.input ? this.input.getName() : nls.localize('binaryEditor', "Binary Viewer");
}
protected createEditor(parent: Builder): void {
// Container for Binary
const binaryContainerElement = document.createElement('div');
binaryContainerElement.className = 'binary-container';
this.binaryContainer = $(binaryContainerElement);
this.binaryContainer.style('outline', 'none');
this.binaryContainer.tabindex(0); // enable focus support from the editor part (do not remove)
// Custom Scrollbars
this.scrollbar = new DomScrollableElement(binaryContainerElement, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto });
parent.getHTMLElement().appendChild(this.scrollbar.getDomNode());
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
const oldInput = this.input;
super.setInput(input, options);
// Detect options
const forceOpen = options && options.forceOpen;
// Same Input
if (!forceOpen && input.matches(oldInput)) {
return TPromise.as<void>(null);
}
// Different Input (Reload)
return input.resolve(true).then((resolvedModel: EditorModel) => {
// Assert Model instance
if (!(resolvedModel instanceof BinaryEditorModel)) {
return TPromise.wrapError<void>(new Error('Unable to open file as binary'));
}
// Assert that the current input is still the one we expect. This prevents a race condition when loading takes long and another input was set meanwhile
if (!this.input || this.input !== input) {
return null;
}
// Render Input
const model = <BinaryEditorModel>resolvedModel;
ResourceViewer.show(
{ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag() },
this.binaryContainer,
this.scrollbar,
(resource: URI) => {
this.windowsService.openExternal(resource.toString()).then(didOpen => {
if (!didOpen) {
return this.windowsService.showItemInFolder(resource.fsPath);
}
return void 0;
});
},
(meta) => this.handleMetadataChanged(meta));
return TPromise.as<void>(null);
});
}
private handleMetadataChanged(meta: string): void {
this.metadata = meta;
this._onMetadataChanged.fire();
}
public getMetadata(): string {
return this.metadata;
}
public clearInput(): void {
// Clear Meta
this.handleMetadataChanged(null);
// Empty HTML Container
$(this.binaryContainer).empty();
super.clearInput();
}
public layout(dimension: Dimension): void {
// Pass on to Binary Container
this.binaryContainer.size(dimension.width, dimension.height);
this.scrollbar.scanDomNode();
}
public focus(): void {
this.binaryContainer.domFocus();
}
public dispose(): void {
// Destroy Container
this.binaryContainer.destroy();
this.scrollbar.dispose();
super.dispose();
}
}

View File

@@ -0,0 +1,408 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Registry } from 'vs/platform/registry/common/platform';
import nls = require('vs/nls');
import URI from 'vs/base/common/uri';
import { Action, IAction } from 'vs/base/common/actions';
import { IEditorQuickOpenEntry, IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen';
import { StatusbarItemDescriptor, StatusbarAlignment, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorInput, IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory, SideBySideEditorInput } from 'vs/workbench/common/editor';
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
import { ChangeEncodingAction, ChangeEOLAction, ChangeModeAction, EditorStatus } from 'vs/workbench/browser/parts/editor/editorStatus';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry';
import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions, ActionBarContributor } from 'vs/workbench/browser/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import {
CloseEditorsInGroupAction, CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, JoinTwoGroupsAction, KeepEditorAction, CloseOtherEditorsInGroupAction, OpenToSideAction, RevertAndCloseEditorAction,
NavigateBetweenGroupsAction, FocusActiveGroupAction, FocusFirstGroupAction, FocusSecondGroupAction, FocusThirdGroupAction, EvenGroupWidthsAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup, ShowEditorsInGroupOneAction,
toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, CloseRightEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, ReopenClosedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, NAVIGATE_IN_GROUP_ONE_PREFIX,
OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, ClearEditorHistoryAction, ShowEditorsInGroupTwoAction, MoveEditorRightInGroupAction, OpenNextEditorInGroup, OpenPreviousEditorInGroup, OpenNextRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction,
NAVIGATE_IN_GROUP_TWO_PREFIX, ShowEditorsInGroupThreeAction, NAVIGATE_IN_GROUP_THREE_PREFIX, FocusLastEditorInStackAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction, MoveEditorToNextGroupAction, MoveEditorLeftInGroupAction, ClearRecentFilesAction
} from 'vs/workbench/browser/parts/editor/editorActions';
import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { getQuickNavigateHandler, inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
// Register String Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
TextResourceEditor.ID,
nls.localize('textEditor', "Text Editor"),
'vs/workbench/browser/parts/editor/textResourceEditor',
'TextResourceEditor'
),
[
new SyncDescriptor(UntitledEditorInput),
new SyncDescriptor(ResourceEditorInput)
]
);
// Register Text Diff Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
TextDiffEditor.ID,
nls.localize('textDiffEditor', "Text Diff Editor"),
'vs/workbench/browser/parts/editor/textDiffEditor',
'TextDiffEditor'
),
[
new SyncDescriptor(DiffEditorInput)
]
);
// Register Binary Resource Diff Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
BinaryResourceDiffEditor.ID,
nls.localize('binaryDiffEditor', "Binary Diff Editor"),
'vs/workbench/browser/parts/editor/binaryDiffEditor',
'BinaryResourceDiffEditor'
),
[
new SyncDescriptor(DiffEditorInput)
]
);
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
new EditorDescriptor(
SideBySideEditor.ID,
nls.localize('sideBySideEditor', "Side by Side Editor"),
'vs/workbench/browser/parts/editor/sideBySideEditor',
'SideBySideEditor'
),
[
new SyncDescriptor(SideBySideEditorInput)
]
);
interface ISerializedUntitledEditorInput {
resource: string;
resourceJSON: object;
modeId: string;
encoding: string;
}
// Register Editor Input Factory
class UntitledEditorInputFactory implements IEditorInputFactory {
constructor(
@ITextFileService private textFileService: ITextFileService
) {
}
public serialize(editorInput: EditorInput): string {
if (!this.textFileService.isHotExitEnabled) {
return null; // never restore untitled unless hot exit is enabled
}
const untitledEditorInput = <UntitledEditorInput>editorInput;
// {{SQL CARBON EDIT}}
if (!untitledEditorInput.getResource) {
return null;
}
let resource = untitledEditorInput.getResource();
if (untitledEditorInput.hasAssociatedFilePath) {
resource = URI.file(resource.fsPath); // untitled with associated file path use the file schema
}
const serialized: ISerializedUntitledEditorInput = {
resource: resource.toString(), // Keep for backwards compatibility
resourceJSON: resource.toJSON(),
modeId: untitledEditorInput.getModeId(),
encoding: untitledEditorInput.getEncoding()
};
return JSON.stringify(serialized);
}
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): UntitledEditorInput {
return instantiationService.invokeFunction<UntitledEditorInput>(accessor => {
const deserialized: ISerializedUntitledEditorInput = JSON.parse(serializedEditorInput);
const resource = !!deserialized.resourceJSON ? URI.revive(deserialized.resourceJSON) : URI.parse(deserialized.resource);
const filePath = resource.scheme === 'file' ? resource.fsPath : void 0;
const language = deserialized.modeId;
const encoding = deserialized.encoding;
return accessor.get(IWorkbenchEditorService).createInput({ resource, filePath, language, encoding }) as UntitledEditorInput;
});
}
}
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditorInputFactory(UntitledEditorInput.ID, UntitledEditorInputFactory);
interface ISerializedSideBySideEditorInput {
name: string;
description: string;
detailsSerialized: string;
masterSerialized: string;
detailsTypeId: string;
masterTypeId: string;
}
// Register Side by Side Editor Input Factory
class SideBySideEditorInputFactory implements IEditorInputFactory {
public serialize(editorInput: EditorInput): string {
const input = <SideBySideEditorInput>editorInput;
if (input.details && input.master) {
const registry = Registry.as<IEditorRegistry>(EditorExtensions.Editors);
const detailsInputFactory = registry.getEditorInputFactory(input.details.getTypeId());
const masterInputFactory = registry.getEditorInputFactory(input.master.getTypeId());
if (detailsInputFactory && masterInputFactory) {
const detailsSerialized = detailsInputFactory.serialize(input.details);
const masterSerialized = masterInputFactory.serialize(input.master);
if (detailsSerialized && masterSerialized) {
return JSON.stringify(<ISerializedSideBySideEditorInput>{
name: input.getName(),
description: input.getDescription(),
detailsSerialized,
masterSerialized,
detailsTypeId: input.details.getTypeId(),
masterTypeId: input.master.getTypeId()
});
}
}
}
return null;
}
public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
const deserialized: ISerializedSideBySideEditorInput = JSON.parse(serializedEditorInput);
const registry = Registry.as<IEditorRegistry>(EditorExtensions.Editors);
const detailsInputFactory = registry.getEditorInputFactory(deserialized.detailsTypeId);
const masterInputFactory = registry.getEditorInputFactory(deserialized.masterTypeId);
if (detailsInputFactory && masterInputFactory) {
const detailsInput = detailsInputFactory.deserialize(instantiationService, deserialized.detailsSerialized);
const masterInput = masterInputFactory.deserialize(instantiationService, deserialized.masterSerialized);
if (detailsInput && masterInput) {
return new SideBySideEditorInput(deserialized.name, deserialized.description, detailsInput, masterInput);
}
}
return null;
}
}
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditorInputFactory(SideBySideEditorInput.ID, SideBySideEditorInputFactory);
// Register Editor Status
const statusBar = Registry.as<IStatusbarRegistry>(StatusExtensions.Statusbar);
statusBar.registerStatusbarItem(new StatusbarItemDescriptor(EditorStatus, StatusbarAlignment.RIGHT, 100 /* High Priority */));
// Register Status Actions
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeModeAction, ChangeModeAction.ID, ChangeModeAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_M) }), 'Change Language Mode');
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEOLAction, ChangeEOLAction.ID, ChangeEOLAction.LABEL), 'Change End of Line Sequence');
registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL), 'Change File Encoding');
export class QuickOpenActionContributor extends ActionBarContributor {
private openToSideActionInstance: OpenToSideAction;
constructor( @IInstantiationService private instantiationService: IInstantiationService) {
super();
}
public hasActions(context: any): boolean {
const entry = this.getEntry(context);
return !!entry;
}
public getActions(context: any): IAction[] {
const actions: Action[] = [];
const entry = this.getEntry(context);
if (entry) {
if (!this.openToSideActionInstance) {
this.openToSideActionInstance = this.instantiationService.createInstance(OpenToSideAction);
} else {
this.openToSideActionInstance.updateClass();
}
actions.push(this.openToSideActionInstance);
}
return actions;
}
private getEntry(context: any): IEditorQuickOpenEntry {
if (!context || !context.element) {
return null;
}
return toEditorQuickOpenEntry(context.element);
}
}
const actionBarRegistry = Registry.as<IActionBarRegistry>(ActionBarExtensions.Actionbar);
actionBarRegistry.registerActionBarContributor(Scope.VIEWER, QuickOpenActionContributor);
const editorPickerContextKey = 'inEditorsPicker';
const editorPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(editorPickerContextKey));
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/browser/parts/editor/editorPicker',
'GroupOnePicker',
NAVIGATE_IN_GROUP_ONE_PREFIX,
editorPickerContextKey,
[
{
prefix: NAVIGATE_IN_GROUP_ONE_PREFIX,
needsEditor: false,
description: nls.localize('groupOnePicker', "Show Editors in First Group")
},
{
prefix: NAVIGATE_IN_GROUP_TWO_PREFIX,
needsEditor: false,
description: nls.localize('groupTwoPicker', "Show Editors in Second Group")
},
{
prefix: NAVIGATE_IN_GROUP_THREE_PREFIX,
needsEditor: false,
description: nls.localize('groupThreePicker', "Show Editors in Third Group")
}
]
)
);
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/browser/parts/editor/editorPicker',
'GroupTwoPicker',
NAVIGATE_IN_GROUP_TWO_PREFIX,
editorPickerContextKey,
[]
)
);
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/browser/parts/editor/editorPicker',
'GroupThreePicker',
NAVIGATE_IN_GROUP_THREE_PREFIX,
editorPickerContextKey,
[]
)
);
Registry.as<IQuickOpenRegistry>(QuickOpenExtensions.Quickopen).registerQuickOpenHandler(
new QuickOpenHandlerDescriptor(
'vs/workbench/browser/parts/editor/editorPicker',
'AllEditorsPicker',
NAVIGATE_ALL_EDITORS_GROUP_PREFIX,
editorPickerContextKey,
[
{
prefix: NAVIGATE_ALL_EDITORS_GROUP_PREFIX,
needsEditor: false,
description: nls.localize('allEditorsPicker', "Show All Opened Editors")
}
]
)
);
// Register Editor Actions
const category = nls.localize('view', "View");
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditorInGroup, OpenNextEditorInGroup.ID, OpenNextEditorInGroup.LABEL), 'View: Open Next Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorInGroup, OpenPreviousEditorInGroup.ID, OpenPreviousEditorInGroup.LABEL), 'View: Open Previous Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorAction, OpenNextRecentlyUsedEditorAction.ID, OpenNextRecentlyUsedEditorAction.LABEL), 'View: Open Next Recently Used Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction.ID, OpenPreviousRecentlyUsedEditorAction.LABEL), 'View: Open Previous Recently Used Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllEditorsAction, ShowAllEditorsAction.ID, ShowAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_P), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupOneAction, ShowEditorsInGroupOneAction.ID, ShowEditorsInGroupOneAction.LABEL), 'View: Show Editors in First Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupTwoAction, ShowEditorsInGroupTwoAction.ID, ShowEditorsInGroupTwoAction.LABEL), 'View: Show Editors in Second Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowEditorsInGroupThreeAction, ShowEditorsInGroupThreeAction.ID, ShowEditorsInGroupThreeAction.LABEL), 'View: Show Editors in Third Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditor, OpenNextEditor.ID, OpenNextEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageDown, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET] } }), 'View: Open Next Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditor, OpenPreviousEditor.ID, OpenPreviousEditor.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET] } }), 'View: Open Previous Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ReopenClosedEditorAction, ReopenClosedEditorAction.ID, ReopenClosedEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'View: Reopen Closed Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearRecentFilesAction, ClearRecentFilesAction.ID, ClearRecentFilesAction.LABEL), 'View: Clear Recently Opened', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(KeepEditorAction, KeepEditorAction.ID, KeepEditorAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.Enter) }), 'View: Keep Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_W) }), 'View: Close All Editors', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseLeftEditorsInGroupAction, CloseLeftEditorsInGroupAction.ID, CloseLeftEditorsInGroupAction.LABEL), 'View: Close Editors to the Left', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseRightEditorsInGroupAction, CloseRightEditorsInGroupAction.ID, CloseRightEditorsInGroupAction.LABEL), 'View: Close Editors to the Right', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseUnmodifiedEditorsInGroupAction, CloseUnmodifiedEditorsInGroupAction.ID, CloseUnmodifiedEditorsInGroupAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U) }), 'View: Close Unmodified Editors in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorsInGroupAction, CloseEditorsInGroupAction.ID, CloseEditorsInGroupAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W) }), 'View: Close All Editors in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseOtherEditorsInGroupAction, CloseOtherEditorsInGroupAction.ID, CloseOtherEditorsInGroupAction.LABEL, { primary: null, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T } }), 'View: Close Other Editors', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseEditorsInOtherGroupsAction, CloseEditorsInOtherGroupsAction.ID, CloseEditorsInOtherGroupsAction.LABEL), 'View: Close Editors in Other Groups', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(SplitEditorAction, SplitEditorAction.ID, SplitEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.US_BACKSLASH }), 'View: Split Editor', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(JoinTwoGroupsAction, JoinTwoGroupsAction.ID, JoinTwoGroupsAction.LABEL), 'View: Join Editors of Two Groups', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBetweenGroupsAction, NavigateBetweenGroupsAction.ID, NavigateBetweenGroupsAction.LABEL), 'View: Navigate Between Editor Groups', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusActiveGroupAction, FocusActiveGroupAction.ID, FocusActiveGroupAction.LABEL), 'View: Focus Active Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusFirstGroupAction, FocusFirstGroupAction.ID, FocusFirstGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus First Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSecondGroupAction, FocusSecondGroupAction.ID, FocusSecondGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_2 }), 'View: Focus Second Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusThirdGroupAction, FocusThirdGroupAction.ID, FocusThirdGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_3 }), 'View: Focus Third Editor Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusLastEditorInStackAction, FocusLastEditorInStackAction.ID, FocusLastEditorInStackAction.LABEL, { primary: KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_0 } }), 'View: Open Last Editor in Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(EvenGroupWidthsAction, EvenGroupWidthsAction.ID, EvenGroupWidthsAction.LABEL), 'View: Even Editor Group Widths', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MaximizeGroupAction, MaximizeGroupAction.ID, MaximizeGroupAction.LABEL), 'View: Maximize Editor Group and Hide Sidebar', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MinimizeOtherGroupsAction, MinimizeOtherGroupsAction.ID, MinimizeOtherGroupsAction.LABEL), 'View: Minimize Other Editor Groups', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorLeftInGroupAction, MoveEditorLeftInGroupAction.ID, MoveEditorLeftInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageUp, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.LeftArrow) } }), 'View: Move Editor Left', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorRightInGroupAction, MoveEditorRightInGroupAction.ID, MoveEditorRightInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.PageDown, mac: { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.RightArrow) } }), 'View: Move Editor Right', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupLeftAction, MoveGroupLeftAction.ID, MoveGroupLeftAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.LeftArrow) }), 'View: Move Editor Group Left', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveGroupRightAction, MoveGroupRightAction.ID, MoveGroupRightAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.RightArrow) }), 'View: Move Editor Group Right', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToPreviousGroupAction, MoveEditorToPreviousGroupAction.ID, MoveEditorToPreviousGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.LeftArrow } }), 'View: Move Editor into Previous Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(MoveEditorToNextGroupAction, MoveEditorToNextGroupAction.ID, MoveEditorToNextGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.RightArrow, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.RightArrow } }), 'View: Move Editor into Next Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusPreviousGroup, FocusPreviousGroup.ID, FocusPreviousGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.LeftArrow) }), 'View: Focus Previous Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusNextGroup, FocusNextGroup.ID, FocusNextGroup.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.RightArrow) }), 'View: Focus Next Group', category);
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateForwardAction, NavigateForwardAction.ID, NavigateForwardAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.RightArrow }, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_MINUS } }), 'Go Forward');
registry.registerWorkbenchAction(new SyncActionDescriptor(NavigateBackwardsAction, NavigateBackwardsAction.ID, NavigateBackwardsAction.LABEL, { primary: null, win: { primary: KeyMod.Alt | KeyCode.LeftArrow }, mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS } }), 'Go Back');
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorFromHistoryAction, OpenPreviousEditorFromHistoryAction.ID, OpenPreviousEditorFromHistoryAction.LABEL), 'Open Previous Editor from History');
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearEditorHistoryAction, ClearEditorHistoryAction.ID, ClearEditorHistoryAction.LABEL), 'Clear Editor History');
registry.registerWorkbenchAction(new SyncActionDescriptor(RevertAndCloseEditorAction, RevertAndCloseEditorAction.ID, RevertAndCloseEditorAction.LABEL), 'View: Revert and Close Editor', category);
// Register Editor Picker Actions including quick navigate support
const openNextEditorKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } };
const openPreviousEditorKeybinding = { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } };
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction.ID, OpenNextRecentlyUsedEditorInGroupAction.LABEL, openNextEditorKeybinding), 'Open Next Recently Used Editor in Group');
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction.ID, OpenPreviousRecentlyUsedEditorInGroupAction.LABEL, openPreviousEditorKeybinding), 'Open Previous Recently Used Editor in Group');
const quickOpenNavigateNextInEditorPickerId = 'workbench.action.quickOpenNavigateNextInEditorPicker';
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: quickOpenNavigateNextInEditorPickerId,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
handler: getQuickNavigateHandler(quickOpenNavigateNextInEditorPickerId, true),
when: editorPickerContext,
primary: openNextEditorKeybinding.primary,
mac: openNextEditorKeybinding.mac
});
const quickOpenNavigatePreviousInEditorPickerId = 'workbench.action.quickOpenNavigatePreviousInEditorPicker';
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: quickOpenNavigatePreviousInEditorPickerId,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(50),
handler: getQuickNavigateHandler(quickOpenNavigatePreviousInEditorPickerId, false),
when: editorPickerContext,
primary: openPreviousEditorKeybinding.primary,
mac: openPreviousEditorKeybinding.mac
});
// Editor Commands
editorCommands.setup();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,275 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { ActiveEditorMoveArguments, ActiveEditorMovePositioning, ActiveEditorMovePositioningBy, EditorCommands, TextCompareEditorVisible } from 'vs/workbench/common/editor';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditor, Position, POSITIONS } from 'vs/platform/editor/common/editor';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
import { EditorStacksModel } from 'vs/workbench/common/editor/editorStacksModel';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IMessageService, Severity, CloseAction } from 'vs/platform/message/common/message';
import { Action } from 'vs/base/common/actions';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
export function setup(): void {
registerActiveEditorMoveCommand();
registerDiffEditorCommands();
registerOpenEditorAtIndexCommands();
handleCommandDeprecations();
}
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
if (!types.isObject(arg)) {
return false;
}
const activeEditorMoveArg: ActiveEditorMoveArguments = arg;
if (!types.isString(activeEditorMoveArg.to)) {
return false;
}
if (!types.isUndefined(activeEditorMoveArg.by) && !types.isString(activeEditorMoveArg.by)) {
return false;
}
if (!types.isUndefined(activeEditorMoveArg.value) && !types.isNumber(activeEditorMoveArg.value)) {
return false;
}
return true;
};
function registerActiveEditorMoveCommand(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: EditorCommands.MoveActiveEditor,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: EditorContextKeys.textFocus,
primary: null,
handler: (accessor, args: any) => moveActiveEditor(args, accessor),
description: {
description: nls.localize('editorCommand.activeEditorMove.description', "Move the active editor by tabs or groups"),
args: [
{
name: nls.localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"),
description: nls.localize('editorCommand.activeEditorMove.arg.description', `Argument Properties:
* 'to': String value providing where to move.
* 'by': String value providing the unit for move. By tab or by group.
* 'value': Number value providing how many positions or an absolute position to move.
`),
constraint: isActiveEditorMoveArg
}
]
}
});
}
function moveActiveEditor(args: ActiveEditorMoveArguments = {}, accessor: ServicesAccessor): void {
const showTabs = accessor.get(IEditorGroupService).getTabOptions().showTabs;
args.to = args.to || ActiveEditorMovePositioning.RIGHT;
args.by = showTabs ? args.by || ActiveEditorMovePositioningBy.TAB : ActiveEditorMovePositioningBy.GROUP;
args.value = types.isUndefined(args.value) ? 1 : args.value;
const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor();
if (activeEditor) {
switch (args.by) {
case ActiveEditorMovePositioningBy.TAB:
return moveActiveTab(args, activeEditor, accessor);
case ActiveEditorMovePositioningBy.GROUP:
return moveActiveEditorToGroup(args, activeEditor, accessor);
}
}
}
function moveActiveTab(args: ActiveEditorMoveArguments, activeEditor: IEditor, accessor: ServicesAccessor): void {
const editorGroupsService: IEditorGroupService = accessor.get(IEditorGroupService);
const editorGroup = editorGroupsService.getStacksModel().groupAt(activeEditor.position);
let index = editorGroup.indexOf(activeEditor.input);
switch (args.to) {
case ActiveEditorMovePositioning.FIRST:
index = 0;
break;
case ActiveEditorMovePositioning.LAST:
index = editorGroup.count - 1;
break;
case ActiveEditorMovePositioning.LEFT:
index = index - args.value;
break;
case ActiveEditorMovePositioning.RIGHT:
index = index + args.value;
break;
case ActiveEditorMovePositioning.CENTER:
index = Math.round(editorGroup.count / 2) - 1;
break;
case ActiveEditorMovePositioning.POSITION:
index = args.value - 1;
break;
}
index = index < 0 ? 0 : index >= editorGroup.count ? editorGroup.count - 1 : index;
editorGroupsService.moveEditor(activeEditor.input, editorGroup, editorGroup, { index });
}
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, activeEditor: IEditor, accessor: ServicesAccessor): void {
let newPosition = activeEditor.position;
switch (args.to) {
case ActiveEditorMovePositioning.LEFT:
newPosition = newPosition - 1;
break;
case ActiveEditorMovePositioning.RIGHT:
newPosition = newPosition + 1;
break;
case ActiveEditorMovePositioning.FIRST:
newPosition = Position.ONE;
break;
case ActiveEditorMovePositioning.LAST:
newPosition = Position.THREE;
break;
case ActiveEditorMovePositioning.CENTER:
newPosition = Position.TWO;
break;
case ActiveEditorMovePositioning.POSITION:
newPosition = args.value - 1;
break;
}
newPosition = POSITIONS.indexOf(newPosition) !== -1 ? newPosition : activeEditor.position;
accessor.get(IEditorGroupService).moveEditor(activeEditor.input, activeEditor.position, newPosition);
}
function registerDiffEditorCommands(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'workbench.action.compareEditor.nextChange',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: TextCompareEditorVisible,
primary: null,
handler: accessor => navigateInDiffEditor(accessor, true)
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'workbench.action.compareEditor.previousChange',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: TextCompareEditorVisible,
primary: null,
handler: accessor => navigateInDiffEditor(accessor, false)
});
function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
let editorService = accessor.get(IWorkbenchEditorService);
const candidates = [editorService.getActiveEditor(), ...editorService.getVisibleEditors()].filter(e => e instanceof TextDiffEditor);
if (candidates.length > 0) {
next ? (<TextDiffEditor>candidates[0]).getDiffNavigator().next() : (<TextDiffEditor>candidates[0]).getDiffNavigator().previous();
}
}
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: '_workbench.printStacksModel',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
handler(accessor: ServicesAccessor) {
console.log(`${accessor.get(IEditorGroupService).getStacksModel().toString()}\n\n`);
},
when: undefined,
primary: undefined
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: '_workbench.validateStacksModel',
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
handler(accessor: ServicesAccessor) {
(<EditorStacksModel>accessor.get(IEditorGroupService).getStacksModel()).validate();
},
when: undefined,
primary: undefined
});
}
function handleCommandDeprecations(): void {
const mapDeprecatedCommands = {
'workbench.action.files.newFile': 'explorer.newFile',
'workbench.action.files.newFolder': 'explorer.newFolder'
};
Object.keys(mapDeprecatedCommands).forEach(deprecatedCommandId => {
const newCommandId = mapDeprecatedCommands[deprecatedCommandId];
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: deprecatedCommandId,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
handler(accessor: ServicesAccessor) {
const messageService = accessor.get(IMessageService);
const commandService = accessor.get(ICommandService);
messageService.show(Severity.Warning, {
message: nls.localize('commandDeprecated', "Command **{0}** has been removed. You can use **{1}** instead", deprecatedCommandId, newCommandId),
actions: [
new Action('openKeybindings', nls.localize('openKeybindings', "Configure Keyboard Shortcuts"), null, true, () => {
return commandService.executeCommand('workbench.action.openGlobalKeybindings');
}),
CloseAction
]
});
},
when: undefined,
primary: undefined
});
});
}
function registerOpenEditorAtIndexCommands(): void {
// Keybindings to focus a specific index in the tab folder if tabs are enabled
for (let i = 0; i < 9; i++) {
const editorIndex = i;
const visibleIndex = i + 1;
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: 'workbench.action.openEditorAtIndex' + visibleIndex,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: void 0,
primary: KeyMod.Alt | toKeyCode(visibleIndex),
mac: { primary: KeyMod.WinCtrl | toKeyCode(visibleIndex) },
handler: accessor => {
const editorService = accessor.get(IWorkbenchEditorService);
const editorGroupService = accessor.get(IEditorGroupService);
const active = editorService.getActiveEditor();
if (active) {
const group = editorGroupService.getStacksModel().groupAt(active.position);
const editor = group.getEditor(editorIndex);
if (editor) {
return editorService.openEditor(editor);
}
}
return void 0;
}
});
}
function toKeyCode(index: number): KeyCode {
switch (index) {
case 0: return KeyCode.KEY_0;
case 1: return KeyCode.KEY_1;
case 2: return KeyCode.KEY_2;
case 3: return KeyCode.KEY_3;
case 4: return KeyCode.KEY_4;
case 5: return KeyCode.KEY_5;
case 6: return KeyCode.KEY_6;
case 7: return KeyCode.KEY_7;
case 8: return KeyCode.KEY_8;
case 9: return KeyCode.KEY_9;
}
return void 0;
}
}

Some files were not shown because too many files have changed in this diff Show More