Merge VS Code 1.23.1 (#1520)

This commit is contained in:
Matt Irvine
2018-06-05 11:24:51 -07:00
committed by GitHub
parent e3baf5c443
commit 0c58f09e59
3651 changed files with 74249 additions and 48599 deletions

View File

@@ -0,0 +1,10 @@
<svg width="24" height="24" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Canvas" fill="none">
<g id="beaker">
<g id="Shape">
<path fill-rule="evenodd" clip-rule="evenodd" d="M 20.7596 21.885L 15.6896 10.5L 15.6896 4.5L 17.1896 4.5L 17.1896 3L 3.68959 3L 3.68959 4.5L 5.18959 4.5L 5.18959 10.5L 0.134588 21.885C -0.315412 22.875 0.419588 24 1.49959 24L 19.4096 24C 20.4896 24 21.2096 22.875 20.7746 21.885L 20.7596 21.885ZM 4.81458 15L 6.68958 10.5L 6.68958 4.5L 14.1896 4.5L 14.1896 10.5L 16.0646 15L 4.81458 15ZM 11.1896 12L 12.6896 12L 12.6896 13.5L 11.1896 13.5L 11.1896 12ZM 9.68958 10.5L 8.18958 10.5L 8.18958 9L 9.68958 9L 9.68958 10.5ZM 9.68958 6L 11.1896 6L 11.1896 7.5L 9.68958 7.5L 9.68958 6ZM 9.68958 1.5L 8.18958 1.5L 8.18958 0L 9.68958 0L 9.68958 1.5Z" transform="translate(0.810059 0)" fill="white"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 913 B

View File

@@ -0,0 +1,206 @@
/*---------------------------------------------------------------------------------------------
* 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 { IJSONSchema } from 'vs/base/common/jsonSchema';
import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { join } from 'vs/base/common/paths';
import { createCSSRule } from 'vs/base/browser/dom';
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions, ToggleViewletAction } from 'vs/workbench/browser/viewlet';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IExtensionService, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ViewLocation } from 'vs/workbench/common/views';
import { PersistentViewsViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { forEach } from 'vs/base/common/collections';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
export interface IUserFriendlyViewsContainerDescriptor {
id: string;
title: string;
icon: string;
}
const viewsContainerSchema: IJSONSchema = {
type: 'object',
properties: {
id: {
description: localize({ key: 'vscode.extension.contributes.views.containers.id', comment: ['Contribution refers to those that an extension contributes to VS Code through an extension/contribution point. '] }, "Unique id used to identify the container in which views can be contributed using 'views' contribution point"),
type: 'string',
pattern: '^[a-zA-Z0-9_-]+$'
},
title: {
description: localize('vscode.extension.contributes.views.containers.title', 'Human readable string used to render the container'),
type: 'string'
},
icon: {
description: localize('vscode.extension.contributes.views.containers.icon', "Path to the container icon. Icons are 24x24 centered on a 50x40 square and have a fill color of 'rgb(215, 218, 224)' or '#d7dae0'. It is recommended that icons be in SVG, though any image file type is accepted."),
type: 'string'
}
}
};
export const viewsContainersContribution: IJSONSchema = {
description: localize('vscode.extension.contributes.viewsContainers', 'Contributes views containers to the editor'),
type: 'object',
properties: {
'activitybar': {
description: localize('views.container.activitybar', "Contribute views containers to Activity Bar"),
type: 'array',
items: viewsContainerSchema
}
}
};
export const viewsContainersExtensionPoint: IExtensionPoint<{ [loc: string]: IUserFriendlyViewsContainerDescriptor[] }> = ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: IUserFriendlyViewsContainerDescriptor[] }>('viewsContainers', [], viewsContainersContribution);
const TEST_VIEW_CONTAINER_ORDER = 6;
class ViewsContainersExtensionHandler implements IWorkbenchContribution {
constructor() {
this.registerTestViewContainer();
this.handleAndRegisterCustomViewContainers();
}
private registerTestViewContainer(): void {
const id = 'test';
const title = localize('test', "Test");
const cssClass = `extensionViewlet-${id}`;
const icon = require.toUrl('./media/test.svg');
this.registerCustomViewlet({ id, title, icon }, TEST_VIEW_CONTAINER_ORDER, cssClass);
}
private handleAndRegisterCustomViewContainers() {
viewsContainersExtensionPoint.setHandler((extensions) => {
for (let extension of extensions) {
const { value, collector } = extension;
forEach(value, entry => {
if (!this.isValidViewsContainer(entry.value, collector)) {
return;
}
switch (entry.key) {
case 'activitybar':
this.registerCustomViewContainers(entry.value, extension.description);
break;
}
});
}
});
}
private isValidViewsContainer(viewsContainersDescriptors: IUserFriendlyViewsContainerDescriptor[], collector: ExtensionMessageCollector): boolean {
if (!Array.isArray(viewsContainersDescriptors)) {
collector.error(localize('requirearray', "views containers must be an array"));
return false;
}
for (let descriptor of viewsContainersDescriptors) {
if (typeof descriptor.id !== 'string') {
collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string`. Only alphanumeric characters, '_', and '-' are allowed.", 'id'));
return false;
}
if (!(/^[a-z0-9_-]+$/i.test(descriptor.id))) {
collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string`. Only alphanumeric characters, '_', and '-' are allowed.", 'id'));
return false;
}
if (typeof descriptor.title !== 'string') {
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'title'));
return false;
}
if (typeof descriptor.icon !== 'string') {
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'icon'));
return false;
}
}
return true;
}
private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription) {
containers.forEach((descriptor, index) => {
const cssClass = `extensionViewlet-${descriptor.id}`;
const icon = join(extension.extensionFolderPath, descriptor.icon);
this.registerCustomViewlet({ id: descriptor.id, title: descriptor.title, icon }, TEST_VIEW_CONTAINER_ORDER + index + 1, cssClass);
});
}
private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor, order: number, cssClass: string): void {
const viewletRegistry = Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets);
const id = `workbench.view.extension.${descriptor.id}`;
if (!viewletRegistry.getViewlet(id)) {
const location: ViewLocation = ViewLocation.register(id);
// Register as viewlet
class CustomViewlet extends PersistentViewsViewlet {
constructor(
@IPartService partService: IPartService,
@ITelemetryService telemetryService: ITelemetryService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@IInstantiationService instantiationService: IInstantiationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IThemeService themeService: IThemeService,
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService
) {
super(id, location, `${id}.state`, true, partService, telemetryService, storageService, instantiationService, themeService, contextService, contextKeyService, contextMenuService, extensionService);
}
}
const viewletDescriptor = new ViewletDescriptor(
CustomViewlet,
id,
descriptor.title,
cssClass,
order
);
viewletRegistry.registerViewlet(viewletDescriptor);
// Register Action to Open Viewlet
class OpenCustomViewletAction extends ToggleViewletAction {
constructor(
id: string, label: string,
@IViewletService viewletService: IViewletService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService
) {
super(id, label, id, viewletService, editorService);
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(
new SyncActionDescriptor(OpenCustomViewletAction, id, localize('showViewlet', "Show {0}", descriptor.title)),
'View: Show {0}',
localize('view', "View")
);
// Generate CSS to show the icon in the activity bar
const iconClass = `.monaco-workbench > .activitybar .monaco-action-bar .action-label.${cssClass}`;
createCSSRule(iconClass, `-webkit-mask: url('${descriptor.icon}') no-repeat 50% 50%`);
}
}
}
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(ViewsContainersExtensionHandler, LifecyclePhase.Starting);

View File

@@ -12,11 +12,10 @@ import { ViewLocation, ViewsRegistry, ICustomViewDescriptor } from 'vs/workbench
import { CustomTreeViewPanel } from 'vs/workbench/browser/parts/views/customViewPanel';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { coalesce, } from 'vs/base/common/arrays';
import { viewsContainersExtensionPoint } from 'vs/workbench/api/browser/viewsContainersExtensionPoint';
namespace schema {
// --views contribution point
export interface IUserFriendlyViewDescriptor {
id: string;
name: string;
@@ -70,20 +69,49 @@ namespace schema {
type: 'object',
properties: {
'explorer': {
description: localize('views.explorer', "Explorer View"),
description: localize('views.explorer', "Contributes views to Explorer container in the Activity bar"),
type: 'array',
items: viewDescriptor
items: viewDescriptor,
default: []
},
'debug': {
description: localize('views.debug', "Debug View"),
description: localize('views.debug', "Contributes views to Debug container in the Activity bar"),
type: 'array',
items: viewDescriptor
items: viewDescriptor,
default: []
},
'scm': {
description: localize('views.scm', "Contributes views to SCM container in the Activity bar"),
type: 'array',
items: viewDescriptor,
default: []
},
'test': {
description: localize('views.test', "Contributes views to Test container in the Activity bar"),
type: 'array',
items: viewDescriptor,
default: []
}
},
additionalProperties: {
description: localize('views.contributed', "Contributes views to contributed views container"),
type: 'array',
items: viewDescriptor,
default: []
}
};
}
ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyViewDescriptor[] }>('views', [], schema.viewsContribution)
function getViewLocation(value: string): ViewLocation {
switch (value) {
case 'explorer': return ViewLocation.Explorer;
case 'debug': return ViewLocation.Debug;
case 'scm': return ViewLocation.SCM;
default: return ViewLocation.get(`workbench.view.extension.${value}`);
}
}
ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyViewDescriptor[] }>('views', [viewsContainersExtensionPoint], schema.viewsContribution)
.setHandler((extensions) => {
for (let extension of extensions) {
const { value, collector } = extension;
@@ -93,12 +121,11 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyV
return;
}
const location = ViewLocation.getContributedViewLocation(entry.key);
let location = getViewLocation(entry.key);
if (!location) {
collector.warn(localize('locationId.invalid', "`{0}` is not a valid view location", entry.key));
return;
collector.warn(localize('ViewContainerDoesnotExist', "View container '{0}' does not exist and all views registered to it will be added to 'Explorer'.", entry.key));
location = ViewLocation.Explorer;
}
const registeredViews = ViewsRegistry.getViews(location);
const viewIds = [];
const viewDescriptors = coalesce(entry.value.map(item => {
@@ -129,4 +156,4 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyV
ViewsRegistry.registerViews(viewDescriptors);
});
}
});
});

View File

@@ -16,6 +16,7 @@ import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorEx
import { LanguageConfigurationFileHandler } from 'vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint';
// --- mainThread participants
import 'vs/workbench/api/node/apiCommands';
import './mainThreadCommands';
import './mainThreadConfiguration';
// {{SQL CARBON EDIT}}
@@ -42,6 +43,7 @@ import './mainThreadOutputService';
import './mainThreadProgress';
import './mainThreadQuickOpen';
import './mainThreadSCM';
import './mainThreadSearch';
import './mainThreadSaveParticipant';
import './mainThreadStatusBar';
import './mainThreadStorage';
@@ -51,6 +53,7 @@ import './mainThreadTerminalService';
import './mainThreadTreeViews';
import './mainThreadLogService';
import './mainThreadWebview';
import './mainThreadUrls';
import './mainThreadWindow';
import './mainThreadWorkspace';

View File

@@ -9,7 +9,7 @@
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import uri from 'vs/base/common/uri';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData } from 'vs/workbench/parts/debug/common/debug';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IAdapterExecutable, ITerminalSettings, IDebugAdapter, IDebugAdapterProvider } from 'vs/workbench/parts/debug/common/debug';
import { TPromise } from 'vs/base/common/winjs.base';
import {
ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext,
@@ -17,13 +17,20 @@ import {
} from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import severity from 'vs/base/common/severity';
import { AbstractDebugAdapter, convertToVSCPaths, convertToDAPaths } from 'vs/workbench/parts/debug/node/debugAdapter';
import * as paths from 'vs/base/common/paths';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
@extHostNamedCustomer(MainContext.MainThreadDebugService)
export class MainThreadDebugService implements MainThreadDebugServiceShape {
export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterProvider {
private _proxy: ExtHostDebugServiceShape;
private _toDispose: IDisposable[];
private _breakpointEventsActive: boolean;
private _debugAdapters: Map<number, ExtensionHostDebugAdapter>;
private _debugAdaptersHandleCounter = 1;
constructor(
extHostContext: IExtHostContext,
@@ -49,6 +56,26 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
}
}
}));
this._debugAdapters = new Map<number, ExtensionHostDebugAdapter>();
}
public $registerDebugTypes(debugTypes: string[]) {
this._toDispose.push(this.debugService.getConfigurationManager().registerDebugAdapterProvider(debugTypes, this));
}
createDebugAdapter(debugType: string, adapterInfo): IDebugAdapter {
const handle = this._debugAdaptersHandleCounter++;
const da = new ExtensionHostDebugAdapter(handle, this._proxy, debugType, adapterInfo);
this._debugAdapters.set(handle, da);
return da;
}
substituteVariables(folder: IWorkspaceFolder, config: IConfig): TPromise<IConfig> {
return this._proxy.$substituteVariables(folder.uri, config);
}
runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<void> {
return this._proxy.$runInTerminal(args, config);
}
public dispose(): void {
@@ -102,9 +129,10 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
id: l.id,
enabled: l.enabled,
lineNumber: l.line + 1,
column: l.character > 0 ? l.character + 1 : 0,
column: l.character > 0 ? l.character + 1 : undefined, // a column value of 0 results in an omitted column attribute; see #46784
condition: l.condition,
hitCondition: l.hitCondition
hitCondition: l.hitCondition,
logMessage: l.logMessage
}
);
this.debugService.addBreakpoints(uri.revive(dto.uri), rawbps);
@@ -121,7 +149,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
return void 0;
}
private convertToDto(bps: (IBreakpoint | IFunctionBreakpoint)[]): (ISourceBreakpointDto | IFunctionBreakpointDto)[] {
private convertToDto(bps: (ReadonlyArray<IBreakpoint | IFunctionBreakpoint>)): (ISourceBreakpointDto | IFunctionBreakpointDto)[] {
return bps.map(bp => {
if ('name' in bp) {
const fbp = <IFunctionBreakpoint>bp;
@@ -142,6 +170,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
enabled: sbp.enabled,
condition: sbp.condition,
hitCondition: sbp.hitCondition,
logMessage: sbp.logMessage,
uri: sbp.uri,
line: sbp.lineNumber > 0 ? sbp.lineNumber - 1 : 0,
character: (typeof sbp.column === 'number' && sbp.column > 0) ? sbp.column - 1 : 0,
@@ -209,6 +238,67 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape {
this.debugService.logToRepl(value, severity.Warning);
return TPromise.wrap<void>(undefined);
}
public $acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage) {
convertToVSCPaths(message, source => {
if (typeof source.path === 'object') {
source.path = uri.revive(source.path).toString();
}
});
this._debugAdapters.get(handle).acceptMessage(message);
}
public $acceptDAError(handle: number, name: string, message: string, stack: string) {
this._debugAdapters.get(handle).fireError(handle, new Error(`${name}: ${message}\n${stack}`));
}
public $acceptDAExit(handle: number, code: number, signal: string) {
this._debugAdapters.get(handle).fireExit(handle, code, signal);
}
}
/**
* DebugAdapter that communicates via extension protocol with another debug adapter.
*/
// {{SQL CARBON EDIT}}
/*
class ExtensionHostDebugAdapter extends AbstractDebugAdapter {
constructor(private _handle: number, private _proxy: ExtHostDebugServiceShape, private _debugType: string, private _adapterExecutable: IAdapterExecutable | null) {
super();
}
public fireError(handle: number, err: Error) {
this._onError.fire(err);
}
public fireExit(handle: number, code: number, signal: string) {
this._onExit.fire(code);
}
public startSession(): TPromise<void> {
return this._proxy.$startDASession(this._handle, this._debugType, this._adapterExecutable);
}
public sendMessage(message: DebugProtocol.ProtocolMessage): void {
convertToDAPaths(message, source => {
if (paths.isAbsolute(source.path)) {
(<any>source).path = uri.file(source.path);
} else {
(<any>source).path = uri.parse(source.path);
}
});
this._proxy.$sendDAMessage(this._handle, message);
}
public stopSession(): TPromise<void> {
return this._proxy.$stopDASession(this._handle);
}
}
// {{SQL CARBON EDIT}}
*/

View File

@@ -10,11 +10,13 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ExtHostContext, MainContext, IExtHostContext, MainThreadDecorationsShape, ExtHostDecorationsShape, DecorationData, DecorationRequest } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IDecorationsService, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations';
import { TPromise } from 'vs/base/common/winjs.base';
import { values } from 'vs/base/common/collections';
class DecorationRequestsQueue {
private _idPool = 0;
private _requests: DecorationRequest[] = [];
private _requests: { [id: number]: DecorationRequest } = Object.create(null);
private _resolver: { [id: number]: Function } = Object.create(null);
private _timer: number;
@@ -25,12 +27,15 @@ class DecorationRequestsQueue {
//
}
enqueue(handle: number, uri: URI): Thenable<DecorationData> {
return new Promise((resolve, reject) => {
const id = ++this._idPool;
this._requests.push({ id, handle, uri });
enqueue(handle: number, uri: URI): TPromise<DecorationData> {
const id = ++this._idPool;
return new TPromise((resolve, reject) => {
this._requests[id] = { id, handle, uri };
this._resolver[id] = resolve;
this._processQueue();
}, () => {
delete this._requests[id];
delete this._resolver[id];
});
}
@@ -43,7 +48,7 @@ class DecorationRequestsQueue {
// make request
const requests = this._requests;
const resolver = this._resolver;
this._proxy.$provideDecorations(requests).then(data => {
this._proxy.$provideDecorations(values(requests)).then(data => {
for (const id in resolver) {
resolver[id](data[id]);
}

View File

@@ -29,6 +29,15 @@ export class MainThreadDiagnostics implements MainThreadDiagnosticsShape {
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void {
for (let entry of entries) {
let [uri, markers] = entry;
if (markers) {
for (const marker of markers) {
if (marker.relatedInformation) {
for (const relatedInformation of marker.relatedInformation) {
relatedInformation.resource = URI.revive(relatedInformation.resource);
}
}
}
}
this._markerService.changeOne(owner, URI.revive(uri), markers);
}
this._activeOwners.add(owner);

View File

@@ -28,6 +28,7 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
$showOpenDialog(options: MainThreadDialogOpenOptions): Promise<string[]> {
// TODO@joh what about remote dev setup?
if (options.defaultUri && options.defaultUri.scheme !== 'file') {
// {{ SQL CARBON EDIT }} - Add type assertion to fix build break
return <any>Promise.reject(new Error('Not supported - Open-dialogs can only be opened on `file`-uris.'));
}
return new Promise<string[]>(resolve => {
@@ -40,6 +41,7 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
$showSaveDialog(options: MainThreadDialogSaveOptions): Promise<string> {
// TODO@joh what about remote dev setup?
if (options.defaultUri && options.defaultUri.scheme !== 'file') {
// {{ SQL CARBON EDIT }} - Add type assertion to fix build break
return <any>Promise.reject(new Error('Not supported - Save-dialogs can only be opened on `file`-uris.'));
}
return new Promise<string>(resolve => {

View File

@@ -6,7 +6,7 @@
import URI, { UriComponents } from 'vs/base/common/uri';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModelService, shouldSynchronizeModel } 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';
@@ -132,12 +132,12 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
private _shouldHandleFileEvent(e: TextFileModelChangeEvent): boolean {
const model = this._modelService.getModel(e.resource);
return model && !model.isTooLargeForHavingARichMode();
return model && shouldSynchronizeModel(model);
}
private _onModelAdded(model: ITextModel): void {
// Same filter as in mainThreadEditorsTracker
if (model.isTooLargeForHavingARichMode()) {
if (!shouldSynchronizeModel(model)) {
// don't synchronize too large models
return null;
}

View File

@@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModelService, shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
import { ITextModel } from 'vs/editor/common/model';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import Event, { Emitter } from 'vs/base/common/event';
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';
@@ -192,7 +192,7 @@ class MainThreadDocumentAndEditorStateComputer {
}
private _updateStateOnModelAdd(model: ITextModel): void {
if (model.isTooLargeForHavingARichMode()) {
if (!shouldSynchronizeModel(model)) {
// ignore
return;
}
@@ -222,7 +222,7 @@ class MainThreadDocumentAndEditorStateComputer {
// models: ignore too large models
const models = new Set<ITextModel>();
for (const model of this._modelService.getModels()) {
if (!model.isTooLargeForHavingARichMode()) {
if (shouldSynchronizeModel(model)) {
models.add(model);
}
}
@@ -233,8 +233,11 @@ class MainThreadDocumentAndEditorStateComputer {
let activeEditor: string = null;
for (const editor of this._codeEditorService.listCodeEditors()) {
if (editor.isSimpleWidget) {
continue;
}
const model = editor.getModel();
if (model && !model.isTooLargeForHavingARichMode()
if (model && shouldSynchronizeModel(model)
&& !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
) {

View File

@@ -5,7 +5,7 @@
'use strict';
import * as editorCommon from 'vs/editor/common/editorCommon';
import Event, { Emitter } from 'vs/base/common/event';
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';
@@ -185,7 +185,7 @@ export class MainThreadTextEditor {
private _codeEditorListeners: IDisposable[];
private _properties: MainThreadTextEditorProperties;
private _onPropertiesChanged: Emitter<IEditorPropertiesChangeData>;
private readonly _onPropertiesChanged: Emitter<IEditorPropertiesChangeData>;
constructor(
id: string,
@@ -322,7 +322,7 @@ export class MainThreadTextEditor {
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 creationOpts = this._modelService.getCreationOptions(this._model.getLanguageIdentifier().language, this._model.uri, this._model.isForSimpleWidget);
let insertSpaces = creationOpts.insertSpaces;
let tabSize = creationOpts.tabSize;

View File

@@ -16,7 +16,7 @@ export class MainThreadErrors implements MainThreadErrorsShape {
}
$onUnexpectedError(err: any | SerializedError): void {
if (err.$isError) {
if (err && err.$isError) {
const { name, message, stack } = err;
err = new Error();
err.message = message;

View File

@@ -4,114 +4,63 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise, PPromise } from 'vs/base/common/winjs.base';
import { ExtHostContext, MainContext, IExtHostContext, MainThreadFileSystemShape, ExtHostFileSystemShape, IFileChangeDto } from '../node/extHost.protocol';
import { IFileService, IFileSystemProvider, IStat, IFileChange } from 'vs/platform/files/common/files';
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import Event, { Emitter } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions } from 'vs/platform/files/common/files';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IProgress } from 'vs/platform/progress/common/progress';
import { ISearchResultProvider, ISearchQuery, ISearchComplete, ISearchProgressItem, QueryType, IFileMatch, ISearchService, ILineMatch } from 'vs/platform/search/common/search';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { onUnexpectedError } from 'vs/base/common/errors';
import { values } from 'vs/base/common/map';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../node/extHost.protocol';
@extHostNamedCustomer(MainContext.MainThreadFileSystem)
export class MainThreadFileSystem implements MainThreadFileSystemShape {
private readonly _proxy: ExtHostFileSystemShape;
private readonly _provider = new Map<number, RemoteFileSystemProvider>();
private readonly _fileProvider = new Map<number, RemoteFileSystemProvider>();
constructor(
extHostContext: IExtHostContext,
@IFileService private readonly _fileService: IFileService,
@ISearchService private readonly _searchService: ISearchService,
@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService
@IFileService private readonly _fileService: IFileService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystem);
}
dispose(): void {
this._provider.forEach(value => dispose());
this._provider.clear();
this._fileProvider.forEach(value => value.dispose());
this._fileProvider.clear();
}
$registerFileSystemProvider(handle: number, scheme: string): void {
this._provider.set(handle, new RemoteFileSystemProvider(this._fileService, this._searchService, scheme, handle, this._proxy));
$registerFileSystemProvider(handle: number, scheme: string, capabilities: FileSystemProviderCapabilities): void {
this._fileProvider.set(handle, new RemoteFileSystemProvider(this._fileService, scheme, capabilities, handle, this._proxy));
}
$unregisterFileSystemProvider(handle: number): void {
dispose(this._provider.get(handle));
this._provider.delete(handle);
}
$onDidAddFileSystemRoot(data: UriComponents): void {
this._workspaceEditingService.addFolders([{ uri: URI.revive(data) }], true).done(null, onUnexpectedError);
$unregisterProvider(handle: number): void {
dispose(this._fileProvider.get(handle));
this._fileProvider.delete(handle);
}
$onFileSystemChange(handle: number, changes: IFileChangeDto[]): void {
this._provider.get(handle).$onFileSystemChange(changes);
}
$reportFileChunk(handle: number, session: number, chunk: number[]): void {
this._provider.get(handle).reportFileChunk(session, chunk);
}
// --- search
$handleFindMatch(handle: number, session, data: UriComponents | [UriComponents, ILineMatch]): void {
this._provider.get(handle).handleFindMatch(session, data);
this._fileProvider.get(handle).$onFileSystemChange(changes);
}
}
class FileReadOperation {
private static _idPool = 0;
constructor(
readonly progress: IProgress<Uint8Array>,
readonly id: number = ++FileReadOperation._idPool
) {
//
}
}
class SearchOperation {
private static _idPool = 0;
constructor(
readonly progress: (match: IFileMatch) => any,
readonly id: number = ++SearchOperation._idPool,
readonly matches = new Map<string, IFileMatch>()
) {
//
}
}
class RemoteFileSystemProvider implements IFileSystemProvider, ISearchResultProvider {
class RemoteFileSystemProvider implements IFileSystemProvider {
private readonly _onDidChange = new Emitter<IFileChange[]>();
private readonly _registrations: IDisposable[];
private readonly _reads = new Map<number, FileReadOperation>();
private readonly _searches = new Map<number, SearchOperation>();
readonly onDidChange: Event<IFileChange[]> = this._onDidChange.event;
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChange.event;
readonly capabilities: FileSystemProviderCapabilities;
constructor(
fileService: IFileService,
searchService: ISearchService,
private readonly _scheme: string,
scheme: string,
capabilities: FileSystemProviderCapabilities,
private readonly _handle: number,
private readonly _proxy: ExtHostFileSystemShape
) {
this._registrations = [
fileService.registerProvider(_scheme, this),
searchService.registerSearchResultProvider(this),
];
this.capabilities = capabilities;
this._registrations = [fileService.registerProvider(scheme, this)];
}
dispose(): void {
@@ -119,6 +68,16 @@ class RemoteFileSystemProvider implements IFileSystemProvider, ISearchResultProv
this._onDidChange.dispose();
}
watch(resource: URI, opts: IWatchOptions) {
const session = Math.random();
this._proxy.$watch(this._handle, session, resource, opts);
return {
dispose: () => {
this._proxy.$unwatch(this._handle, session);
}
};
}
$onFileSystemChange(changes: IFileChangeDto[]): void {
this._onDidChange.fire(changes.map(RemoteFileSystemProvider._createFileChange));
}
@@ -129,98 +88,42 @@ class RemoteFileSystemProvider implements IFileSystemProvider, ISearchResultProv
// --- forwarding calls
utimes(resource: URI, mtime: number, atime: number): TPromise<IStat, any> {
return this._proxy.$utimes(this._handle, resource, mtime, atime);
}
stat(resource: URI): TPromise<IStat, any> {
return this._proxy.$stat(this._handle, resource);
}
read(resource: URI, offset: number, count: number, progress: IProgress<Uint8Array>): TPromise<number, any> {
const read = new FileReadOperation(progress);
this._reads.set(read.id, read);
return this._proxy.$read(this._handle, read.id, offset, count, resource).then(value => {
this._reads.delete(read.id);
return value;
stat(resource: URI): TPromise<IStat> {
return this._proxy.$stat(this._handle, resource).then(undefined, err => {
throw err;
});
}
reportFileChunk(session: number, chunk: number[]): void {
this._reads.get(session).progress.report(Buffer.from(chunk));
readFile(resource: URI): TPromise<Uint8Array, any> {
return this._proxy.$readFile(this._handle, resource).then(encoded => {
return Buffer.from(encoded, 'base64');
});
}
write(resource: URI, content: Uint8Array): TPromise<void, any> {
return this._proxy.$write(this._handle, resource, [].slice.call(content));
writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): TPromise<void, any> {
let encoded = Buffer.isBuffer(content)
? content.toString('base64')
: Buffer.from(content.buffer, content.byteOffset, content.byteLength).toString('base64');
return this._proxy.$writeFile(this._handle, resource, encoded, opts);
}
unlink(resource: URI): TPromise<void, any> {
return this._proxy.$unlink(this._handle, resource);
delete(resource: URI): TPromise<void, any> {
return this._proxy.$delete(this._handle, resource);
}
move(resource: URI, target: URI): TPromise<IStat, any> {
return this._proxy.$move(this._handle, resource, target);
}
mkdir(resource: URI): TPromise<IStat, any> {
mkdir(resource: URI): TPromise<void, any> {
return this._proxy.$mkdir(this._handle, resource);
}
readdir(resource: URI): TPromise<[URI, IStat][], any> {
return this._proxy.$readdir(this._handle, resource).then(data => {
return data.map(tuple => <[URI, IStat]>[URI.revive(tuple[0]), tuple[1]]);
});
}
rmdir(resource: URI): TPromise<void, any> {
return this._proxy.$rmdir(this._handle, resource);
readdir(resource: URI): TPromise<[string, FileType][], any> {
return this._proxy.$readdir(this._handle, resource);
}
// --- search
search(query: ISearchQuery): PPromise<ISearchComplete, ISearchProgressItem> {
if (isFalsyOrEmpty(query.folderQueries)) {
return PPromise.as(undefined);
}
let includes = { ...query.includePattern };
let excludes = { ...query.excludePattern };
for (const folderQuery of query.folderQueries) {
if (folderQuery.folder.scheme === this._scheme) {
includes = { ...includes, ...folderQuery.includePattern };
excludes = { ...excludes, ...folderQuery.excludePattern };
}
}
return new PPromise((resolve, reject, report) => {
const search = new SearchOperation(report);
this._searches.set(search.id, search);
const promise = query.type === QueryType.File
? this._proxy.$findFiles(this._handle, search.id, query.filePattern)
: this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, { excludes: Object.keys(excludes), includes: Object.keys(includes) });
promise.then(() => {
this._searches.delete(search.id);
resolve(({ results: values(search.matches), stats: undefined }));
}, err => {
this._searches.delete(search.id);
reject(err);
});
});
rename(resource: URI, target: URI, opts: FileOverwriteOptions): TPromise<void, any> {
return this._proxy.$rename(this._handle, resource, target, opts);
}
handleFindMatch(session: number, dataOrUri: UriComponents | [UriComponents, ILineMatch]): void {
let resource: URI;
let match: ILineMatch;
if (Array.isArray(dataOrUri)) {
resource = URI.revive(dataOrUri[0]);
match = dataOrUri[1];
} else {
resource = URI.revive(dataOrUri);
}
const { matches } = this._searches.get(session);
if (!matches.has(resource.toString())) {
matches.set(resource.toString(), { resource, lineMatches: [] });
}
if (match) {
matches.get(resource.toString()).lineMatches.push(match);
}
copy(resource: URI, target: URI, opts: FileOverwriteOptions): TPromise<void, any> {
return this._proxy.$copy(this._handle, resource, target, opts);
}
}

View File

@@ -8,7 +8,7 @@
import { ExtHostContext, ObjectIdentifier, IExtHostContext } from '../node/extHost.protocol';
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 { Event, Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { isThenable } from 'vs/base/common/async';
@@ -32,7 +32,7 @@ export class HeapService implements IHeapService {
_serviceBrand: any;
private _onGarbageCollection: Emitter<number[]> = new Emitter<number[]>();
private readonly _onGarbageCollection: Emitter<number[]> = new Emitter<number[]>();
public readonly onGarbageCollection: Event<number[]> = this._onGarbageCollection.event;
private _activeSignals = new WeakMap<any, object>();

View File

@@ -7,7 +7,6 @@
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 { ITextModel, ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { WorkspaceSymbolProviderRegistry, IWorkspaceSymbolProvider } from 'vs/workbench/parts/search/common/search';
@@ -15,7 +14,7 @@ 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, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, SymbolInformationDto, CodeActionDto, reviveWorkspaceEditDto } from '../node/extHost.protocol';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, SymbolInformationDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter } from '../node/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IHeapService } from './mainThreadHeapService';
@@ -97,7 +96,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- outline
$registerOutlineSupport(handle: number, selector: vscode.DocumentSelector): void {
$registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.DocumentSymbolProviderRegistry.register(toLanguageSelector(selector), <modes.DocumentSymbolProvider>{
provideDocumentSymbols: (model: ITextModel, token: CancellationToken): Thenable<modes.SymbolInformation[]> => {
return wireCancellationToken(token, this._proxy.$provideDocumentSymbols(handle, model.uri)).then(MainThreadLanguageFeatures._reviveSymbolInformationDto);
@@ -107,7 +106,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- code lens
$registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector, eventHandle: number): void {
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void {
const provider = <modes.CodeLensProvider>{
provideCodeLenses: (model: ITextModel, token: CancellationToken): modes.ICodeLensSymbol[] | Thenable<modes.ICodeLensSymbol[]> => {
@@ -136,7 +135,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- declaration
$registerDeclaractionSupport(handle: number, selector: vscode.DocumentSelector): void {
$registerDeclaractionSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.DefinitionProviderRegistry.register(toLanguageSelector(selector), <modes.DefinitionProvider>{
provideDefinition: (model, position, token): Thenable<modes.Definition> => {
return wireCancellationToken(token, this._proxy.$provideDefinition(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveLocationDto);
@@ -144,7 +143,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
});
}
$registerImplementationSupport(handle: number, selector: vscode.DocumentSelector): void {
$registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.ImplementationProviderRegistry.register(toLanguageSelector(selector), <modes.ImplementationProvider>{
provideImplementation: (model, position, token): Thenable<modes.Definition> => {
return wireCancellationToken(token, this._proxy.$provideImplementation(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveLocationDto);
@@ -152,7 +151,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
});
}
$registerTypeDefinitionSupport(handle: number, selector: vscode.DocumentSelector): void {
$registerTypeDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.TypeDefinitionProviderRegistry.register(toLanguageSelector(selector), <modes.TypeDefinitionProvider>{
provideTypeDefinition: (model, position, token): Thenable<modes.Definition> => {
return wireCancellationToken(token, this._proxy.$provideTypeDefinition(handle, model.uri, position)).then(MainThreadLanguageFeatures._reviveLocationDto);
@@ -162,7 +161,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- extra info
$registerHoverProvider(handle: number, selector: vscode.DocumentSelector): void {
$registerHoverProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.HoverProviderRegistry.register(toLanguageSelector(selector), <modes.HoverProvider>{
provideHover: (model: ITextModel, position: EditorPosition, token: CancellationToken): Thenable<modes.Hover> => {
return wireCancellationToken(token, this._proxy.$provideHover(handle, model.uri, position));
@@ -172,7 +171,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- occurrences
$registerDocumentHighlightProvider(handle: number, selector: vscode.DocumentSelector): void {
$registerDocumentHighlightProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.DocumentHighlightProviderRegistry.register(toLanguageSelector(selector), <modes.DocumentHighlightProvider>{
provideDocumentHighlights: (model: ITextModel, position: EditorPosition, token: CancellationToken): Thenable<modes.DocumentHighlight[]> => {
return wireCancellationToken(token, this._proxy.$provideDocumentHighlights(handle, model.uri, position));
@@ -182,7 +181,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- references
$registerReferenceSupport(handle: number, selector: vscode.DocumentSelector): void {
$registerReferenceSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.ReferenceProviderRegistry.register(toLanguageSelector(selector), <modes.ReferenceProvider>{
provideReferences: (model: ITextModel, position: EditorPosition, context: modes.ReferenceContext, token: CancellationToken): Thenable<modes.Location[]> => {
return wireCancellationToken(token, this._proxy.$provideReferences(handle, model.uri, position, context)).then(MainThreadLanguageFeatures._reviveLocationDto);
@@ -192,17 +191,18 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- quick fix
$registerQuickFixSupport(handle: number, selector: vscode.DocumentSelector): void {
$registerQuickFixSupport(handle: number, selector: ISerializedDocumentFilter[], providedCodeActionKinds?: string[]): void {
this._registrations[handle] = modes.CodeActionProviderRegistry.register(toLanguageSelector(selector), <modes.CodeActionProvider>{
provideCodeActions: (model: ITextModel, range: EditorRange, context: modes.CodeActionContext, token: CancellationToken): Thenable<modes.CodeAction[]> => {
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideCodeActions(handle, model.uri, range, context))).then(MainThreadLanguageFeatures._reviveCodeActionDto);
}
},
providedCodeActionKinds
});
}
// --- formatting
$registerDocumentFormattingSupport(handle: number, selector: vscode.DocumentSelector): void {
$registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.DocumentFormattingEditProviderRegistry.register(toLanguageSelector(selector), <modes.DocumentFormattingEditProvider>{
provideDocumentFormattingEdits: (model: ITextModel, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
return wireCancellationToken(token, this._proxy.$provideDocumentFormattingEdits(handle, model.uri, options));
@@ -210,7 +210,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
});
}
$registerRangeFormattingSupport(handle: number, selector: vscode.DocumentSelector): void {
$registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.DocumentRangeFormattingEditProviderRegistry.register(toLanguageSelector(selector), <modes.DocumentRangeFormattingEditProvider>{
provideDocumentRangeFormattingEdits: (model: ITextModel, range: EditorRange, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> => {
return wireCancellationToken(token, this._proxy.$provideDocumentRangeFormattingEdits(handle, model.uri, range, options));
@@ -218,7 +218,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
});
}
$registerOnTypeFormattingSupport(handle: number, selector: vscode.DocumentSelector, autoFormatTriggerCharacters: string[]): void {
$registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[]): void {
this._registrations[handle] = modes.OnTypeFormattingEditProviderRegistry.register(toLanguageSelector(selector), <modes.OnTypeFormattingEditProvider>{
autoFormatTriggerCharacters,
@@ -251,20 +251,21 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- rename
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector, supportsResolveInitialValues: boolean): void {
$registerRenameSupport(handle: number, selector: ISerializedDocumentFilter[], supportResolveLocation: boolean): void {
this._registrations[handle] = modes.RenameProviderRegistry.register(toLanguageSelector(selector), <modes.RenameProvider>{
provideRenameEdits: (model: ITextModel, position: EditorPosition, newName: string, token: CancellationToken): Thenable<modes.WorkspaceEdit> => {
return wireCancellationToken(token, this._proxy.$provideRenameEdits(handle, model.uri, position, newName)).then(reviveWorkspaceEditDto);
},
resolveInitialRenameValue: supportsResolveInitialValues
? (model: ITextModel, position: EditorPosition, token: CancellationToken): Thenable<modes.RenameInitialValue> => wireCancellationToken(token, this._proxy.$resolveInitialRenameValue(handle, model.uri, position))
resolveRenameLocation: supportResolveLocation
? (model: ITextModel, position: EditorPosition, token: CancellationToken): Thenable<modes.RenameLocation> => wireCancellationToken(token, this._proxy.$resolveRenameLocation(handle, model.uri, position))
: undefined
});
}
// --- suggest
$registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[], supportsResolveDetails: boolean): void {
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean): void {
this._registrations[handle] = modes.SuggestRegistry.register(toLanguageSelector(selector), <modes.ISuggestSupport>{
triggerCharacters,
provideCompletionItems: (model: ITextModel, position: EditorPosition, context: modes.SuggestContext, token: CancellationToken): Thenable<modes.ISuggestResult> => {
@@ -287,7 +288,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- parameter hints
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): void {
$registerSignatureHelpProvider(handle: number, selector: ISerializedDocumentFilter[], triggerCharacter: string[]): void {
this._registrations[handle] = modes.SignatureHelpProviderRegistry.register(toLanguageSelector(selector), <modes.SignatureHelpProvider>{
signatureHelpTriggerCharacters: triggerCharacter,
@@ -301,7 +302,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- links
$registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): void {
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.LinkProviderRegistry.register(toLanguageSelector(selector), <modes.LinkProvider>{
provideLinks: (model, token) => {
return this._heapService.trackRecursive(wireCancellationToken(token, this._proxy.$provideDocumentLinks(handle, model.uri)));
@@ -314,7 +315,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- colors
$registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): void {
$registerDocumentColorProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
const proxy = this._proxy;
this._registrations[handle] = modes.ColorProviderRegistry.register(toLanguageSelector(selector), <modes.DocumentColorProvider>{
provideDocumentColors: (model, token) => {
@@ -348,11 +349,11 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- folding
$registerFoldingProvider(handle: number, selector: vscode.DocumentSelector): void {
$registerFoldingRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
const proxy = this._proxy;
this._registrations[handle] = modes.FoldingProviderRegistry.register(toLanguageSelector(selector), <modes.FoldingProvider>{
provideFoldingRanges: (model, token) => {
return wireCancellationToken(token, proxy.$provideFoldingRanges(handle, model.uri));
this._registrations[handle] = modes.FoldingRangeProviderRegistry.register(toLanguageSelector(selector), <modes.FoldingRangeProvider>{
provideFoldingRanges: (model, context, token) => {
return wireCancellationToken(token, proxy.$provideFoldingRanges(handle, model.uri, context));
}
});
}

View File

@@ -4,18 +4,18 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import Severity from 'vs/base/common/severity';
import { Action, IAction } from 'vs/base/common/actions';
import { MainThreadMessageServiceShape, MainContext, IExtHostContext, MainThreadMessageOptions } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { once } from 'vs/base/common/event';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { localize } from 'vs/nls';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { dispose } from 'vs/base/common/lifecycle';
@extHostNamedCustomer(MainContext.MainThreadMessageService)
export class MainThreadMessageService implements MainThreadMessageServiceShape {
@@ -24,7 +24,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
extHostContext: IExtHostContext,
@INotificationService private readonly _notificationService: INotificationService,
@ICommandService private readonly _commandService: ICommandService,
@IChoiceService private readonly _choiceService: IChoiceService,
@IDialogService private readonly _dialogService: IDialogService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService
) {
//
@@ -71,11 +71,11 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
let source: string;
if (extension) {
source = localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
source = nls.localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
}
if (!source) {
source = localize('defaultSource', "Extension");
source = nls.localize('defaultSource', "Extension");
}
const secondaryActions: IAction[] = [];
@@ -93,6 +93,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
// if promise has not been resolved yet, now is the time to ensure a return value
// otherwise if already resolved it means the user clicked one of the buttons
once(messageHandle.onDidClose)(() => {
dispose(...primaryActions, ...secondaryActions);
resolve(undefined);
});
});
@@ -101,7 +102,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
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) => {
const buttons = commands.map((command, index) => {
if (command.isCloseAffordance === true) {
cancelId = index;
}
@@ -110,16 +111,16 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
});
if (cancelId === void 0) {
if (options.length > 0) {
options.push(nls.localize('cancel', "Cancel"));
if (buttons.length > 0) {
buttons.push(nls.localize('cancel', "Cancel"));
} else {
options.push(nls.localize('ok', "OK"));
buttons.push(nls.localize('ok', "OK"));
}
cancelId = options.length - 1;
cancelId = buttons.length - 1;
}
return this._choiceService.choose(severity, message, options, cancelId, true)
return this._dialogService.show(severity, message, buttons, { cancelId })
.then(result => result === commands.length ? undefined : commands[result].handle);
}
}

View File

@@ -5,7 +5,7 @@
'use strict';
import { IProgressService2, IProgress, IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
import { MainThreadProgressShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { MainThreadProgressShape, MainContext, IExtHostContext, ExtHostProgressShape, ExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadProgress)
@@ -13,11 +13,13 @@ export class MainThreadProgress implements MainThreadProgressShape {
private _progressService: IProgressService2;
private _progress = new Map<number, { resolve: Function, progress: IProgress<IProgressStep> }>();
private _proxy: ExtHostProgressShape;
constructor(
extHostContext: IExtHostContext,
@IProgressService2 progressService: IProgressService2
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostProgress);
this._progressService = progressService;
}
@@ -28,7 +30,8 @@ export class MainThreadProgress implements MainThreadProgressShape {
$startProgress(handle: number, options: IProgressOptions): void {
const task = this._createTask(handle);
this._progressService.withProgress(options, task);
this._progressService.withProgress(options, task, () => this._proxy.$acceptProgressCanceled(handle));
}
$progressReport(handle: number, message: IProgressStep): void {

View File

@@ -7,6 +7,7 @@
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 { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { InputBoxOptions } from 'vscode';
import { ExtHostContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@@ -16,6 +17,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
private _proxy: ExtHostQuickOpenShape;
private _quickOpenService: IQuickOpenService;
private _quickInputService: IQuickInputService;
private _doSetItems: (items: MyQuickPickItems[]) => any;
private _doSetError: (error: Error) => any;
private _contents: TPromise<MyQuickPickItems[]>;
@@ -23,16 +25,18 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
constructor(
extHostContext: IExtHostContext,
@IQuickOpenService quickOpenService: IQuickOpenService
@IQuickOpenService quickOpenService: IQuickOpenService,
@IQuickInputService quickInputService: IQuickInputService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostQuickOpen);
this._quickOpenService = quickOpenService;
this._quickInputService = quickInputService;
}
public dispose(): void {
}
$show(options: IPickOptions): TPromise<number> {
$show(options: IPickOptions): TPromise<number | number[]> {
const myToken = ++this._token;
@@ -50,16 +54,29 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
};
});
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);
}
});
if (options.canSelectMany) {
return asWinJsPromise(token => this._quickInputService.pick(this._contents, options, token)).then(items => {
if (items) {
return items.map(item => item.handle);
}
return undefined;
}, undefined, progress => {
if (progress) {
this._proxy.$onItemSelected((<MyQuickPickItems>progress).handle);
}
});
} else {
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> {
@@ -97,6 +114,6 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
};
}
return asWinJsPromise(token => this._quickOpenService.input(inputOptions, token));
return asWinJsPromise(token => this._quickInputService.input(inputOptions, token));
}
}

View File

@@ -7,7 +7,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import URI, { UriComponents } from 'vs/base/common/uri';
import Event, { Emitter } from 'vs/base/common/event';
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, IInputValidation } from 'vs/workbench/services/scm/common/scm';

View File

@@ -28,6 +28,16 @@ import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common
import { localize } from 'vs/nls';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IFileService } from 'vs/platform/files/common/files';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
import { CodeAction } from 'vs/editor/common/modes';
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { ICodeActionsOnSaveOptions } from 'vs/editor/common/config/editorOptions';
export interface ISaveParticipantParticipant extends ISaveParticipant {
// progressMessage: string;
@@ -49,7 +59,7 @@ class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
}
private doTrimTrailingWhitespace(model: ITextModel, isAutoSaved: boolean): void {
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
let prevSelection: Selection[] = [];
const cursors: Position[] = [];
let editor = findEditor(model, this.codeEditorService);
@@ -59,6 +69,12 @@ class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
prevSelection = editor.getSelections();
if (isAutoSaved) {
cursors.push(...prevSelection.map(s => new Position(s.positionLineNumber, s.positionColumn)));
const snippetsRange = SnippetController2.get(editor).getSessionEnclosingRange();
if (snippetsRange) {
for (let lineNumber = snippetsRange.startLineNumber; lineNumber <= snippetsRange.endLineNumber; lineNumber++) {
cursors.push(new Position(lineNumber, model.getLineMaxColumn(lineNumber)));
}
}
}
}
@@ -113,7 +129,7 @@ export class FinalNewLineParticipant implements ISaveParticipantParticipant {
return;
}
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
let prevSelection: Selection[] = [];
const editor = findEditor(model, this.codeEditorService);
if (editor) {
prevSelection = editor.getSelections();
@@ -150,7 +166,7 @@ export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant
return;
}
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
let prevSelection: Selection[] = [];
const editor = findEditor(model, this.codeEditorService);
if (editor) {
prevSelection = editor.getSelections();
@@ -165,7 +181,7 @@ export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant
currentLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(currentLine) === -1;
}
const deletionRange = new Range(currentLineNumber + 1, 1, lineCount + 1, 1);
const deletionRange = model.validateRange(new Range(currentLineNumber + 1, 1, lineCount + 1, 1));
if (!deletionRange.isEmpty()) {
model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], edits => prevSelection);
}
@@ -197,8 +213,10 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
const versionNow = model.getVersionId();
const { tabSize, insertSpaces } = model.getOptions();
const timeout = this._configurationService.getValue('editor.formatOnSaveTimeout', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() });
return new Promise<ISingleEditOperation[]>((resolve, reject) => {
setTimeout(reject, 750);
setTimeout(() => reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout)), timeout);
getDocumentFormattingEdits(model, { tabSize, insertSpaces })
.then(edits => this._editorWorkerService.computeMoreMinimalEdits(model.uri, edits))
.then(resolve, err => {
@@ -222,7 +240,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
}
private _editsWithEditor(editor: ICodeEditor, edits: ISingleEditOperation[]): void {
EditOperationsCommand.execute(editor, edits, false);
EditOperationsCommand.execute(editor, edits);
}
private _editWithModel(model: ITextModel, edits: ISingleEditOperation[]): void {
@@ -249,6 +267,59 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
}
}
class CodeActionOnParticipant implements ISaveParticipant {
constructor(
@ITextModelService private readonly _textModelService: ITextModelService,
@IFileService private readonly _fileService: IFileService,
@ICommandService private readonly _commandService: ICommandService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
@IConfigurationService private readonly _configurationService: IConfigurationService
) { }
async participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
if (env.reason === SaveReason.AUTO) {
return undefined;
}
const model = editorModel.textEditorModel;
const editor = findEditor(model, this._codeEditorService);
if (!editor) {
return undefined;
}
const settingsOverrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() };
const setting = this._configurationService.getValue<ICodeActionsOnSaveOptions>('editor.codeActionsOnSave', settingsOverrides);
if (!setting) {
return undefined;
}
const codeActionsOnSave = Object.keys(setting).filter(x => setting[x]).map(x => new CodeActionKind(x));
if (!codeActionsOnSave.length) {
return undefined;
}
const timeout = this._configurationService.getValue<number>('editor.codeActionsOnSaveTimeout', settingsOverrides);
return new Promise<CodeAction[]>((resolve, reject) => {
setTimeout(() => reject(localize('codeActionsOnSave.didTimeout', "Aborted codeActionsOnSave after {0}ms", timeout)), timeout);
this.getActionsToRun(model, codeActionsOnSave).then(resolve);
}).then(actionsToRun => this.applyCodeActions(actionsToRun, editor));
}
private async applyCodeActions(actionsToRun: CodeAction[], editor: ICodeEditor) {
for (const action of actionsToRun) {
await applyCodeAction(action, this._textModelService, this._fileService, this._commandService, editor);
}
}
private async getActionsToRun(model: ITextModel, codeActionsOnSave: CodeActionKind[]) {
const actions = await getCodeActions(model, model.getFullModelRange(), { kind: CodeActionKind.Source, includeSourceActions: true });
const actionsToRun = actions.filter(returnedAction => returnedAction.kind && codeActionsOnSave.some(onSaveKind => onSaveKind.contains(returnedAction.kind)));
return actionsToRun;
}
}
class ExtHostSaveParticipant implements ISaveParticipantParticipant {
private _proxy: ExtHostDocumentSaveParticipantShape;
@@ -259,14 +330,14 @@ class ExtHostSaveParticipant implements ISaveParticipantParticipant {
participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
if (editorModel.textEditorModel.isTooLargeForHavingARichMode()) {
if (!shouldSynchronizeModel(editorModel.textEditorModel)) {
// the model never made it to the extension
// host meaning we cannot participate in its save
return undefined;
}
return new Promise<any>((resolve, reject) => {
setTimeout(reject, 1750);
setTimeout(() => reject(localize('timeout.onWillSave', "Aborted onWillSaveTextDocument-event after 1750ms")), 1750);
this._proxy.$participateInSave(editorModel.getResource(), env.reason).then(values => {
for (const success of values) {
if (!success) {
@@ -293,6 +364,7 @@ export class SaveParticipant implements ISaveParticipant {
) {
this._saveParticipants = [
instantiationService.createInstance(TrimWhitespaceParticipant),
instantiationService.createInstance(CodeActionOnParticipant),
instantiationService.createInstance(FormatOnSaveParticipant),
instantiationService.createInstance(FinalNewLineParticipant),
instantiationService.createInstance(TrimFinalNewLinesParticipant),
@@ -312,7 +384,7 @@ export class SaveParticipant implements ISaveParticipant {
const promiseFactory = this._saveParticipants.map(p => () => {
return Promise.resolve(p.participate(model, env));
});
return sequence(promiseFactory).then(() => { }, err => this._logService.error(err));
return sequence(promiseFactory).then(() => { }, err => this._logService.warn(err));
});
}
}

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 { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { values } from 'vs/base/common/map';
import URI, { UriComponents } from 'vs/base/common/uri';
import { PPromise, TPromise } from 'vs/base/common/winjs.base';
import { IFileMatch, ILineMatch, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, QueryType } from 'vs/platform/search/common/search';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { ExtHostContext, ExtHostSearchShape, IExtHostContext, MainContext, MainThreadSearchShape } from '../node/extHost.protocol';
@extHostNamedCustomer(MainContext.MainThreadSearch)
export class MainThreadSearch implements MainThreadSearchShape {
private readonly _proxy: ExtHostSearchShape;
private readonly _searchProvider = new Map<number, RemoteSearchProvider>();
constructor(
extHostContext: IExtHostContext,
@ISearchService private readonly _searchService: ISearchService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostSearch);
}
dispose(): void {
this._searchProvider.forEach(value => dispose());
this._searchProvider.clear();
}
$registerSearchProvider(handle: number, scheme: string): void {
this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, scheme, handle, this._proxy));
}
$unregisterProvider(handle: number): void {
dispose(this._searchProvider.get(handle));
this._searchProvider.delete(handle);
}
$handleFindMatch(handle: number, session, data: UriComponents | [UriComponents, ILineMatch]): void {
this._searchProvider.get(handle).handleFindMatch(session, data);
}
}
class SearchOperation {
private static _idPool = 0;
constructor(
readonly progress: (match: IFileMatch) => any,
readonly id: number = ++SearchOperation._idPool,
readonly matches = new Map<string, IFileMatch>()
) {
//
}
addMatch(resource: URI, match: ILineMatch): void {
if (!this.matches.has(resource.toString())) {
this.matches.set(resource.toString(), { resource, lineMatches: [] });
}
if (match) {
this.matches.get(resource.toString()).lineMatches.push(match);
}
this.progress(this.matches.get(resource.toString()));
}
}
class RemoteSearchProvider implements ISearchResultProvider {
private readonly _registrations: IDisposable[];
private readonly _searches = new Map<number, SearchOperation>();
constructor(
searchService: ISearchService,
private readonly _scheme: string,
private readonly _handle: number,
private readonly _proxy: ExtHostSearchShape
) {
this._registrations = [searchService.registerSearchResultProvider(this)];
}
dispose(): void {
dispose(this._registrations);
}
search(query: ISearchQuery): PPromise<ISearchComplete, ISearchProgressItem> {
if (isFalsyOrEmpty(query.folderQueries)) {
return PPromise.as(undefined);
}
let includes = { ...query.includePattern };
let excludes = { ...query.excludePattern };
for (const folderQuery of query.folderQueries) {
if (folderQuery.folder.scheme === this._scheme) {
includes = { ...includes, ...folderQuery.includePattern };
excludes = { ...excludes, ...folderQuery.excludePattern };
}
}
let outer: TPromise;
return new PPromise((resolve, reject, report) => {
const search = new SearchOperation(report);
this._searches.set(search.id, search);
outer = query.type === QueryType.File
? this._proxy.$provideFileSearchResults(this._handle, search.id, query.filePattern)
: this._proxy.$provideTextSearchResults(this._handle, search.id, query.contentPattern, { excludes: Object.keys(excludes), includes: Object.keys(includes) });
outer.then(() => {
this._searches.delete(search.id);
resolve(({ results: values(search.matches), stats: undefined }));
}, err => {
this._searches.delete(search.id);
reject(err);
});
}, () => {
if (outer) {
outer.cancel();
}
});
}
handleFindMatch(session: number, dataOrUri: UriComponents | [UriComponents, ILineMatch]): void {
if (!this._searches.has(session)) {
// ignore...
return;
}
let resource: URI;
let match: ILineMatch;
if (Array.isArray(dataOrUri)) {
resource = URI.revive(dataOrUri[0]);
match = dataOrUri[1];
} else {
resource = URI.revive(dataOrUri);
}
this._searches.get(session).addMatch(resource, match);
}
}

View File

@@ -4,16 +4,337 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import * as crypto from 'crypto';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import * as nls from 'vs/nls';
import { ContributedTask, ExtensionTaskSourceTransfer } from 'vs/workbench/parts/tasks/common/tasks';
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';
import URI from 'vs/base/common/uri';
import * as Objects from 'vs/base/common/objects';
import { TPromise } from 'vs/base/common/winjs.base';
import * as Types from 'vs/base/common/types';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import {
ContributedTask, ExtensionTaskSourceTransfer, TaskIdentifier, TaskExecution, Task, TaskEvent, TaskEventKind,
PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, TaskSourceKind, ExtensionTaskSource, RevealKind, PanelKind
} from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskService, TaskFilter } from 'vs/workbench/parts/tasks/common/taskService';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext, IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import {
TaskDefinitionDTO, TaskExecutionDTO, ProcessExecutionOptionsDTO, TaskPresentationOptionsDTO,
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO
} from 'vs/workbench/api/shared/tasks';
export { TaskDTO, TaskHandleDTO, TaskExecutionDTO, TaskFilterDTO };
namespace TaskExecutionDTO {
export function from(value: TaskExecution): TaskExecutionDTO {
return {
id: value.id,
task: TaskDTO.from(value.task)
};
}
export function to(value: TaskExecutionDTO, workspace: IWorkspaceContextService): TaskExecution {
return {
id: value.id,
task: TaskDTO.to(value.task, workspace)
};
}
}
namespace TaskDefinitionDTO {
export function from(value: TaskIdentifier): TaskDefinitionDTO {
let result = Objects.assign(Object.create(null), value);
delete result._key;
return result;
}
export function to(value: TaskDefinitionDTO): TaskIdentifier {
const hash = crypto.createHash('md5');
hash.update(JSON.stringify(value));
let result = Objects.assign(Object.create(null), value);
result._key = hash.digest('hex');
return result;
}
}
namespace TaskPresentationOptionsDTO {
export function from(value: PresentationOptions): TaskPresentationOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
return Objects.assign(Object.create(null), value);
}
export function to(value: TaskPresentationOptionsDTO): PresentationOptions {
if (value === void 0 || value === null) {
return undefined;
}
return Objects.assign(Object.create(null), value);
}
}
namespace ProcessExecutionOptionsDTO {
export function from(value: CommandOptions): ProcessExecutionOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
return {
cwd: value.cwd,
env: value.env
};
}
export function to(value: ProcessExecutionOptionsDTO): CommandOptions {
if (value === void 0 || value === null) {
return undefined;
}
return {
cwd: value.cwd,
env: value.env
};
}
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ProcessExecutionDTO {
let candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
}
export function from(value: CommandConfiguration): ProcessExecutionDTO {
let process: string = Types.isString(value.name) ? value.name : value.name.value;
let args: string[] = value.args ? value.args.map(value => Types.isString(value) ? value : value.value) : [];
let result: ProcessExecutionDTO = {
process: process,
args: args
};
if (value.options) {
result.options = ProcessExecutionOptionsDTO.from(value.options);
}
return result;
}
export function to(value: ProcessExecutionDTO): CommandConfiguration {
let result: CommandConfiguration = {
runtime: RuntimeType.Process,
name: value.process,
args: value.args,
presentation: undefined
};
if (value.options) {
result.options = ProcessExecutionOptionsDTO.to(value.options);
}
return result;
}
}
namespace ShellExecutionOptionsDTO {
export function from(value: CommandOptions): ShellExecutionOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
let result: ShellExecutionOptionsDTO = {
cwd: value.cwd,
env: value.env
};
if (value.shell) {
result.executable = value.shell.executable;
result.shellArgs = value.shell.args;
result.shellQuoting = value.shell.quoting;
}
return result;
}
export function to(value: ShellExecutionOptionsDTO): CommandOptions {
if (value === void 0 || value === null) {
return undefined;
}
let result: CommandOptions = {
cwd: value.cwd,
env: value.env
};
if (value.executable) {
result.shell = {
executable: value.executable
};
if (value.shellArgs) {
result.shell.args = value.shellArgs;
}
if (value.shellQuoting) {
result.shell.quoting = value.shellQuoting;
}
}
return result;
}
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ShellExecutionDTO {
let candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
}
export function from(value: CommandConfiguration): ShellExecutionDTO {
let result: ShellExecutionDTO = {};
if (value.name && Types.isString(value.name) && (value.args === void 0 || value.args === null || value.args.length === 0)) {
result.commandLine = value.name;
} else {
result.command = value.name;
result.args = value.args;
}
if (value.options) {
result.options = ShellExecutionOptionsDTO.from(value.options);
}
return result;
}
export function to(value: ShellExecutionDTO): CommandConfiguration {
let result: CommandConfiguration = {
runtime: RuntimeType.Shell,
name: value.commandLine ? value.commandLine : value.command,
args: value.args,
presentation: undefined
};
if (value.options) {
result.options = ShellExecutionOptionsDTO.to(value.options);
}
return result;
}
}
namespace TaskSourceDTO {
export function from(value: TaskSource): TaskSourceDTO {
let result: TaskSourceDTO = {
label: value.label
};
if (value.kind === TaskSourceKind.Extension) {
result.extensionId = value.extension;
if (value.workspaceFolder) {
result.scope = value.workspaceFolder.uri;
} else {
result.scope = value.scope;
}
} else if (value.kind === TaskSourceKind.Workspace) {
result.extensionId = '$core';
result.scope = value.config.workspaceFolder.uri;
}
return result;
}
export function to(value: TaskSourceDTO, workspace: IWorkspaceContextService): ExtensionTaskSource {
let scope: TaskScope;
let workspaceFolder: IWorkspaceFolder;
if (value.scope === void 0) {
if (workspace.getWorkspace().folders.length === 0) {
scope = TaskScope.Global;
workspaceFolder = undefined;
} else {
scope = TaskScope.Folder;
workspaceFolder = workspace.getWorkspace().folders[0];
}
} else if (typeof value.scope === 'number') {
scope = value.scope;
} else {
scope = TaskScope.Folder;
workspaceFolder = workspace.getWorkspaceFolder(URI.revive(value.scope));
}
let result: ExtensionTaskSource = {
kind: TaskSourceKind.Extension,
label: value.label,
extension: value.extensionId,
scope,
workspaceFolder
};
return result;
}
}
namespace TaskHandleDTO {
export function is(value: any): value is TaskHandleDTO {
let candidate: TaskHandleDTO = value;
return candidate && Types.isString(candidate.id) && !!candidate.workspaceFolder;
}
}
namespace TaskDTO {
export function from(task: Task): TaskDTO {
if (task === void 0 || task === null || (!CustomTask.is(task) && !ContributedTask.is(task))) {
return undefined;
}
let result: TaskDTO = {
_id: task._id,
name: task.name,
definition: TaskDefinitionDTO.from(Task.getTaskDefinition(task)),
source: TaskSourceDTO.from(task._source),
execution: undefined,
presentationOptions: task.command ? TaskPresentationOptionsDTO.from(task.command.presentation) : undefined,
isBackground: task.isBackground,
problemMatchers: [],
hasDefinedMatchers: ContributedTask.is(task) ? task.hasDefinedMatchers : false
};
if (task.group) {
result.group = task.group;
}
if (task.command) {
if (task.command.runtime === RuntimeType.Process) {
result.execution = ProcessExecutionDTO.from(task.command);
} else if (task.command.runtime === RuntimeType.Shell) {
result.execution = ShellExecutionDTO.from(task.command);
}
}
if (task.problemMatchers) {
for (let matcher of task.problemMatchers) {
if (Types.isString(matcher)) {
result.problemMatchers.push(matcher);
}
}
}
return result;
}
export function to(task: TaskDTO, workspace: IWorkspaceContextService): Task {
if (typeof task.name !== 'string') {
return undefined;
}
let command: CommandConfiguration;
if (ShellExecutionDTO.is(task.execution)) {
command = ShellExecutionDTO.to(task.execution);
} else if (ProcessExecutionDTO.is(task.execution)) {
command = ProcessExecutionDTO.to(task.execution);
}
if (!command) {
return undefined;
}
command.presentation = TaskPresentationOptionsDTO.to(task.presentationOptions);
command.presentation = Objects.assign(command.presentation || {}, { echo: true, reveal: RevealKind.Always, focus: false, panel: PanelKind.Shared });
let source = TaskSourceDTO.to(task.source, workspace);
let label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
let definition = TaskDefinitionDTO.to(task.definition);
let id = `${task.source.extensionId}.${definition._key}`;
let result: ContributedTask = {
_id: id, // uuidMap.getUUID(identifier),
_source: source,
_label: label,
type: definition.type,
defines: definition,
name: task.name,
identifier: label,
group: task.group,
command: command,
isBackground: !!task.isBackground,
problemMatchers: task.problemMatchers.slice(),
hasDefinedMatchers: task.hasDefinedMatchers
};
return result;
}
}
namespace TaskFilterDTO {
export function from(value: TaskFilter): TaskFilterDTO {
return value;
}
export function to(value: TaskFilterDTO): TaskFilter {
return value;
}
}
@extHostNamedCustomer(MainContext.MainThreadTask)
export class MainThreadTask implements MainThreadTaskShape {
@@ -28,6 +349,14 @@ export class MainThreadTask implements MainThreadTaskShape {
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTask);
this._activeHandles = Object.create(null);
this._taskService.onDidStateChange((event: TaskEvent) => {
let task = event.__task;
if (event.kind === TaskEventKind.Start) {
this._proxy.$taskStarted(TaskExecutionDTO.from(Task.getTaskExecution(task)));
} else if (event.kind === TaskEventKind.End) {
this._proxy.$taskEnded(TaskExecutionDTO.from(Task.getTaskExecution(task)));
}
});
}
public dispose(): void {
@@ -63,4 +392,61 @@ export class MainThreadTask implements MainThreadTaskShape {
delete this._activeHandles[handle];
return TPromise.wrap<void>(undefined);
}
public $fetchTasks(filter?: TaskFilterDTO): TPromise<TaskDTO[]> {
return this._taskService.tasks(TaskFilterDTO.to(filter)).then((tasks) => {
let result: TaskDTO[] = [];
for (let task of tasks) {
let item = TaskDTO.from(task);
if (item) {
result.push(item);
}
}
return result;
});
}
public $executeTask(value: TaskHandleDTO | TaskDTO): TPromise<TaskExecutionDTO> {
return new TPromise<TaskExecutionDTO>((resolve, reject) => {
if (TaskHandleDTO.is(value)) {
let workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task) => {
this._taskService.run(task);
let result: TaskExecutionDTO = {
id: value.id,
task: TaskDTO.from(task)
};
resolve(result);
}, (error) => {
reject(new Error('Task not found'));
});
} else {
let task = TaskDTO.to(value, this._workspaceContextServer);
this._taskService.run(task);
let result: TaskExecutionDTO = {
id: task._id,
task: TaskDTO.from(task)
};
resolve(result);
}
});
}
public $terminateTask(id: string): TPromise<void> {
return new TPromise<void>((resolve, reject) => {
this._taskService.getActiveTasks().then((tasks) => {
for (let task of tasks) {
if (id === task._id) {
this._taskService.terminate(task).then((value) => {
resolve(undefined);
}, (error) => {
reject(undefined);
});
return;
}
}
reject(new Error('Task to terminate not found'));
});
});
}
}

View File

@@ -25,7 +25,7 @@ export class MainThreadTelemetry implements MainThreadTelemetryShape {
}
$publicLog(eventName: string, data: any = Object.create(null)): void {
// __GDPR__COMMON__ "pluginHostTelemetry" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
// __GDPR__COMMON__ "pluginHostTelemetry" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
data[MainThreadTelemetry._name] = true;
this._telemetryService.publicLog(eventName, data);
}

View File

@@ -5,25 +5,39 @@
'use strict';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/parts/terminal/common/terminal';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest } 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 { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } 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[];
private _toDispose: IDisposable[] = [];
private _terminalProcesses: { [id: number]: ITerminalProcessExtHostProxy } = {};
private _dataListeners: { [id: number]: IDisposable } = {};
constructor(
extHostContext: IExtHostContext,
@ITerminalService private terminalService: ITerminalService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
this._toDispose = [];
this._toDispose.push(terminalService.onInstanceDisposed((terminalInstance) => this._onTerminalDisposed(terminalInstance)));
this._toDispose.push(terminalService.onInstanceProcessIdReady((terminalInstance) => this._onTerminalProcessIdReady(terminalInstance)));
this._toDispose.push(terminalService.onInstanceCreated((terminalInstance) => {
// Delay this message so the TerminalInstance constructor has a chance to finish and
// return the ID normally to the extension host. The ID that is passed here will be used
// to register non-extension API terminals in the extension host.
setTimeout(() => this._onTerminalOpened(terminalInstance), 100);
}));
this._toDispose.push(terminalService.onInstanceDisposed(terminalInstance => this._onTerminalDisposed(terminalInstance)));
this._toDispose.push(terminalService.onInstanceProcessIdReady(terminalInstance => this._onTerminalProcessIdReady(terminalInstance)));
this._toDispose.push(terminalService.onInstanceRequestExtHostProcess(request => this._onTerminalRequestExtHostProcess(request)));
// Set initial ext host state
this.terminalService.terminalInstances.forEach(t => {
this._onTerminalOpened(t);
t.processReady.then(() => this._onTerminalProcessIdReady(t));
});
}
public dispose(): void {
@@ -43,7 +57,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
ignoreConfigurationCwd: true,
env
};
return TPromise.as(this.terminalService.createInstance(shellLaunchConfig).id);
return TPromise.as(this.terminalService.createTerminal(shellLaunchConfig).id);
}
public $show(terminalId: number, preserveFocus: boolean): void {
@@ -74,11 +88,59 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
}
public $registerOnDataListener(terminalId: number): void {
let terminalInstance = this.terminalService.getInstanceFromId(terminalId);
if (terminalInstance) {
this._dataListeners[terminalId] = terminalInstance.onData(data => this._onTerminalData(terminalId, data));
terminalInstance.onDisposed(instance => delete this._dataListeners[terminalId]);
}
}
private _onTerminalData(terminalId: number, data: string): void {
this._proxy.$acceptTerminalProcessData(terminalId, data);
}
private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalClosed(terminalInstance.id);
}
private _onTerminalOpened(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalOpened(terminalInstance.id, terminalInstance.title);
}
private _onTerminalProcessIdReady(terminalInstance: ITerminalInstance): void {
this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId);
}
private _onTerminalRequestExtHostProcess(request: ITerminalProcessExtHostRequest): void {
this._terminalProcesses[request.proxy.terminalId] = request.proxy;
const shellLaunchConfigDto: ShellLaunchConfigDto = {
name: request.shellLaunchConfig.name,
executable: request.shellLaunchConfig.executable,
args: request.shellLaunchConfig.args,
cwd: request.shellLaunchConfig.cwd,
env: request.shellLaunchConfig.env
};
this._proxy.$createProcess(request.proxy.terminalId, shellLaunchConfigDto, request.cols, request.rows);
request.proxy.onInput(data => this._proxy.$acceptProcessInput(request.proxy.terminalId, data));
request.proxy.onResize((cols, rows) => this._proxy.$acceptProcessResize(request.proxy.terminalId, cols, rows));
request.proxy.onShutdown(() => this._proxy.$acceptProcessShutdown(request.proxy.terminalId));
}
public $sendProcessTitle(terminalId: number, title: string): void {
this._terminalProcesses[terminalId].emitTitle(title);
}
public $sendProcessData(terminalId: number, data: string): void {
this._terminalProcesses[terminalId].emitData(data);
}
public $sendProcessPid(terminalId: number, pid: number): void {
this._terminalProcesses[terminalId].emitPid(pid);
}
public $sendProcessExit(terminalId: number, exitCode: number): void {
this._terminalProcesses[terminalId].emitExit(exitCode);
delete this._terminalProcesses[terminalId];
}
}

View File

@@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
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 { ITreeViewDataProvider, ITreeItem, ICustomViewsService } from 'vs/workbench/common/views';
import { ITreeViewDataProvider, ITreeItem, IViewsService } from 'vs/workbench/common/views';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { distinct } from 'vs/base/common/arrays';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -21,7 +21,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
constructor(
extHostContext: IExtHostContext,
@ICustomViewsService private viewsService: ICustomViewsService,
@IViewsService private viewsService: IViewsService,
@INotificationService private notificationService: INotificationService
) {
super();
@@ -31,7 +31,12 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
$registerTreeViewDataProvider(treeViewId: string): void {
const dataProvider = this._register(new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService));
this._dataProviders.set(treeViewId, dataProvider);
this.viewsService.getTreeViewer(treeViewId).dataProvider = dataProvider;
const treeViewer = this.viewsService.getTreeViewer(treeViewId);
if (treeViewer) {
treeViewer.dataProvider = dataProvider;
} else {
this.notificationService.error('No view is registered with id: ' + treeViewId);
}
}
$reveal(treeViewId: string, item: ITreeItem, parentChain: ITreeItem[], options?: { select?: boolean }): TPromise<void> {
@@ -59,10 +64,10 @@ type TreeItemHandle = string;
class TreeViewDataProvider implements ITreeViewDataProvider {
private _onDidChange: Emitter<ITreeItem[] | undefined | null> = new Emitter<ITreeItem[] | undefined | null>();
private readonly _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>();
private readonly _onDispose: Emitter<void> = new Emitter<void>();
readonly onDispose: Event<void> = this._onDispose.event;
private itemsMap: Map<TreeItemHandle, ITreeItem> = new Map<TreeItemHandle, ITreeItem>();
@@ -87,8 +92,8 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
}
refresh(itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeItem }) {
const itemsToRefresh: ITreeItem[] = [];
if (itemsToRefreshByHandle) {
const itemsToRefresh: ITreeItem[] = [];
for (const treeItemHandle of Object.keys(itemsToRefreshByHandle)) {
const currentTreeItem = this.itemsMap.get(treeItemHandle);
if (currentTreeItem) { // Refresh only if the item exists
@@ -101,13 +106,16 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
// Update maps when handle is changed and refresh parent
this.itemsMap.delete(treeItemHandle);
this.itemsMap.set(currentTreeItem.handle, currentTreeItem);
itemsToRefresh.push(this.itemsMap.get(treeItem.parentHandle));
const parent = treeItem.parentHandle ? this.itemsMap.get(treeItem.parentHandle) : null;
if (parent) {
itemsToRefresh.push(parent);
}
}
}
if (itemsToRefresh.length) {
this._onDidChange.fire(itemsToRefresh);
}
}
}
if (itemsToRefresh.length) {
this._onDidChange.fire(itemsToRefresh);
} else {
this._onDidChange.fire();
}

View File

@@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtHostContext, IExtHostContext, MainContext, MainThreadUrlsShape, ExtHostUrlsShape } from 'vs/workbench/api/node/extHost.protocol';
import { extHostNamedCustomer } from './extHostCustomers';
import { TPromise } from 'vs/base/common/winjs.base';
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import URI from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IExtensionUrlHandler } from 'vs/platform/url/electron-browser/inactiveExtensionUrlHandler';
class ExtensionUrlHandler implements IURLHandler {
constructor(
private readonly proxy: ExtHostUrlsShape,
private readonly handle: number,
readonly extensionId: string
) { }
handleURL(uri: URI): TPromise<boolean> {
if (uri.authority !== this.extensionId) {
return TPromise.as(false);
}
return this.proxy.$handleExternalUri(this.handle, uri).then(() => true);
}
}
@extHostNamedCustomer(MainContext.MainThreadUrls)
export class MainThreadUrls implements MainThreadUrlsShape {
private readonly proxy: ExtHostUrlsShape;
private handlers = new Map<number, { extensionId: string, disposable: IDisposable }>();
constructor(
context: IExtHostContext,
@IURLService private urlService: IURLService,
@IExtensionUrlHandler private inactiveExtensionUrlHandler: IExtensionUrlHandler
) {
this.proxy = context.getProxy(ExtHostContext.ExtHostUrls);
}
$registerProtocolHandler(handle: number, extensionId: string): TPromise<void> {
const handler = new ExtensionUrlHandler(this.proxy, handle, extensionId);
const disposable = this.urlService.registerHandler(handler);
this.handlers.set(handle, { extensionId, disposable });
this.inactiveExtensionUrlHandler.registerExtensionHandler(extensionId, handler);
return TPromise.as(null);
}
$unregisterProtocolHandler(handle: number): TPromise<void> {
const tuple = this.handlers.get(handle);
if (!tuple) {
return TPromise.as(null);
}
const { extensionId, disposable } = tuple;
this.inactiveExtensionUrlHandler.unregisterExtensionHandler(extensionId);
this.handlers.delete(handle);
disposable.dispose();
return TPromise.as(null);
}
dispose(): void {
this.handlers.forEach(({ disposable }) => disposable.dispose());
this.handlers.clear();
}
}

View File

@@ -2,501 +2,117 @@
* 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 * as map from 'vs/base/common/map';
import { TPromise } from 'vs/base/common/winjs.base';
import { MainThreadWebviewsShape, MainContext, IExtHostContext, ExtHostContext, ExtHostWebviewsShape, WebviewHandle } from 'vs/workbench/api/node/extHost.protocol';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { extHostNamedCustomer } from './extHostCustomers';
import { EditorInput, EditorModel, EditorOptions } from 'vs/workbench/common/editor';
import { IEditorModel, Position } from 'vs/platform/editor/common/editor';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { WebviewEditor as BaseWebviewEditor, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from 'vs/workbench/parts/html/browser/webviewEditor';
import { Builder, Dimension } from 'vs/base/browser/builder';
import { Webview } from 'vs/workbench/parts/html/browser/webview';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { localize } from 'vs/nls';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import * as vscode from 'vscode';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import URI from 'vs/base/common/uri';
import DOM = require('vs/base/browser/dom');
import Event, { Emitter } from 'vs/base/common/event';
interface WebviewEvents {
onMessage(message: any): void;
onDidChangePosition(newPosition: Position): void;
onDispose(): void;
onDidClickLink(link: URI, options: vscode.WebviewOptions): void;
}
class WebviewInput extends EditorInput {
private static handlePool = 0;
private readonly _resource: URI;
private _name: string;
private _options: vscode.WebviewOptions;
private _html: string;
private _currentWebviewHtml: string = '';
private _events: WebviewEvents | undefined;
private _container: HTMLElement;
private _webview: Webview | undefined;
private _webviewOwner: any;
private _webviewDisposables: IDisposable[] = [];
private _position: Position;
public static create(
resource: URI,
name: string,
position: Position,
options: vscode.WebviewOptions,
html: string,
events: WebviewEvents,
partService: IPartService
): WebviewInput {
const id = WebviewInput.handlePool++;
const webviewContainer = document.createElement('div');
webviewContainer.id = `webview-${id}`;
partService.getContainer(Parts.EDITOR_PART).appendChild(webviewContainer);
return new WebviewInput(resource, name, position, options, html, events, webviewContainer, undefined);
}
constructor(
resource: URI,
name: string,
position: Position,
options: vscode.WebviewOptions,
html: string,
events: WebviewEvents,
container: HTMLElement,
webview: Webview | undefined
) {
super();
this._resource = resource;
this._name = name;
this._position = position;
this._options = options;
this._html = html;
this._events = events;
this._container = container;
this._webview = webview;
}
public getTypeId(): string {
return 'webview';
}
public dispose() {
this.disposeWebview();
if (this._container) {
this._container.remove();
this._container = undefined;
}
if (this._events) {
this._events.onDispose();
this._events = undefined;
}
super.dispose();
}
public getResource(): URI {
return this._resource;
}
public getName(): string {
return this._name;
}
public setName(value: string): void {
this._name = value;
this._onDidChangeLabel.fire();
}
public get position(): Position {
return this._position;
}
public get html(): string {
return this._html;
}
public setHtml(value: string): void {
if (value === this._currentWebviewHtml) {
return;
}
this._html = value;
if (this._webview) {
this._webview.contents = value;
this._currentWebviewHtml = value;
}
}
public get options(): vscode.WebviewOptions {
return this._options;
}
public set options(value: vscode.WebviewOptions) {
this._options = value;
}
public resolve(refresh?: boolean): TPromise<IEditorModel, any> {
return TPromise.as(new EditorModel());
}
public supportsSplitEditor() {
return false;
}
public get container(): HTMLElement {
return this._container;
}
public get webview(): Webview | undefined {
return this._webview;
}
public set webview(value: Webview) {
this._webviewDisposables = dispose(this._webviewDisposables);
this._webview = value;
this._webview.onDidClickLink(link => {
if (this._events) {
this._events.onDidClickLink(link, this._options);
}
}, null, this._webviewDisposables);
this._webview.onMessage(message => {
if (this._events) {
this._events.onMessage(message);
}
}, null, this._webviewDisposables);
}
public claimWebview(owner: any) {
this._webviewOwner = owner;
}
public releaseWebview(owner: any) {
if (this._webviewOwner === owner) {
this._webviewOwner = undefined;
if (this._options.retainContextWhenHidden) {
this.container.style.visibility = 'hidden';
} else {
this.disposeWebview();
}
}
}
public disposeWebview() {
// The input owns the webview and its parent
if (this._webview) {
this._webview.dispose();
this._webview = undefined;
}
this._webviewDisposables = dispose(this._webviewDisposables);
this._webviewOwner = undefined;
this.container.style.visibility = 'hidden';
this._currentWebviewHtml = '';
}
public onDidChangePosition(position: Position) {
if (this._events) {
this._events.onDidChangePosition(position);
}
this._position = position;
}
}
class WebviewEditor extends BaseWebviewEditor {
public static readonly ID = 'WebviewEditor';
private editorFrame: HTMLElement;
private webviewContent: HTMLElement;
private _onDidFocusWebview: Emitter<void>;
private _webviewFocusTracker?: DOM.IFocusTracker;
private _webviewFocusListenerDisposable?: IDisposable;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IStorageService storageService: IStorageService,
@IThemeService themeService: IThemeService,
@IContextKeyService private _contextKeyService: IContextKeyService,
@IPartService private readonly _partService: IPartService,
@IContextViewService private readonly _contextViewService: IContextViewService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService
) {
super(WebviewEditor.ID, telemetryService, themeService, storageService, _contextKeyService);
this._onDidFocusWebview = new Emitter<void>();
}
protected createEditor(parent: Builder): void {
this.editorFrame = parent.getHTMLElement();
this.content = document.createElement('div');
parent.append(this.content);
}
private doUpdateContainer() {
const webviewContainer = this.input && (this.input as WebviewInput).container;
if (webviewContainer) {
const frameRect = this.editorFrame.getBoundingClientRect();
const containerRect = webviewContainer.parentElement.getBoundingClientRect();
webviewContainer.style.position = 'absolute';
webviewContainer.style.top = `${frameRect.top - containerRect.top}px`;
webviewContainer.style.left = `${frameRect.left - containerRect.left}px`;
webviewContainer.style.width = `${frameRect.width}px`;
webviewContainer.style.height = `${frameRect.height}px`;
}
}
public layout(dimension: Dimension): void {
if (this._webview) {
this.doUpdateContainer();
}
super.layout(dimension);
}
public dispose(): void {
// Let the editor input dispose of the webview.
this._webview = undefined;
this.webviewContent = undefined;
this._onDidFocusWebview.dispose();
if (this._webviewFocusTracker) {
this._webviewFocusTracker.dispose();
}
if (this._webviewFocusListenerDisposable) {
this._webviewFocusListenerDisposable.dispose();
}
super.dispose();
}
public sendMessage(data: any): void {
if (this._webview) {
this._webview.sendMessage(data);
}
}
public get onDidFocus(): Event<any> {
return this._onDidFocusWebview.event;
}
protected setEditorVisible(visible: boolean, position?: Position): void {
if (this.input && this.input instanceof WebviewInput) {
if (visible) {
this.input.claimWebview(this);
} else {
this.input.releaseWebview(this);
}
this.updateWebview(this.input as WebviewInput);
}
if (this.webviewContent) {
if (visible) {
this.webviewContent.style.visibility = 'visible';
this.doUpdateContainer();
} else {
this.webviewContent.style.visibility = 'hidden';
}
}
super.setEditorVisible(visible, position);
}
public clearInput() {
if (this.input && this.input instanceof WebviewInput) {
this.input.releaseWebview(this);
}
this._webview = undefined;
this.webviewContent = undefined;
super.clearInput();
}
async setInput(input: WebviewInput, options: EditorOptions): TPromise<void> {
if (this.input && this.input.matches(input)) {
return undefined;
}
if (this.input && this.input.getResource().fsPath !== input.getResource().fsPath) {
(this.input as WebviewInput).releaseWebview(this);
this._webview = undefined;
this.webviewContent = undefined;
}
await super.setInput(input, options);
input.onDidChangePosition(this.position);
this.updateWebview(input);
}
private updateWebview(input: WebviewInput) {
const webview = this.getWebview(input);
input.claimWebview(this);
webview.options = {
allowScripts: input.options.enableScripts,
enableWrappedPostMessage: true,
useSameOriginForRoot: false,
localResourceRoots: (input && input.options.localResourceRoots) || this._contextService.getWorkspace().folders.map(x => x.uri)
};
input.setHtml(input.html);
this.webviewContent.style.visibility = 'visible';
this.doUpdateContainer();
}
private getWebview(input: WebviewInput): Webview {
if (this._webview) {
return this._webview;
}
this.webviewContent = input.container;
const existing = input.webview;
if (existing) {
this._webview = existing;
return existing;
}
this._webviewFocusTracker = DOM.trackFocus(this.webviewContent);
this._webviewFocusListenerDisposable = this._webviewFocusTracker.onDidFocus(() => {
this._onDidFocusWebview.fire();
});
this._contextKeyService = this._contextKeyService.createScoped(this.webviewContent);
this.contextKey = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS.bindTo(this._contextKeyService);
this.findInputFocusContextKey = KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED.bindTo(this._contextKeyService);
this.findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService);
this._webview = new Webview(
this.webviewContent,
this._partService.getContainer(Parts.EDITOR_PART),
this.themeService,
this._environmentService,
this._contextViewService,
this.contextKey,
this.findInputFocusContextKey,
{
enableWrappedPostMessage: true,
useSameOriginForRoot: false
});
input.webview = this._webview;
this.content.setAttribute('aria-flowto', this.webviewContent.id);
this.doUpdateContainer();
return this._webview;
}
}
import { TPromise } from 'vs/base/common/winjs.base';
import { localize } from 'vs/nls';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { Position } from 'vs/platform/editor/common/editor';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle } from 'vs/workbench/api/node/extHost.protocol';
import { WebviewEditor } from 'vs/workbench/parts/webview/electron-browser/webviewEditor';
import { WebviewEditorInput } from 'vs/workbench/parts/webview/electron-browser/webviewEditorInput';
import { IWebviewEditorService, WebviewInputOptions, WebviewReviver } from 'vs/workbench/parts/webview/electron-browser/webviewEditorService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { extHostNamedCustomer } from './extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews implements MainThreadWebviewsShape {
export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver {
private static readonly serializeTimeout = 500; // ms
private static readonly viewType = 'mainThreadWebview';
private static readonly standardSupportedLinkSchemes = ['http', 'https', 'mailto'];
private _toDispose: Disposable[] = [];
private static revivalPool = 0;
private _toDispose: IDisposable[] = [];
private readonly _proxy: ExtHostWebviewsShape;
private readonly _webviews = new Map<WebviewHandle, WebviewInput>();
private readonly _webviews = new Map<WebviewPanelHandle, WebviewEditorInput>();
private readonly _revivers = new Set<string>();
private _activeWebview: WebviewInput | undefined = undefined;
private _activeWebview: WebviewPanelHandle | undefined = undefined;
constructor(
context: IExtHostContext,
@IContextKeyService _contextKeyService: IContextKeyService,
@IPartService private readonly _partService: IPartService,
@IContextKeyService contextKeyService: IContextKeyService,
@IEditorGroupService editorGroupService: IEditorGroupService,
@ILifecycleService lifecycleService: ILifecycleService,
@IWorkbenchEditorService private readonly _editorService: IWorkbenchEditorService,
@IEditorGroupService private readonly _editorGroupService: IEditorGroupService,
@IOpenerService private readonly _openerService: IOpenerService
@IWebviewEditorService private readonly _webviewService: IWebviewEditorService,
@IOpenerService private readonly _openerService: IOpenerService,
@IExtensionService private readonly _extensionService: IExtensionService,
) {
this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews);
_editorGroupService.onEditorsChanged(this.onEditorsChanged, this, this._toDispose);
editorGroupService.onEditorsChanged(this.onEditorsChanged, this, this._toDispose);
editorGroupService.onEditorGroupMoved(this.onEditorGroupMoved, this, this._toDispose);
this._toDispose.push(_webviewService.registerReviver(MainThreadWebviews.viewType, this));
lifecycleService.onWillShutdown(e => {
e.veto(this._onWillShutdown());
}, this, this._toDispose);
}
dispose(): void {
this._toDispose = dispose(this._toDispose);
}
$createWebview(handle: WebviewHandle, uri: URI, title: string, column: Position, options: vscode.WebviewOptions): void {
const webviewInput = WebviewInput.create(URI.revive(uri), title, column, options, '', {
onMessage: message => this._proxy.$onMessage(handle, message),
onDidChangePosition: position => this._proxy.$onDidChangePosition(handle, position),
onDispose: () => {
this._proxy.$onDidDisposeWeview(handle).then(() => {
this._webviews.delete(handle);
});
},
onDidClickLink: (link, options) => this.onDidClickLink(link, options)
}, this._partService);
$createWebviewPanel(
handle: WebviewPanelHandle,
viewType: string,
title: string,
column: Position,
options: WebviewInputOptions,
extensionFolderPath: string
): void {
const webview = this._webviewService.createWebview(MainThreadWebviews.viewType, title, column, options, extensionFolderPath, this.createWebviewEventDelegate(handle));
webview.state = {
viewType: viewType,
state: undefined
};
this._webviews.set(handle, webviewInput);
this._editorService.openEditor(webviewInput, { pinned: true }, column);
this._webviews.set(handle, webview);
this._activeWebview = handle;
}
$disposeWebview(handle: WebviewHandle): void {
$disposeWebview(handle: WebviewPanelHandle): void {
const webview = this.getWebview(handle);
if (webview) {
this._editorService.closeEditor(webview.position, webview);
}
webview.dispose();
}
$setTitle(handle: WebviewHandle, value: string): void {
$setTitle(handle: WebviewPanelHandle, value: string): void {
const webview = this.getWebview(handle);
webview.setName(value);
}
$setHtml(handle: WebviewHandle, value: string): void {
$setHtml(handle: WebviewPanelHandle, value: string): void {
const webview = this.getWebview(handle);
webview.setHtml(value);
webview.html = value;
}
$show(handle: WebviewHandle, column: Position): void {
const webviewInput = this.getWebview(handle);
if (webviewInput.position === column) {
this._editorService.openEditor(webviewInput, { preserveFocus: true }, column);
} else {
this._editorGroupService.moveEditor(webviewInput, webviewInput.position, column, { preserveFocus: true });
$reveal(handle: WebviewPanelHandle, column: Position | null): void {
const webview = this.getWebview(handle);
if (webview.isDisposed()) {
return;
}
this._webviewService.revealWebview(webview, column);
}
async $sendMessage(handle: WebviewHandle, message: any): Promise<boolean> {
const webviewInput = this.getWebview(handle);
async $postMessage(handle: WebviewPanelHandle, message: any): TPromise<boolean> {
const webview = this.getWebview(handle);
const editors = this._editorService.getVisibleEditors()
.filter(e => e instanceof WebviewEditor)
.map(e => e as WebviewEditor)
.filter(e => e.input.matches(webviewInput));
.filter(e => e.input.matches(webview));
for (const editor of editors) {
editor.sendMessage(message);
@@ -505,18 +121,91 @@ export class MainThreadWebviews implements MainThreadWebviewsShape {
return (editors.length > 0);
}
private getWebview(handle: number): WebviewInput {
const webviewInput = this._webviews.get(handle);
if (!webviewInput) {
$registerSerializer(viewType: string): void {
this._revivers.add(viewType);
}
$unregisterSerializer(viewType: string): void {
this._revivers.delete(viewType);
}
reviveWebview(webview: WebviewEditorInput): TPromise<void> {
const viewType = webview.state.viewType;
return this._extensionService.activateByEvent(`onView:${viewType}`).then(() => {
const handle = 'revival-' + MainThreadWebviews.revivalPool++;
this._webviews.set(handle, webview);
webview._events = this.createWebviewEventDelegate(handle);
return this._proxy.$deserializeWebviewPanel(handle, webview.state.viewType, webview.getTitle(), webview.state.state, webview.position, webview.options)
.then(undefined, () => {
webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
});
});
}
canRevive(webview: WebviewEditorInput): boolean {
if (webview.isDisposed()) {
return false;
}
return this._revivers.has(webview.viewType) || webview.reviver !== null;
}
private _onWillShutdown(): TPromise<boolean> {
const toRevive: WebviewPanelHandle[] = [];
this._webviews.forEach((view, key) => {
if (this.canRevive(view)) {
toRevive.push(key);
}
});
const reviveResponses = toRevive.map(handle =>
TPromise.any([
this._proxy.$serializeWebviewPanel(handle).then(
state => ({ handle, state }),
() => ({ handle, state: null })),
TPromise.timeout(MainThreadWebviews.serializeTimeout).then(() => ({ handle, state: null }))
]).then(x => x.value));
return TPromise.join(reviveResponses).then(results => {
for (const result of results) {
const view = this._webviews.get(result.handle);
if (view) {
if (result.state) {
view.state.state = result.state;
} else {
view.state = null;
}
}
}
return false; // Don't veto shutdown
});
}
private createWebviewEventDelegate(handle: WebviewPanelHandle) {
return {
onDidClickLink: uri => this.onDidClickLink(handle, uri),
onMessage: message => this._proxy.$onMessage(handle, message),
onDispose: () => {
this._proxy.$onDidDisposeWebviewPanel(handle).then(
() => this._webviews.delete(handle),
() => this._webviews.delete(handle));
}
};
}
private getWebview(handle: WebviewPanelHandle): WebviewEditorInput {
const webview = this._webviews.get(handle);
if (!webview) {
throw new Error('Unknown webview handle:' + handle);
}
return webviewInput;
return webview;
}
private onEditorsChanged() {
const activeEditor = this._editorService.getActiveEditor();
let newActiveWebview: { input: WebviewInput, handle: WebviewHandle } | undefined = undefined;
if (activeEditor && activeEditor.input instanceof WebviewInput) {
let newActiveWebview: { input: WebviewEditorInput, handle: WebviewPanelHandle } | undefined = undefined;
if (activeEditor && activeEditor.input instanceof WebviewEditorInput) {
for (const handle of map.keys(this._webviews)) {
const input = this._webviews.get(handle);
if (input.matches(activeEditor.input)) {
@@ -526,33 +215,64 @@ export class MainThreadWebviews implements MainThreadWebviewsShape {
}
}
if (newActiveWebview && newActiveWebview.handle === this._activeWebview) {
// Webview itself unchanged but position may have changed
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, true, newActiveWebview.input.position);
return;
}
// Broadcast view state update for currently active
if (typeof this._activeWebview !== 'undefined') {
const oldActiveWebview = this._webviews.get(this._activeWebview);
if (oldActiveWebview) {
this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, false, oldActiveWebview.position);
}
}
// Then for newly active
if (newActiveWebview) {
if (!this._activeWebview || !newActiveWebview.input.matches(this._activeWebview)) {
this._proxy.$onDidChangeActiveWeview(newActiveWebview.handle);
this._activeWebview = newActiveWebview.input;
}
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, true, activeEditor.position);
this._activeWebview = newActiveWebview.handle;
} else {
if (this._activeWebview) {
this._proxy.$onDidChangeActiveWeview(undefined);
this._activeWebview = undefined;
}
this._activeWebview = undefined;
}
}
private onDidClickLink(link: URI, options: vscode.WebviewOptions): void {
private onEditorGroupMoved(): void {
for (const workbenchEditor of this._editorService.getVisibleEditors()) {
if (!workbenchEditor.input) {
return;
}
this._webviews.forEach((input, handle) => {
if (workbenchEditor.input.matches(input) && input.position !== workbenchEditor.position) {
input.updatePosition(workbenchEditor.position);
this._proxy.$onDidChangeWebviewPanelViewState(handle, handle === this._activeWebview, workbenchEditor.position);
}
});
}
}
private onDidClickLink(handle: WebviewPanelHandle, link: URI): void {
if (!link) {
return;
}
const enableCommandUris = options.enableCommandUris;
const webview = this.getWebview(handle);
const enableCommandUris = webview.options.enableCommandUris;
if (MainThreadWebviews.standardSupportedLinkSchemes.indexOf(link.scheme) >= 0 || enableCommandUris && link.scheme === 'command') {
this._openerService.open(link);
}
}
}
(<IEditorRegistry>Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor(
WebviewEditor,
WebviewEditor.ID,
localize('webview.editor.label', "webview editor")),
[new SyncDescriptor(WebviewInput)]);
private static getDeserializationFailedContents(viewType: string) {
return `<!DOCTYPE html>
<html>
<head>
<base href="https://code.visualstudio.com/raw/">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https: data:; media-src https:; script-src 'none'; style-src vscode-core-resource: https: 'unsafe-inline'; child-src 'none'; frame-src 'none';">
</head>
<body>${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)}</body>
</html>`;
}
}

View File

@@ -9,6 +9,7 @@ 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';
import { latch } from 'vs/base/common/event';
@extHostNamedCustomer(MainContext.MainThreadWindow)
export class MainThreadWindow implements MainThreadWindowShape {
@@ -22,7 +23,8 @@ export class MainThreadWindow implements MainThreadWindowShape {
) {
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostWindow);
windowService.onDidChangeFocus(this.proxy.$onDidChangeWindowFocus, this.proxy, this.disposables);
latch(windowService.onDidChangeFocus)
(this.proxy.$onDidChangeWindowFocus, this.proxy, this.disposables);
}
$getWindowVisibility(): TPromise<boolean> {

View File

@@ -0,0 +1,99 @@
/*---------------------------------------------------------------------------------------------
* 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 * as vscode from 'vscode';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands';
import { Position as EditorPosition, ITextEditorOptions } from 'vs/platform/editor/common/editor';
// -----------------------------------------------------------------
// The following commands are registered on both sides separately.
//
// We are trying to maintain backwards compatibility for cases where
// API commands are encoded as markdown links, for example.
// -----------------------------------------------------------------
export interface ICommandsExecutor {
executeCommand<T>(id: string, ...args: any[]): Thenable<T>;
}
function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) => any): ICommandHandler {
return (accessor, ...args: any[]) => {
return handler(accessor.get(ICommandService), ...args);
};
}
export class PreviewHTMLAPICommand {
public static ID = 'vscode.previewHtml';
public static execute(executor: ICommandsExecutor, uri: URI, position?: vscode.ViewColumn, label?: string, options?: any): Thenable<any> {
return executor.executeCommand('_workbench.previewHtml',
uri,
typeof position === 'number' && typeConverters.fromViewColumn(position),
label,
options
);
}
}
CommandsRegistry.registerCommand(PreviewHTMLAPICommand.ID, adjustHandler(PreviewHTMLAPICommand.execute));
export class OpenFolderAPICommand {
public static ID = 'vscode.openFolder';
public static execute(executor: ICommandsExecutor, uri?: URI, forceNewWindow?: boolean): Thenable<any> {
if (!uri) {
return executor.executeCommand('_files.pickFolderAndOpen', forceNewWindow);
}
return executor.executeCommand('_files.windowOpen', [uri.fsPath], forceNewWindow);
}
}
CommandsRegistry.registerCommand(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute));
export class DiffAPICommand {
public static ID = 'vscode.diff';
public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: vscode.TextDocumentShowOptions): Thenable<any> {
return executor.executeCommand('_workbench.diff', [
left, right,
label,
undefined,
typeConverters.toTextEditorOptions(options),
options ? typeConverters.fromViewColumn(options.viewColumn) : undefined
]);
}
}
CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute));
export class OpenAPICommand {
public static ID = 'vscode.open';
public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions): Thenable<any> {
let options: ITextEditorOptions;
let column: EditorPosition;
if (columnOrOptions) {
if (typeof columnOrOptions === 'number') {
column = typeConverters.fromViewColumn(columnOrOptions);
} else {
options = typeConverters.toTextEditorOptions(columnOrOptions);
column = typeConverters.fromViewColumn(columnOrOptions.viewColumn);
}
}
return executor.executeCommand('_workbench.open', [
resource,
options,
column
]);
}
}
CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute));
export class RemoveFromRecentlyOpenedAPICommand {
public static ID = 'vscode.removeFromRecentlyOpened';
public static execute(executor: ICommandsExecutor, path: string): Thenable<any> {
return executor.executeCommand('_workbench.removeFromRecentlyOpened', path);
}
}
CommandsRegistry.registerCommand(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute));

View File

@@ -46,6 +46,7 @@ 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 * as files from 'vs/platform/files/common/files';
import { MainContext, ExtHostContext, IInitData, IExtHostContext } from './extHost.protocol';
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
@@ -55,10 +56,11 @@ import { ExtHostFileSystem } from 'vs/workbench/api/node/extHostFileSystem';
import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations';
import { toGlobPattern, toLanguageSelector } from 'vs/workbench/api/node/extHostTypeConverters';
import { ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { OverviewRulerLane } from 'vs/editor/common/model';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview';
import { ExtHostSearch } from './extHostSearch';
import { ExtHostUrls } from './extHostUrls';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
@@ -99,9 +101,10 @@ export function createApiFactory(
const extHostHeapService = rpcProtocol.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService());
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, new ExtHostDecorations(rpcProtocol));
const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol));
const extHostDocumentsAndEditors = rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(rpcProtocol, extHostWebviews));
const extHostUrls = rpcProtocol.set(ExtHostContext.ExtHostUrls, new ExtHostUrls(rpcProtocol));
const extHostDocumentsAndEditors = rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(rpcProtocol));
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors));
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadTextEditors)));
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, extHostLogService));
@@ -111,15 +114,17 @@ export function createApiFactory(
//const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(threadService, extHostWorkspace));
rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration);
const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics));
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService());
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace));
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = Object.keys(ExtHostContext).map((key) => ExtHostContext[key]);
@@ -129,44 +134,51 @@ export function createApiFactory(
const extHostMessageService = new ExtHostMessageService(rpcProtocol);
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol);
const extHostProgress = new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress));
const extHostOutputService = new ExtHostOutputService(rpcProtocol);
const extHostLanguages = new ExtHostLanguages(rpcProtocol);
// Register API-ish commands
ExtHostApiCommands.register(extHostCommands);
ExtHostApiCommands.register(extHostCommands, extHostTask);
return function (extension: IExtensionDescription): typeof vscode {
if (!isFalsyOrEmpty(product.extensionAllowedProposedApi)
&& product.extensionAllowedProposedApi.indexOf(extension.id) >= 0
) {
// fast lane -> proposed api is available to all extensions
// that are listed in product.json-files
extension.enableProposedApi = true;
} else 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.`);
// Check document selectors for being overly generic. Technically this isn't a problem but
// in practice many extensions say they support `fooLang` but need fs-access to do so. Those
// extension should specify then the `file`-scheme, e.g `{ scheme: 'fooLang', language: 'fooLang' }`
// We only inform once, it is not a warning because we just want to raise awareness and because
// we cannot say if the extension is doing it right or wrong...
let checkSelector = (function () {
let done = initData.environment.extensionDevelopmentPath !== extension.extensionFolderPath;
function informOnce(selector: vscode.DocumentSelector) {
if (!done) {
console.info(`Extension '${extension.id}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`);
done = true;
}
}
}
return function perform(selector: vscode.DocumentSelector): vscode.DocumentSelector {
if (Array.isArray(selector)) {
selector.forEach(perform);
} else if (typeof selector === 'string') {
informOnce(selector);
} else {
if (typeof selector.scheme === 'undefined') {
informOnce(selector);
}
if (!extension.enableProposedApi && typeof selector.exclusive === 'boolean') {
throwProposedApiError(extension);
}
}
return selector;
};
})();
// namespace: commands
const commands: typeof vscode.commands = {
registerCommand(id: string, command: <T>(...args: any[]) => T | Thenable<T>, thisArgs?: any): vscode.Disposable {
return extHostCommands.registerCommand(id, command, thisArgs);
return extHostCommands.registerCommand(true, 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 => {
return extHostCommands.registerCommand(true, id, (...args: any[]): any => {
let activeTextEditor = extHostEditors.getActiveTextEditor();
if (!activeTextEditor) {
console.warn('Cannot execute ' + id + ' because there is no active text editor.');
@@ -187,7 +199,7 @@ export function createApiFactory(
});
},
registerDiffInformationCommand: proposedApiFunction(extension, (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => {
return extHostCommands.registerCommand(id, async (...args: any[]) => {
return extHostCommands.registerCommand(true, id, async (...args: any[]) => {
let activeTextEditor = extHostEditors.getActiveTextEditor();
if (!activeTextEditor) {
console.warn('Cannot execute ' + id + ' because there is no active text editor.');
@@ -213,6 +225,7 @@ export function createApiFactory(
get language() { return Platform.language; },
get appName() { return product.nameLong; },
get appRoot() { return initData.environment.appRoot; },
get logLevel() { return extHostLogService.getLevel(); }
});
// namespace: extensions
@@ -234,69 +247,75 @@ export function createApiFactory(
createDiagnosticCollection(name?: string): vscode.DiagnosticCollection {
return extHostDiagnostics.createDiagnosticCollection(name);
},
get onDidChangeDiagnostics() {
return extHostDiagnostics.onDidChangeDiagnostics;
},
getDiagnostics: (resource?) => {
return <any>extHostDiagnostics.getDiagnostics(resource);
},
getLanguages(): TPromise<string[]> {
return extHostLanguages.getLanguages();
},
match(selector: vscode.DocumentSelector, document: vscode.TextDocument): number {
return score(toLanguageSelector(selector), document.uri, document.languageId);
return score(toLanguageSelector(selector), document.uri, document.languageId, true);
},
registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCodeActionProvider(selector, provider);
registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
return extHostLanguageFeatures.registerCodeActionProvider(checkSelector(selector), provider, metadata);
},
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCodeLensProvider(selector, provider);
return extHostLanguageFeatures.registerCodeLensProvider(checkSelector(selector), provider);
},
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDefinitionProvider(selector, provider);
return extHostLanguageFeatures.registerDefinitionProvider(checkSelector(selector), provider);
},
registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable {
return extHostLanguageFeatures.registerImplementationProvider(selector, provider);
return extHostLanguageFeatures.registerImplementationProvider(checkSelector(selector), provider);
},
registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable {
return extHostLanguageFeatures.registerTypeDefinitionProvider(selector, provider);
return extHostLanguageFeatures.registerTypeDefinitionProvider(checkSelector(selector), provider);
},
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable {
return extHostLanguageFeatures.registerHoverProvider(selector, provider, extension.id);
return extHostLanguageFeatures.registerHoverProvider(checkSelector(selector), provider, extension.id);
},
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentHighlightProvider(selector, provider);
return extHostLanguageFeatures.registerDocumentHighlightProvider(checkSelector(selector), provider);
},
registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable {
return extHostLanguageFeatures.registerReferenceProvider(selector, provider);
return extHostLanguageFeatures.registerReferenceProvider(checkSelector(selector), provider);
},
registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
return extHostLanguageFeatures.registerRenameProvider(selector, provider, extension.enableProposedApi);
return extHostLanguageFeatures.registerRenameProvider(checkSelector(selector), provider);
},
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentSymbolProvider(selector, provider);
return extHostLanguageFeatures.registerDocumentSymbolProvider(checkSelector(selector), provider);
},
registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable {
return extHostLanguageFeatures.registerWorkspaceSymbolProvider(provider);
},
registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentFormattingEditProvider(selector, provider);
return extHostLanguageFeatures.registerDocumentFormattingEditProvider(checkSelector(selector), provider);
},
registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentRangeFormattingEditProvider(selector, provider);
return extHostLanguageFeatures.registerDocumentRangeFormattingEditProvider(checkSelector(selector), provider);
},
registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacters: string[]): vscode.Disposable {
return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(selector, provider, [firstTriggerCharacter].concat(moreTriggerCharacters));
return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters));
},
registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, ...triggerCharacters: string[]): vscode.Disposable {
return extHostLanguageFeatures.registerSignatureHelpProvider(selector, provider, triggerCharacters);
return extHostLanguageFeatures.registerSignatureHelpProvider(checkSelector(selector), provider, triggerCharacters);
},
registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable {
return extHostLanguageFeatures.registerCompletionItemProvider(selector, provider, triggerCharacters);
return extHostLanguageFeatures.registerCompletionItemProvider(checkSelector(selector), provider, triggerCharacters);
},
registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentLinkProvider(selector, provider);
return extHostLanguageFeatures.registerDocumentLinkProvider(checkSelector(selector), provider);
},
registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable {
return extHostLanguageFeatures.registerColorProvider(selector, provider);
return extHostLanguageFeatures.registerColorProvider(checkSelector(selector), provider);
},
registerFoldingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable {
return extHostLanguageFeatures.registerFoldingRangeProvider(checkSelector(selector), provider);
},
registerFoldingProvider: proposedApiFunction(extension, (selector: vscode.DocumentSelector, provider: vscode.FoldingProvider): vscode.Disposable => {
return extHostLanguageFeatures.registerFoldingProvider(selector, provider);
}),
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => {
return extHostLanguageFeatures.setLanguageConfiguration(language, configuration);
}
@@ -310,6 +329,9 @@ export function createApiFactory(
get visibleTextEditors() {
return extHostEditors.getVisibleTextEditors();
},
get terminals() {
return proposedApiFunction(extension, extHostTerminalService.terminals);
},
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)) {
@@ -336,15 +358,18 @@ export function createApiFactory(
onDidChangeTextEditorOptions(listener: (e: vscode.TextEditorOptionsChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) {
return extHostEditors.onDidChangeTextEditorOptions(listener, thisArgs, disposables);
},
onDidChangeTextEditorVisibleRanges: proposedApiFunction(extension, (listener: (e: vscode.TextEditorVisibleRangesChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => {
onDidChangeTextEditorVisibleRanges(listener: (e: vscode.TextEditorVisibleRangesChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) {
return extHostEditors.onDidChangeTextEditorVisibleRanges(listener, thisArgs, disposables);
}),
},
onDidChangeTextEditorViewColumn(listener, thisArg?, disposables?) {
return extHostEditors.onDidChangeTextEditorViewColumn(listener, thisArg, disposables);
},
onDidCloseTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidCloseTerminal(listener, thisArg, disposables);
},
onDidOpenTerminal: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostTerminalService.onDidOpenTerminal(listener, thisArg, disposables);
}),
get state() {
return extHostWindow.state;
},
@@ -360,7 +385,7 @@ export function createApiFactory(
showErrorMessage(message, first, ...rest) {
return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest);
},
showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken) {
showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken): any {
return extHostQuickOpen.showQuickPick(items, options, token);
},
showWorkspaceFolderPick(options: vscode.WorkspaceFolderPickOptions) {
@@ -385,20 +410,26 @@ export function createApiFactory(
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>) {
withProgress<R>(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; worked?: number }>, token: vscode.CancellationToken) => Thenable<R>) {
return extHostProgress.withProgress(extension, options, task);
},
createOutputChannel(name: string): vscode.OutputChannel {
return extHostOutputService.createOutputChannel(name);
},
createWebviewPanel(viewType: string, title: string, column: vscode.ViewColumn, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
return extHostWebviews.createWebview(viewType, title, column, options, extension.extensionFolderPath);
},
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.TreeView<any> {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, (fn) => proposedApiFunction(extension, fn));
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider);
},
createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider<any> }): vscode.TreeView<any> {
return extHostTreeViews.createTreeView(viewId, options);
},
// proposed API
sampleFunction: proposedApiFunction(extension, () => {
@@ -407,12 +438,11 @@ export function createApiFactory(
registerDecorationProvider: proposedApiFunction(extension, (provider: vscode.DecorationProvider) => {
return extHostDecorations.registerDecorationProvider(provider, extension.id);
}),
// {{SQL CARBON EDIT}}
createWebview(uri: vscode.Uri, title: string, column: vscode.ViewColumn, options: vscode.WebviewOptions): vscode.Webview {
return extHostWebviews.createWebview(uri, title, column, options);
},
onDidChangeActiveEditor: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostDocumentsAndEditors.onDidChangeActiveEditor(listener, thisArg, disposables);
registerWebviewPanelSerializer: proposedApiFunction(extension, (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer);
}),
registerProtocolHandler: proposedApiFunction(extension, (handler: vscode.ProtocolHandler) => {
return extHostUrls.registerProtocolHandler(extension.id, handler);
})
};
@@ -512,8 +542,29 @@ export function createApiFactory(
registerTaskProvider: (type: string, provider: vscode.TaskProvider) => {
return extHostTask.registerTaskProvider(extension, provider);
},
registerFileSystemProvider: proposedApiFunction(extension, (authority, provider) => {
return extHostFileSystem.registerFileSystemProvider(authority, provider);
fetchTasks: proposedApiFunction(extension, (filter?: vscode.TaskFilter): Thenable<vscode.Task[]> => {
return extHostTask.fetchTasks(filter);
}),
executeTask: proposedApiFunction(extension, (task: vscode.Task): Thenable<vscode.TaskExecution> => {
return extHostTask.executeTask(extension, task);
}),
get taskExecutions(): vscode.TaskExecution[] {
return extHostTask.taskExecutions;
},
onDidStartTask: (listeners, thisArgs?, disposables?) => {
return extHostTask.onDidStartTask(listeners, thisArgs, disposables);
},
onDidEndTask: (listeners, thisArgs?, disposables?) => {
return extHostTask.onDidEndTask(listeners, thisArgs, disposables);
},
registerFileSystemProvider(scheme, provider, options) {
return extHostFileSystem.registerFileSystemProvider(scheme, provider, options);
},
registerDeprecatedFileSystemProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostFileSystem.registerDeprecatedFileSystemProvider(scheme, provider);
}),
registerSearchProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostSearch.registerSearchProvider(scheme, provider);
})
};
@@ -559,6 +610,7 @@ export function createApiFactory(
// {{SQL CARBON EDIT}}
// DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable,
Diagnostic: extHostTypes.Diagnostic,
DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation,
DiagnosticSeverity: extHostTypes.DiagnosticSeverity,
Disposable: extHostTypes.Disposable,
DocumentHighlight: extHostTypes.DocumentHighlight,
@@ -582,6 +634,12 @@ export function createApiFactory(
SourceBreakpoint: extHostTypes.SourceBreakpoint,
StatusBarAlignment: extHostTypes.StatusBarAlignment,
SymbolInformation: extHostTypes.SymbolInformation,
HierarchicalSymbolInformation: class extends extHostTypes.HierarchicalSymbolInformation {
constructor(name, kind, keyof, range) {
checkProposedApiEnabled(extension);
super(name, kind, keyof, range);
}
},
SymbolKind: extHostTypes.SymbolKind,
SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType,
TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason,
@@ -605,16 +663,19 @@ export function createApiFactory(
TaskGroup: extHostTypes.TaskGroup,
ProcessExecution: extHostTypes.ProcessExecution,
ShellExecution: extHostTypes.ShellExecution,
ShellQuoting: extHostTypes.ShellQuoting,
TaskScope: extHostTypes.TaskScope,
Task: extHostTypes.Task,
ConfigurationTarget: extHostTypes.ConfigurationTarget,
RelativePattern: extHostTypes.RelativePattern,
DeprecatedFileChangeType: extHostTypes.DeprecatedFileChangeType,
DeprecatedFileType: extHostTypes.DeprecatedFileType,
FileChangeType: extHostTypes.FileChangeType,
FileType: extHostTypes.FileType,
FoldingRangeList: extHostTypes.FoldingRangeList,
FileType: files.FileType,
FileSystemError: extHostTypes.FileSystemError,
FoldingRange: extHostTypes.FoldingRange,
FoldingRangeType: extHostTypes.FoldingRangeType
FoldingRangeKind: extHostTypes.FoldingRangeKind
};
};
}
@@ -643,7 +704,7 @@ class Extension<T> implements vscode.Extension<T> {
}
activate(): Thenable<T> {
return this._extensionService.activateById(this.id, new ExtensionActivatedByAPI(false)).then(() => this.exports);
return this._extensionService.activateByIdWithErrors(this.id, new ExtensionActivatedByAPI(false)).then(() => this.exports);
}
}

View File

@@ -24,7 +24,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import * as modes from 'vs/editor/common/modes';
import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { IConfig, IAdapterExecutable } from 'vs/workbench/parts/debug/common/debug';
import { IConfig, IAdapterExecutable, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug';
import { IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
@@ -42,22 +42,19 @@ import { ITreeItem } from 'vs/workbench/common/views';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { SerializedError } from 'vs/base/common/errors';
import { IStat, FileChangeType } from 'vs/platform/files/common/files';
import { IStat, FileChangeType, IWatchOptions, FileSystemProviderCapabilities, FileWriteOptions, FileType, FileOverwriteOptions } from 'vs/platform/files/common/files';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { ILineMatch, IPatternInfo } from 'vs/platform/search/common/search';
import { LogLevel } from 'vs/platform/log/common/log';
import { TaskExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO } from 'vs/workbench/api/shared/tasks';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
enableProposedApiForAll: boolean;
enableProposedApiFor: string | string[];
appRoot: string;
appSettingsHome: string;
disableExtensions: boolean;
userExtensionsHome: string;
extensionDevelopmentPath: string;
extensionTestsPath: string;
}
@@ -77,9 +74,8 @@ export interface IInitData {
configuration: IConfigurationInitData;
telemetryInfo: ITelemetryInfo;
windowId: number;
args: ParsedArgs;
execPath: string;
logLevel: LogLevel;
logsPath: string;
}
export interface IConfigurationInitData extends IConfigurationData {
@@ -257,28 +253,36 @@ export interface ISerializedLanguageConfiguration {
};
}
export interface ISerializedDocumentFilter {
$serialized: true;
language?: string;
scheme?: string;
pattern?: vscode.GlobPattern;
exclusive?: boolean;
}
export interface MainThreadLanguageFeaturesShape extends IDisposable {
$unregister(handle: number): void;
$registerOutlineSupport(handle: number, selector: vscode.DocumentSelector): void;
$registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector, eventHandle: number): void;
$registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void;
$emitCodeLensEvent(eventHandle: number, event?: any): void;
$registerDeclaractionSupport(handle: number, selector: vscode.DocumentSelector): void;
$registerImplementationSupport(handle: number, selector: vscode.DocumentSelector): void;
$registerTypeDefinitionSupport(handle: number, selector: vscode.DocumentSelector): void;
$registerHoverProvider(handle: number, selector: vscode.DocumentSelector): void;
$registerDocumentHighlightProvider(handle: number, selector: vscode.DocumentSelector): void;
$registerReferenceSupport(handle: number, selector: vscode.DocumentSelector): void;
$registerQuickFixSupport(handle: number, selector: vscode.DocumentSelector): void;
$registerDocumentFormattingSupport(handle: number, selector: vscode.DocumentSelector): void;
$registerRangeFormattingSupport(handle: number, selector: vscode.DocumentSelector): void;
$registerOnTypeFormattingSupport(handle: number, selector: vscode.DocumentSelector, autoFormatTriggerCharacters: string[]): void;
$registerDeclaractionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerTypeDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerHoverProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDocumentHighlightProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerReferenceSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerQuickFixSupport(handle: number, selector: ISerializedDocumentFilter[], supportedKinds?: string[]): void;
$registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[]): void;
$registerNavigateTypeSupport(handle: number): void;
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector, supportsResolveInitialValues: boolean): void;
$registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[], supportsResolveDetails: boolean): void;
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): void;
$registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): void;
$registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): void;
$registerFoldingProvider(handle: number, selector: vscode.DocumentSelector): void;
$registerRenameSupport(handle: number, selector: ISerializedDocumentFilter[], supportsResolveInitialValues: boolean): void;
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean): void;
$registerSignatureHelpProvider(handle: number, selector: ISerializedDocumentFilter[], triggerCharacter: string[]): void;
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDocumentColorProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerFoldingRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$setLanguageConfiguration(handle: number, languageId: string, configuration: ISerializedLanguageConfiguration): void;
}
@@ -316,13 +320,19 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
$hide(terminalId: number): void;
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
$show(terminalId: number, preserveFocus: boolean): void;
$registerOnDataListener(terminalId: number): void;
$sendProcessTitle(terminalId: number, title: string): void;
$sendProcessData(terminalId: number, data: string): void;
$sendProcessPid(terminalId: number, pid: number): void;
$sendProcessExit(terminalId: number, exitCode: number): void;
}
export interface MyQuickPickItems extends IPickOpenEntry {
handle: number;
}
export interface MainThreadQuickOpenShape extends IDisposable {
$show(options: IPickOptions): TPromise<number>;
$show(options: IPickOptions): TPromise<number | number[]>;
$setItems(items: MyQuickPickItems[]): TPromise<any>;
$setError(error: Error): TPromise<any>;
$input(options: vscode.InputBoxOptions, validateInput: boolean): TPromise<string>;
@@ -342,21 +352,35 @@ export interface MainThreadTelemetryShape extends IDisposable {
$publicLog(eventName: string, data?: any): void;
}
export type WebviewHandle = number;
export type WebviewPanelHandle = string;
export interface MainThreadWebviewsShape extends IDisposable {
$createWebview(handle: WebviewHandle, uri: URI, title: string, column: EditorPosition, options: vscode.WebviewOptions): void;
$disposeWebview(handle: WebviewHandle): void;
$show(handle: WebviewHandle, column: EditorPosition): void;
$setTitle(handle: WebviewHandle, value: string): void;
$setHtml(handle: WebviewHandle, value: string): void;
$sendMessage(handle: WebviewHandle, value: any): Thenable<boolean>;
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, column: EditorPosition, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionFolderPath: string): void;
$disposeWebview(handle: WebviewPanelHandle): void;
$reveal(handle: WebviewPanelHandle, column: EditorPosition | undefined): void;
$setTitle(handle: WebviewPanelHandle, value: string): void;
$setHtml(handle: WebviewPanelHandle, value: string): void;
$postMessage(handle: WebviewPanelHandle, value: any): Thenable<boolean>;
$registerSerializer(viewType: string): void;
$unregisterSerializer(viewType: string): void;
}
export interface ExtHostWebviewsShape {
$onMessage(handle: WebviewHandle, message: any): void;
$onDidChangeActiveWeview(handle: WebviewHandle | undefined): void;
$onDidDisposeWeview(handle: WebviewHandle): Thenable<void>;
$onDidChangePosition(handle: WebviewHandle, newPosition: EditorPosition): void;
$onMessage(handle: WebviewPanelHandle, message: any): void;
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, active: boolean, position: EditorPosition): void;
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Thenable<void>;
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorPosition, options: vscode.WebviewOptions): Thenable<void>;
$serializeWebviewPanel(webviewHandle: WebviewPanelHandle): Thenable<any>;
}
export interface MainThreadUrlsShape extends IDisposable {
$registerProtocolHandler(handle: number, extensionId: string): TPromise<void>;
$unregisterProtocolHandler(handle: number): TPromise<void>;
}
export interface ExtHostUrlsShape {
$handleExternalUri(handle: number, uri: UriComponents): TPromise<void>;
}
export interface MainThreadWorkspaceShape extends IDisposable {
@@ -372,18 +396,22 @@ export interface IFileChangeDto {
}
export interface MainThreadFileSystemShape extends IDisposable {
$registerFileSystemProvider(handle: number, scheme: string): void;
$unregisterFileSystemProvider(handle: number): void;
$onDidAddFileSystemRoot(root: UriComponents): void;
$registerFileSystemProvider(handle: number, scheme: string, capabilities: FileSystemProviderCapabilities): void;
$unregisterProvider(handle: number): void;
$onFileSystemChange(handle: number, resource: IFileChangeDto[]): void;
$reportFileChunk(handle: number, session: number, chunk: number[] | null): void;
}
export interface MainThreadSearchShape extends IDisposable {
$registerSearchProvider(handle: number, scheme: string): void;
$unregisterProvider(handle: number): void;
$handleFindMatch(handle: number, session, data: UriComponents | [UriComponents, ILineMatch]): void;
}
export interface MainThreadTaskShape extends IDisposable {
$registerTaskProvider(handle: number): TPromise<void>;
$fetchTasks(filter?: TaskFilterDTO): TPromise<TaskDTO[]>;
$executeTask(task: TaskHandleDTO | TaskDTO): TPromise<TaskExecutionDTO>;
$terminateTask(id: string): TPromise<void>;
$unregisterTaskProvider(handle: number): TPromise<void>;
}
@@ -454,6 +482,10 @@ export interface MainThreadSCMShape extends IDisposable {
export type DebugSessionUUID = string;
export interface MainThreadDebugServiceShape extends IDisposable {
$registerDebugTypes(debugTypes: string[]);
$acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage);
$acceptDAError(handle: number, name: string, message: string, stack: string);
$acceptDAExit(handle: number, code: number, signal: string);
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasDebugAdapterExecutable: boolean, handle: number): TPromise<any>;
$unregisterDebugConfigurationProvider(handle: number): TPromise<any>;
$startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise<boolean>;
@@ -555,16 +587,20 @@ export interface ExtHostWorkspaceShape {
}
export interface ExtHostFileSystemShape {
$utimes(handle: number, resource: UriComponents, mtime: number, atime: number): TPromise<IStat>;
$stat(handle: number, resource: UriComponents): TPromise<IStat>;
$read(handle: number, session: number, offset: number, count: number, resource: UriComponents): TPromise<number>;
$write(handle: number, resource: UriComponents, content: number[]): TPromise<void>;
$unlink(handle: number, resource: UriComponents): TPromise<void>;
$move(handle: number, resource: UriComponents, target: UriComponents): TPromise<IStat>;
$mkdir(handle: number, resource: UriComponents): TPromise<IStat>;
$readdir(handle: number, resource: UriComponents): TPromise<[UriComponents, IStat][]>;
$rmdir(handle: number, resource: UriComponents): TPromise<void>;
$findFiles(handle: number, session: number, query: string): TPromise<void>;
$readdir(handle: number, resource: UriComponents): TPromise<[string, FileType][]>;
$readFile(handle: number, resource: UriComponents): TPromise<string>;
$writeFile(handle: number, resource: UriComponents, base64Encoded: string, opts: FileWriteOptions): TPromise<void>;
$rename(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise<void>;
$copy(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise<void>;
$mkdir(handle: number, resource: UriComponents): TPromise<void>;
$delete(handle: number, resource: UriComponents): TPromise<void>;
$watch(handle: number, session: number, resource: UriComponents, opts: IWatchOptions): void;
$unwatch(handle: number, session: number): void;
}
export interface ExtHostSearchShape {
$provideFileSearchResults(handle: number, session: number, query: string): TPromise<void>;
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, options: { includes: string[], excludes: string[] }): TPromise<void>;
}
@@ -697,7 +733,7 @@ export interface ExtHostLanguageFeaturesShape {
$resolveWorkspaceSymbol(handle: number, symbol: SymbolInformationDto): TPromise<SymbolInformationDto>;
$releaseWorkspaceSymbols(handle: number, id: number): void;
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string): TPromise<WorkspaceEditDto>;
$resolveInitialRenameValue(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.RenameInitialValue>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.RenameLocation>;
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.SuggestContext): TPromise<SuggestResultDto>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.ISuggestion): TPromise<modes.ISuggestion>;
$releaseCompletionItems(handle: number, id: number): void;
@@ -706,7 +742,7 @@ export interface ExtHostLanguageFeaturesShape {
$resolveDocumentLink(handle: number, link: modes.ILink): TPromise<modes.ILink>;
$provideDocumentColors(handle: number, resource: UriComponents): TPromise<IRawColorInfo[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo): TPromise<modes.IColorPresentation[]>;
$provideFoldingRanges(handle: number, resource: UriComponents): TPromise<modes.IFoldingRangeList>;
$provideFoldingRanges(handle: number, resource: UriComponents, context: modes.FoldingContext): TPromise<modes.FoldingRange[]>;
}
export interface ExtHostQuickOpenShape {
@@ -714,9 +750,23 @@ export interface ExtHostQuickOpenShape {
$validateInput(input: string): TPromise<string>;
}
export interface ShellLaunchConfigDto {
name?: string;
executable?: string;
args?: string[] | string;
cwd?: string;
env?: { [key: string]: string };
}
export interface ExtHostTerminalServiceShape {
$acceptTerminalClosed(id: number): void;
$acceptTerminalOpened(id: number, name: string): void;
$acceptTerminalProcessId(id: number, processId: number): void;
$acceptTerminalProcessData(id: number, data: string);
$createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, cols: number, rows: number): void;
$acceptProcessInput(id: number, data: string): void;
$acceptProcessResize(id: number, cols: number, rows: number): void;
$acceptProcessShutdown(id: number): void;
}
export interface ExtHostSCMShape {
@@ -728,23 +778,26 @@ export interface ExtHostSCMShape {
export interface ExtHostTaskShape {
$provideTasks(handle: number): TPromise<TaskSet>;
$taskStarted(execution: TaskExecutionDTO): void;
$taskEnded(execution: TaskExecutionDTO): void;
}
export interface IFunctionBreakpointDto {
type: 'function';
export interface IBreakpointDto {
type: string;
id?: string;
enabled: boolean;
condition?: string;
hitCondition?: string;
logMessage?: string;
}
export interface IFunctionBreakpointDto extends IBreakpointDto {
type: 'function';
functionName: string;
}
export interface ISourceBreakpointDto {
export interface ISourceBreakpointDto extends IBreakpointDto {
type: 'source';
id?: string;
enabled: boolean;
condition?: string;
hitCondition?: string;
uri: UriComponents;
line: number;
character: number;
@@ -764,6 +817,7 @@ export interface ISourceMultiBreakpointDto {
enabled: boolean;
condition?: string;
hitCondition?: string;
logMessage?: string;
line: number;
character: number;
}[];
@@ -772,6 +826,11 @@ export interface ISourceMultiBreakpointDto {
// {{SQL CARBON EDIT}}
/*
export interface ExtHostDebugServiceShape {
$substituteVariables(folder: UriComponents | undefined, config: IConfig): TPromise<IConfig>;
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<void>;
$startDASession(handle: number, debugType: string, adapterExecutableInfo: IAdapterExecutable | null): TPromise<void>;
$stopDASession(handle: number): TPromise<void>;
$sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): TPromise<void>;
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): TPromise<IConfig>;
$provideDebugConfigurations(handle: number, folder: UriComponents | undefined): TPromise<IConfig[]>;
$debugAdapterExecutable(handle: number, folder: UriComponents | undefined): TPromise<IAdapterExecutable>;
@@ -805,6 +864,10 @@ export interface ExtHostLogServiceShape {
$setLevel(level: LogLevel);
}
export interface ExtHostProgressShape {
$acceptProgressCanceled(handle: number): void;
}
// --- proxy identifiers
export const MainContext = {
@@ -831,10 +894,12 @@ export const MainContext = {
MainThreadTelemetry: createMainId<MainThreadTelemetryShape>('MainThreadTelemetry'),
MainThreadTerminalService: createMainId<MainThreadTerminalServiceShape>('MainThreadTerminalService'),
MainThreadWebviews: createMainId<MainThreadWebviewsShape>('MainThreadWebviews'),
MainThreadUrls: createMainId<MainThreadUrlsShape>('MainThreadUrls'),
MainThreadWorkspace: createMainId<MainThreadWorkspaceShape>('MainThreadWorkspace'),
MainThreadFileSystem: createMainId<MainThreadFileSystemShape>('MainThreadFileSystem'),
MainThreadExtensionService: createMainId<MainThreadExtensionServiceShape>('MainThreadExtensionService'),
MainThreadSCM: createMainId<MainThreadSCMShape>('MainThreadSCM'),
MainThreadSearch: createMainId<MainThreadSearchShape>('MainThreadSearch'),
MainThreadTask: createMainId<MainThreadTaskShape>('MainThreadTask'),
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
};
@@ -861,8 +926,11 @@ export const ExtHostContext = {
ExtHostLogService: createExtId<ExtHostLogServiceShape>('ExtHostLogService'),
ExtHostTerminalService: createExtId<ExtHostTerminalServiceShape>('ExtHostTerminalService'),
ExtHostSCM: createExtId<ExtHostSCMShape>('ExtHostSCM'),
ExtHostSearch: createExtId<ExtHostSearchShape>('ExtHostSearch'),
ExtHostTask: createExtId<ExtHostTaskShape>('ExtHostTask'),
ExtHostWorkspace: createExtId<ExtHostWorkspaceShape>('ExtHostWorkspace'),
ExtHostWindow: createExtId<ExtHostWindowShape>('ExtHostWindow'),
ExtHostWebviews: createExtId<ExtHostWebviewsShape>('ExtHostWebviews')
ExtHostWebviews: createExtId<ExtHostWebviewsShape>('ExtHostWebviews'),
ExtHostUrls: createExtId<ExtHostUrlsShape>('ExtHostUrls'),
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress')
};

View File

@@ -10,25 +10,30 @@ 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 { IRawColorInfo } from 'vs/workbench/api/node/extHost.protocol';
import { ISingleEditOperation } from 'vs/editor/common/model';
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 { Position as EditorPosition, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { CustomCodeAction } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { ExtHostTask } from './extHostTask';
import { ICommandsExecutor, PreviewHTMLAPICommand, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand } from './apiCommands';
export class ExtHostApiCommands {
static register(commands: ExtHostCommands) {
return new ExtHostApiCommands(commands).registerCommands();
static register(commands: ExtHostCommands, workspace: ExtHostTask) {
return new ExtHostApiCommands(commands, workspace).registerCommands();
}
private _commands: ExtHostCommands;
private _tasks: ExtHostTask;
private _disposables: IDisposable[] = [];
private constructor(commands: ExtHostCommands) {
private constructor(commands: ExtHostCommands, task: ExtHostTask) {
this._commands = commands;
this._tasks = task;
}
registerCommands() {
@@ -46,6 +51,14 @@ export class ExtHostApiCommands {
],
returns: 'A promise that resolves to an array of Location-instances.'
});
this._register('vscode.executeTypeDefinitionProvider', this._executeTypeDefinitionProvider, {
description: 'Execute all type definition 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-instances.'
});
this._register('vscode.executeImplementationProvider', this._executeImplementationProvider, {
description: 'Execute all implementation providers.',
args: [
@@ -108,7 +121,8 @@ export class ExtHostApiCommands {
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' }
{ name: 'triggerCharacter', description: '(optional) Trigger completion when the user types the character, like `,` or `(`', constraint: value => value === void 0 || typeof value === 'string' },
{ name: 'itemResolveCount', description: '(optional) Number of completions to resolve (too large numbers slow down completions)', constraint: value => value === void 0 || typeof value === 'number' }
],
returns: 'A promise that resolves to a CompletionList-instance.'
});
@@ -123,7 +137,8 @@ export class ExtHostApiCommands {
this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider, {
description: 'Execute CodeLens provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI }
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'itemResolveCount', description: '(optional) Number of lenses that should be resolved and returned. Will only retrun resolved lenses, will impact performance)', constraint: value => value === void 0 || typeof value === 'number' }
],
returns: 'A promise that resolves to an array of CodeLens-instances.'
});
@@ -161,99 +176,94 @@ export class ExtHostApiCommands {
],
returns: 'A promise that resolves to an array of DocumentLink-instances.'
});
this._register('vscode.executeTaskProvider', this._executeTaskProvider, {
description: 'Execute task provider',
args: [],
returns: 'An array of task handles'
});
this._register('vscode.executeDocumentColorProvider', this._executeDocumentColorProvider, {
description: 'Execute document color provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
],
returns: 'A promise that resolves to an array of ColorInformation objects.'
});
this._register('vscode.executeColorPresentationProvider', this._executeColorPresentationProvider, {
description: 'Execute color presentation provider.',
args: [
{ name: 'color', description: 'The color to show and insert', constraint: types.Color },
{ name: 'context', description: 'Context object with uri and range' }
],
returns: 'A promise that resolves to an array of ColorPresentation objects.'
});
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.
// -----------------------------------------------------------------
// The following commands are registered on both sides separately.
//
// We are trying to maintain backwards compatibility for cases where
// API commands are encoded as markdown links, for example.
// -----------------------------------------------------------------
type ICommandHandler = (...args: any[]) => any;
const adjustHandler = (handler: (executor: ICommandsExecutor, ...args: any[]) => any): ICommandHandler => {
return (...args: any[]) => {
return handler(this._commands, ...args);
};
};
this._register(PreviewHTMLAPICommand.ID, adjustHandler(PreviewHTMLAPICommand.execute), {
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 integration 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' }
]
});
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);
}
this._register(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute), {
description: 'Open a folder or workspace 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/workspace unless the newWindow parameter is set to true.',
args: [
{ name: 'uri', description: '(optional) Uri of the folder or workspace file 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/workspace in a new window or the same. Defaults to opening in the same window.', constraint: value => value === void 0 || typeof value === 'boolean' }
]
});
return this._commands.executeCommand('_files.windowOpen', [uri.fsPath], forceNewWindow);
}, {
description: 'Open a folder or workspace 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/workspace unless the newWindow parameter is set to true.',
args: [
{ name: 'uri', description: '(optional) Uri of the folder or workspace file 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/workspace in a new window or the same. Defaults to opening in the same window.', constraint: value => value === void 0 || typeof value === 'boolean' }
]
});
this._register(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute), {
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.diff', (left: URI, right: URI, label: string, options?: vscode.TextDocumentShowOptions) => {
return this._commands.executeCommand('_workbench.diff', [
left, right,
label,
undefined,
typeConverters.toTextEditorOptions(options),
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(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute), {
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: 'columnOrOptions', description: '(optional) Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', constraint: v => v === void 0 || typeof v === 'number' || typeof v === 'object' }
]
});
this._register('vscode.open', (resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions) => {
let options: ITextEditorOptions;
let column: EditorPosition;
if (columnOrOptions) {
if (typeof columnOrOptions === 'number') {
column = typeConverters.fromViewColumn(columnOrOptions);
} else {
options = typeConverters.toTextEditorOptions(columnOrOptions);
column = typeConverters.fromViewColumn(columnOrOptions.viewColumn);
}
}
return this._commands.executeCommand('_workbench.open', [
resource,
options,
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: 'columnOrOptions', description: '(optional) Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', constraint: v => v === void 0 || typeof v === 'number' || typeof v === 'object' }
]
});
this._register('vscode.removeFromRecentlyOpened', (path: string) => {
return this._commands.executeCommand('_workbench.removeFromRecentlyOpened', path);
}, {
description: 'Removes an entry with the given path from the recently opened list.',
args: [
{ name: 'path', description: 'Path to remove from recently opened.', constraint: value => typeof value === 'string' }
]
});
this._register(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute), {
description: 'Removes an entry with the given path from the recently opened list.',
args: [
{ name: 'path', description: 'Path to remove from recently opened.', constraint: value => typeof value === 'string' }
]
});
}
// --- command impl
private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void {
let disposable = this._commands.registerCommand(id, handler, this, description);
let disposable = this._commands.registerCommand(false, id, handler, this, description);
this._disposables.push(disposable);
}
@@ -280,12 +290,17 @@ export class ExtHostApiCommands {
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;
});
return this._commands.executeCommand<modes.Location[]>('_executeDefinitionProvider', args)
.then(tryMapWith(typeConverters.location.to));
}
private _executeTypeDefinitionProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
const args = {
resource,
position: position && typeConverters.fromPosition(position)
};
return this._commands.executeCommand<modes.Location[]>('_executeTypeDefinitionProvider', args)
.then(tryMapWith(typeConverters.location.to));
}
private _executeImplementationProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
@@ -293,12 +308,8 @@ export class ExtHostApiCommands {
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;
});
return this._commands.executeCommand<modes.Location[]>('_executeImplementationProvider', args)
.then(tryMapWith(typeConverters.location.to));
}
private _executeHoverProvider(resource: URI, position: types.Position): Thenable<types.Hover[]> {
@@ -306,12 +317,8 @@ export class ExtHostApiCommands {
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;
});
return this._commands.executeCommand<modes.Hover[]>('_executeHoverProvider', args)
.then(tryMapWith(typeConverters.toHover));
}
private _executeDocumentHighlights(resource: URI, position: types.Position): Thenable<types.DocumentHighlight[]> {
@@ -319,12 +326,8 @@ export class ExtHostApiCommands {
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;
});
return this._commands.executeCommand<modes.DocumentHighlight[]>('_executeDocumentHighlights', args)
.then(tryMapWith(typeConverters.toDocumentHighlight));
}
private _executeReferenceProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
@@ -332,12 +335,8 @@ export class ExtHostApiCommands {
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;
});
return this._commands.executeCommand<modes.Location[]>('_executeReferenceProvider', args)
.then(tryMapWith(typeConverters.location.to));
}
private _executeDocumentRenameProvider(resource: URI, position: types.Position, newName: string): Thenable<types.WorkspaceEdit> {
@@ -371,11 +370,12 @@ export class ExtHostApiCommands {
});
}
private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string): Thenable<types.CompletionList> {
private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string, maxItemsToResolve: number): Thenable<types.CompletionList> {
const args = {
resource,
position: position && typeConverters.fromPosition(position),
triggerCharacter
triggerCharacter,
maxItemsToResolve
};
return this._commands.executeCommand<modes.ISuggestResult>('_executeCompletionItemProvider', args).then(result => {
if (result) {
@@ -387,6 +387,32 @@ export class ExtHostApiCommands {
});
}
private _executeDocumentColorProvider(resource: URI): Thenable<types.ColorInformation[]> {
const args = {
resource
};
return this._commands.executeCommand<IRawColorInfo[]>('_executeDocumentColorProvider', args).then(result => {
if (result) {
return result.map(ci => ({ range: typeConverters.toRange(ci.range), color: typeConverters.Color.to(ci.color) }));
}
return [];
});
}
private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range }): Thenable<types.ColorPresentation[]> {
const args = {
resource: context.uri,
color: typeConverters.Color.from(color),
range: typeConverters.fromRange(context.range),
};
return this._commands.executeCommand<modes.IColorPresentation[]>('_executeColorPresentationProvider', args).then(result => {
if (result) {
return result.map(typeConverters.ColorPresentation.to);
}
return [];
});
}
private _executeDocumentSymbolProvider(resource: URI): Thenable<types.SymbolInformation[]> {
const args = {
resource
@@ -404,11 +430,8 @@ export class ExtHostApiCommands {
resource,
range: typeConverters.fromRange(range)
};
return this._commands.executeCommand<CustomCodeAction[]>('_executeCodeActionProvider', args).then(value => {
if (!Array.isArray(value)) {
return undefined;
}
return value.map(codeAction => {
return this._commands.executeCommand<CustomCodeAction[]>('_executeCodeActionProvider', args)
.then(tryMapWith(codeAction => {
if (codeAction._isSynthetic) {
return this._commands.converter.fromInternal(codeAction.command);
} else {
@@ -424,22 +447,18 @@ export class ExtHostApiCommands {
}
return ret;
}
});
});
}));
}
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 _executeCodeLensProvider(resource: URI, itemResolveCount: number): Thenable<vscode.CodeLens[]> {
const args = { resource, itemResolveCount };
return this._commands.executeCommand<modes.ICodeLensSymbol[]>('_executeCodeLensProvider', args)
.then(tryMapWith(item => {
return new types.CodeLens(
typeConverters.toRange(item.range),
this._commands.converter.fromInternal(item.command));
}));
}
private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
@@ -447,12 +466,8 @@ export class ExtHostApiCommands {
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;
});
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatDocumentProvider', args)
.then(tryMapWith(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text)));
}
private _executeFormatRangeProvider(resource: URI, range: types.Range, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
@@ -461,12 +476,8 @@ export class ExtHostApiCommands {
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;
});
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatRangeProvider', args)
.then(tryMapWith(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text)));
}
private _executeFormatOnTypeProvider(resource: URI, position: types.Position, ch: string, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
@@ -476,20 +487,25 @@ export class ExtHostApiCommands {
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;
});
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatOnTypeProvider', args)
.then(tryMapWith(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text)));
}
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;
});
return this._commands.executeCommand<modes.ILink[]>('_executeLinkProvider', resource)
.then(tryMapWith(typeConverters.DocumentLink.to));
}
private _executeTaskProvider(): Thenable<vscode.Task[]> {
return this._tasks.fetchTasks();
}
}
function tryMapWith<T, R>(f: (x: T) => R) {
return (value: T[]) => {
if (Array.isArray(value)) {
return value.map(f);
}
return undefined;
};
}

View File

@@ -54,7 +54,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
this._argumentProcessors.push(processor);
}
registerCommand(id: string, callback: <T>(...args: any[]) => T | Thenable<T>, thisArg?: any, description?: ICommandHandlerDescription): extHostTypes.Disposable {
registerCommand(global: boolean, id: string, callback: <T>(...args: any[]) => T | Thenable<T>, thisArg?: any, description?: ICommandHandlerDescription): extHostTypes.Disposable {
this._logService.trace('ExtHostCommands#registerCommand', id);
if (!id.trim().length) {
@@ -66,11 +66,15 @@ export class ExtHostCommands implements ExtHostCommandsShape {
}
this._commands.set(id, { callback, thisArg, description });
this._proxy.$registerCommand(id);
if (global) {
this._proxy.$registerCommand(id);
}
return new extHostTypes.Disposable(() => {
if (this._commands.delete(id)) {
this._proxy.$unregisterCommand(id);
if (global) {
this._proxy.$unregisterCommand(id);
}
}
});
}
@@ -112,6 +116,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
try {
validateConstraint(args[i], description.args[i].constraint);
} catch (err) {
// {{ SQL CARBON EDIT }} - Add type assertion to fix build break
return <any>Promise.reject(new Error(`Running the contributed command:'${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`));
}
}
@@ -122,12 +127,14 @@ export class ExtHostCommands implements ExtHostCommandsShape {
return Promise.resolve(result);
} catch (err) {
this._logService.error(err, id);
// {{ SQL CARBON EDIT }} - Add type assertion to fix build break
return <any>Promise.reject(new Error(`Running the contributed command:'${id}' failed.`));
}
}
$executeContributedCommand<T>(id: string, ...args: any[]): Thenable<T> {
if (!this._commands.has(id)) {
// {{ SQL CARBON EDIT }} - Add type assertion to fix build break
return <any>Promise.reject(new Error(`Contributed command '${id}' does not exist.`));
} else {
args = args.map(arg => this._argumentProcessors.reduce((r, p) => p.processArgument(r), arg));
@@ -161,15 +168,16 @@ export class ExtHostCommands implements ExtHostCommandsShape {
export class CommandsConverter {
private readonly _delegatingCommandId: string;
private _commands: ExtHostCommands;
private _heap: ExtHostHeapService;
// --- conversion between internal and api commands
constructor(commands: ExtHostCommands, heap: ExtHostHeapService) {
this._delegatingCommandId = `_internal_command_delegation_${Date.now()}`;
this._commands = commands;
this._heap = heap;
this._commands.registerCommand('_internal_command_delegation', this._executeConvertedCommand, this);
this._commands.registerCommand(true, this._delegatingCommandId, this._executeConvertedCommand, this);
}
toInternal(command: vscode.Command): modes.Command {
@@ -190,7 +198,7 @@ export class CommandsConverter {
const id = this._heap.keep(command);
ObjectIdentifier.mixin(result, id);
result.id = '_internal_command_delegation';
result.id = this._delegatingCommandId;
result.arguments = [id];
}

View File

@@ -6,16 +6,19 @@
import { mixin, deepClone } from 'vs/base/common/objects';
import URI from 'vs/base/common/uri';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import * as vscode from 'vscode';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostConfigurationShape, MainThreadConfigurationShape, IWorkspaceConfigurationChangeEventData, IConfigurationInitData } from './extHost.protocol';
import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes';
import { IConfigurationData, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { Configuration, ConfigurationChangeEvent, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { WorkspaceConfigurationChangeEvent } from 'vs/workbench/services/configuration/common/configurationModels';
import { StrictResourceMap } from 'vs/base/common/map';
import { ResourceMap } from 'vs/base/common/map';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { isObject } from 'vs/base/common/types';
declare var Proxy: any; // TODO@TypeScript
function lookUp(tree: any, key: string) {
if (key) {
@@ -47,7 +50,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationInitData) {
this._proxy = proxy;
this._extHostWorkspace = extHostWorkspace;
this._configuration = Configuration.parse(data);
this._configuration = ExtHostConfiguration.parse(data);
this._configurationScopes = data.configurationScopes;
}
@@ -56,12 +59,12 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
}
$acceptConfigurationChanged(data: IConfigurationData, eventData: IWorkspaceConfigurationChangeEventData) {
this._configuration = Configuration.parse(data);
this._configuration = ExtHostConfiguration.parse(data);
this._onDidChangeConfiguration.fire(this._toConfigurationChangeEvent(eventData));
}
getConfiguration(section?: string, resource?: URI, extensionId?: string): vscode.WorkspaceConfiguration {
const config = deepClone(section
const config = this._toReadonlyValue(section
? lookUp(this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace), section)
: this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace));
@@ -93,6 +96,49 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
let result = lookUp(config, key);
if (typeof result === 'undefined') {
result = defaultValue;
} else {
let clonedConfig = void 0;
const cloneOnWriteProxy = (target: any, accessor: string): any => {
let clonedTarget = void 0;
const cloneTarget = () => {
clonedConfig = clonedConfig ? clonedConfig : deepClone(config);
clonedTarget = clonedTarget ? clonedTarget : lookUp(clonedConfig, accessor);
};
return isObject(target) ?
new Proxy(target, {
get: (target: any, property: string) => {
if (typeof property === 'string' && property.toLowerCase() === 'tojson') {
cloneTarget();
return () => clonedTarget;
}
if (clonedConfig) {
clonedTarget = clonedTarget ? clonedTarget : lookUp(clonedConfig, accessor);
return clonedTarget[property];
}
const result = target[property];
if (typeof property === 'string') {
return cloneOnWriteProxy(result, `${accessor}.${property}`);
}
return result;
},
set: (target: any, property: string, value: any) => {
cloneTarget();
clonedTarget[property] = value;
return true;
},
deleteProperty: (target: any, property: string) => {
cloneTarget();
delete clonedTarget[property];
return true;
},
defineProperty: (target: any, property: string, descriptor: any) => {
cloneTarget();
Object.defineProperty(clonedTarget, property, descriptor);
return true;
}
}) : target;
};
result = cloneOnWriteProxy(result, key);
}
return result;
},
@@ -128,6 +174,22 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
return <vscode.WorkspaceConfiguration>Object.freeze(result);
}
private _toReadonlyValue(result: any): any {
const readonlyProxy = (target) => {
return isObject(target) ?
new Proxy(target, {
get: (target: any, property: string) => readonlyProxy(target[property]),
set: (target: any, property: string, value: any) => { throw new Error(`TypeError: Cannot assign to read only property '${property}' of object`); },
deleteProperty: (target: any, property: string) => { throw new Error(`TypeError: Cannot delete read only property '${property}' of object`); },
defineProperty: (target: any, property: string) => { throw new Error(`TypeError: Cannot define property '${property}' for a readonly object`); },
setPrototypeOf: (target: any) => { throw new Error(`TypeError: Cannot set prototype for a readonly object`); },
isExtensible: () => false,
preventExtensions: () => true
}) : target;
};
return readonlyProxy(result);
}
private _validateConfigurationAccess(key: string, resource: URI, extensionId: string): void {
const scope = this._configurationScopes[key];
const extensionIdText = extensionId ? `[${extensionId}] ` : '';
@@ -147,7 +209,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
private _toConfigurationChangeEvent(data: IWorkspaceConfigurationChangeEventData): vscode.ConfigurationChangeEvent {
const changedConfiguration = new ConfigurationModel(data.changedConfiguration.contents, data.changedConfiguration.keys, data.changedConfiguration.overrides);
const changedConfigurationByResource: StrictResourceMap<ConfigurationModel> = new StrictResourceMap<ConfigurationModel>();
const changedConfigurationByResource: ResourceMap<ConfigurationModel> = new ResourceMap<ConfigurationModel>();
for (const key of Object.keys(data.changedConfigurationByResource)) {
const resource = URI.parse(key);
const model = data.changedConfigurationByResource[key];
@@ -158,4 +220,19 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
affectsConfiguration: (section: string, resource?: URI) => event.affectsConfiguration(section, resource)
});
}
private static parse(data: IConfigurationData): Configuration {
const defaultConfiguration = ExtHostConfiguration.parseConfigurationModel(data.defaults);
const userConfiguration = ExtHostConfiguration.parseConfigurationModel(data.user);
const workspaceConfiguration = ExtHostConfiguration.parseConfigurationModel(data.workspace);
const folders: ResourceMap<ConfigurationModel> = Object.keys(data.folders).reduce((result, key) => {
result.set(URI.parse(key), ExtHostConfiguration.parseConfigurationModel(data.folders[key]));
return result;
}, new ResourceMap<ConfigurationModel>());
return new Configuration(defaultConfiguration, userConfiguration, workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap<ConfigurationModel>(), false);
}
private static parseConfigurationModel(model: IConfigurationModel): ConfigurationModel {
return new ConfigurationModel(model.contents, model.keys, model.overrides).freeze();
}
}

View File

@@ -7,43 +7,49 @@
// {{SQL CARBON EDIT}}
/*
import { TPromise } from 'vs/base/common/winjs.base';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { asWinJsPromise } from 'vs/base/common/async';
import {
MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID,
IMainContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto
} from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import * as vscode from 'vscode';
import URI, { UriComponents } from 'vs/base/common/uri';
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint } from 'vs/workbench/api/node/extHostTypes';
import { generateUuid } from 'vs/base/common/uuid';
import { DebugAdapter, convertToVSCPaths, convertToDAPaths } from 'vs/workbench/parts/debug/node/debugAdapter';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { IAdapterExecutable, ITerminalSettings, IDebuggerContribution, IConfig } from 'vs/workbench/parts/debug/common/debug';
import { getTerminalLauncher } from 'vs/workbench/parts/debug/node/terminals';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { VariableResolver } from 'vs/workbench/services/configurationResolver/node/variableResolver';
import { IConfigurationResolverService } from '../../services/configurationResolver/common/configurationResolver';
import { IStringDictionary } from 'vs/base/common/collections';
import { ExtHostConfiguration } from './extHostConfiguration';
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>;
private readonly _onDidStartDebugSession: Emitter<vscode.DebugSession>;
get onDidStartDebugSession(): Event<vscode.DebugSession> { return this._onDidStartDebugSession.event; }
private _onDidTerminateDebugSession: Emitter<vscode.DebugSession>;
private readonly _onDidTerminateDebugSession: Emitter<vscode.DebugSession>;
get onDidTerminateDebugSession(): Event<vscode.DebugSession> { return this._onDidTerminateDebugSession.event; }
private _onDidChangeActiveDebugSession: Emitter<vscode.DebugSession | undefined>;
private readonly _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>;
private readonly _onDidReceiveDebugSessionCustomEvent: Emitter<vscode.DebugSessionCustomEvent>;
get onDidReceiveDebugSessionCustomEvent(): Event<vscode.DebugSessionCustomEvent> { return this._onDidReceiveDebugSessionCustomEvent.event; }
private _activeDebugConsole: ExtHostDebugConsole;
@@ -52,12 +58,19 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _breakpoints: Map<string, vscode.Breakpoint>;
private _breakpointEventsActive: boolean;
private _onDidChangeBreakpoints: Emitter<vscode.BreakpointsChangeEvent>;
private readonly _onDidChangeBreakpoints: Emitter<vscode.BreakpointsChangeEvent>;
private _debugAdapters: Map<number, DebugAdapter>;
private _variableResolver: IConfigurationResolverService;
constructor(mainContext: IMainContext, workspace: ExtHostWorkspace) {
this._workspace = workspace;
constructor(mainContext: IMainContext,
private _workspace: ExtHostWorkspace,
private _extensionService: ExtHostExtensionService,
private _editorsService: ExtHostDocumentsAndEditors,
private _configurationService: ExtHostConfiguration
) {
this._handleCounter = 0;
this._handlers = new Map<number, vscode.DebugConfigurationProvider>();
@@ -79,6 +92,86 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._breakpoints = new Map<string, vscode.Breakpoint>();
this._breakpointEventsActive = false;
this._debugAdapters = new Map<number, DebugAdapter>();
// register all debug extensions
const debugTypes: string[] = [];
for (const ed of this._extensionService.getAllExtensionDescriptions()) {
if (ed.contributes) {
const debuggers = <IDebuggerContribution[]>ed.contributes['debuggers'];
if (debuggers && debuggers.length > 0) {
for (const dbg of debuggers) {
// only debugger contributions with a "label" are considered a "main" debugger contribution
if (dbg.type && dbg.label) {
debugTypes.push(dbg.type);
}
}
}
}
}
if (debugTypes.length > 0) {
this._debugServiceProxy.$registerDebugTypes(debugTypes);
}
}
public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<void> {
const terminalLauncher = getTerminalLauncher();
if (terminalLauncher) {
return terminalLauncher.runInTerminal(args, config);
}
return void 0;
}
public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): TPromise<IConfig> {
if (!this._variableResolver) {
this._variableResolver = new ExtHostVariableResolverService(this._workspace, this._editorsService, this._configurationService);
}
const folder = <IWorkspaceFolder>this.getFolder(folderUri);
return asWinJsPromise(token => DebugAdapter.substituteVariables(folder, config, this._variableResolver));
}
public $startDASession(handle: number, debugType: string, adpaterExecutable: IAdapterExecutable | null): TPromise<void> {
const mythis = this;
const da = new class extends DebugAdapter {
// DA -> VS Code
public acceptMessage(message: DebugProtocol.ProtocolMessage) {
convertToVSCPaths(message, source => {
if (paths.isAbsolute(source.path)) {
(<any>source).path = URI.file(source.path);
}
});
mythis._debugServiceProxy.$acceptDAMessage(handle, message);
}
}(debugType, adpaterExecutable, this._extensionService.getAllExtensionDescriptions());
this._debugAdapters.set(handle, da);
da.onError(err => this._debugServiceProxy.$acceptDAError(handle, err.name, err.message, err.stack));
da.onExit(code => this._debugServiceProxy.$acceptDAExit(handle, code, null));
return da.startSession();
}
public $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): TPromise<void> {
// VS Code -> DA
convertToDAPaths(message, source => {
if (typeof source.path === 'object') {
source.path = URI.revive(source.path).fsPath;
}
});
const da = this._debugAdapters.get(handle);
if (da) {
da.sendMessage(message);
}
return void 0;
}
public $stopDASession(handle: number): TPromise<void> {
const da = this._debugAdapters.get(handle);
this._debugAdapters.delete(handle);
return da ? da.stopSession() : void 0;
}
private startBreakpoints() {
@@ -113,18 +206,15 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
if (!this._breakpoints.has(bpd.id)) {
let bp: vscode.Breakpoint;
if (bpd.type === 'function') {
bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition);
bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage);
} else {
const uri = URI.revive(bpd.uri);
bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition);
bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage);
}
bp['_id'] = bpd.id;
this._breakpoints.set(bpd.id, bp);
a.push(bp);
}
}
}
@@ -142,17 +232,20 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
for (const bpd of delta.changed) {
let bp = this._breakpoints.get(bpd.id);
if (bp) {
if (bpd.type === 'function') {
if (bp instanceof FunctionBreakpoint && bpd.type === 'function') {
const fbp = <any>bp;
fbp.enabled = bpd.enabled;
fbp.condition = bpd.condition;
fbp.hitCondition = bpd.hitCondition;
fbp.logMessage = bpd.logMessage;
fbp.functionName = bpd.functionName;
} else {
} else if (bp instanceof SourceBreakpoint && bpd.type === 'source') {
const sbp = <any>bp;
sbp.enabled = bpd.enabled;
sbp.condition = bpd.condition;
sbp.hitCondition = bpd.hitCondition;
sbp.logMessage = bpd.logMessage;
sbp.location = new Location(URI.revive(bpd.uri), new Position(bpd.line, bpd.character));
}
c.push(bp);
}
@@ -207,6 +300,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
enabled: bp.enabled,
condition: bp.condition,
hitCondition: bp.hitCondition,
logMessage: bp.logMessage,
line: bp.location.range.start.line,
character: bp.location.range.start.character
});
@@ -215,9 +309,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
type: 'function',
id: bp['_id'],
enabled: bp.enabled,
functionName: bp.functionName,
hitCondition: bp.hitCondition,
condition: bp.condition
logMessage: bp.logMessage,
condition: bp.condition,
functionName: bp.functionName
});
}
}

View File

@@ -43,6 +43,10 @@ export class ExtHostDecorations implements ExtHostDecorationsShape {
return TPromise.join(requests.map(request => {
const { handle, uri, id } = request;
const provider = this._provider.get(handle);
if (!provider) {
// might have been unregistered in the meantime
return void 0;
}
return asWinJsPromise(token => provider.provideDecoration(URI.revive(uri), token)).then(data => {
result[id] = data && <DecorationData>[data.priority, data.bubble, data.title, data.abbreviation, data.color, data.source];
}, err => {

View File

@@ -5,31 +5,36 @@
'use strict';
import { localize } from 'vs/nls';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { IMarkerData, MarkerSeverity } 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 * as converter from './extHostTypeConverters';
import { mergeSort } from 'vs/base/common/arrays';
import { Event, Emitter, debounceEvent, mapEvent } from 'vs/base/common/event';
import { keys } from 'vs/base/common/map';
export class DiagnosticCollection implements vscode.DiagnosticCollection {
private static readonly _maxDiagnosticsPerFile: number = 250;
private readonly _name: string;
private readonly _onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>;
private _proxy: MainThreadDiagnosticsShape;
private _isDisposed = false;
private _data = new Map<string, vscode.Diagnostic[]>();
constructor(name: string, proxy: MainThreadDiagnosticsShape) {
constructor(name: string, proxy: MainThreadDiagnosticsShape, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) {
this._name = name;
this._proxy = proxy;
this._onDidChangeDiagnostics = onDidChangeDiagnostics;
}
dispose(): void {
if (!this._isDisposed) {
this._onDidChangeDiagnostics.fire(keys(this._data));
this._proxy.$clear(this.name);
this._proxy = undefined;
this._data = undefined;
@@ -111,7 +116,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
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));
const len = marker.push(converter.fromDiagnostic(diagnostic));
if (len === DiagnosticCollection._maxDiagnosticsPerFile) {
break orderLoop;
}
@@ -121,7 +126,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
// add 'signal' marker for showing omitted errors/warnings
marker.push({
severity: Severity.Error,
severity: MarkerSeverity.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,
@@ -129,13 +134,14 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
endColumn: marker[marker.length - 1].endColumn
});
} else {
marker = diagnostics.map(DiagnosticCollection.toMarkerData);
marker = diagnostics.map(converter.fromDiagnostic);
}
}
entries.push([uri, marker]);
}
this._onDidChangeDiagnostics.fire(toSync);
this._proxy.$changeMany(this.name, entries);
}
@@ -147,6 +153,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
clear(): void {
this._checkDisposed();
this._onDidChangeDiagnostics.fire(keys(this._data));
this._data.clear();
this._proxy.$clear(this.name);
}
@@ -179,32 +186,6 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
}
}
public 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;
@@ -220,12 +201,42 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
private static _idPool: number = 0;
private _proxy: MainThreadDiagnosticsShape;
private _collections: DiagnosticCollection[];
private readonly _proxy: MainThreadDiagnosticsShape;
private readonly _collections: DiagnosticCollection[] = [];
private readonly _onDidChangeDiagnostics = new Emitter<(vscode.Uri | string)[]>();
static _debouncer(last: (vscode.Uri | string)[], current: (vscode.Uri | string)[]): (vscode.Uri | string)[] {
if (!last) {
return current;
} else {
return last.concat(current);
}
}
static _mapper(last: (vscode.Uri | string)[]): { uris: vscode.Uri[] } {
let uris: vscode.Uri[] = [];
let map = new Set<string>();
for (const uri of last) {
if (typeof uri === 'string') {
if (!map.has(uri)) {
map.add(uri);
uris.push(URI.parse(uri));
}
} else {
if (!map.has(uri.toString())) {
map.add(uri.toString());
uris.push(uri);
}
}
}
Object.freeze(uris);
return { uris };
}
readonly onDidChangeDiagnostics: Event<vscode.DiagnosticChangeEvent> = mapEvent(debounceEvent(this._onDidChangeDiagnostics.event, ExtHostDiagnostics._debouncer, 50), ExtHostDiagnostics._mapper);
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadDiagnostics);
this._collections = [];
}
createDiagnosticCollection(name: string): vscode.DiagnosticCollection {
@@ -233,10 +244,10 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
name = '_generated_diagnostic_collection_name_#' + ExtHostDiagnostics._idPool++;
}
const { _collections, _proxy } = this;
const { _collections, _proxy, _onDidChangeDiagnostics } = this;
const result = new class extends DiagnosticCollection {
constructor() {
super(name, _proxy);
super(name, _proxy, _onDidChangeDiagnostics);
_collections.push(this);
}
dispose() {
@@ -251,7 +262,36 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
return result;
}
forEach(callback: (collection: DiagnosticCollection) => any): void {
this._collections.forEach(callback);
getDiagnostics(resource: vscode.Uri): vscode.Diagnostic[];
getDiagnostics(): [vscode.Uri, vscode.Diagnostic[]][];
getDiagnostics(resource?: vscode.Uri): vscode.Diagnostic[] | [vscode.Uri, vscode.Diagnostic[]][] {
if (resource) {
return this._getDiagnostics(resource);
} else {
let index = new Map<string, number>();
let res: [vscode.Uri, vscode.Diagnostic[]][] = [];
for (const collection of this._collections) {
collection.forEach((uri, diagnostics) => {
let idx = index.get(uri.toString());
if (typeof idx === 'undefined') {
idx = res.length;
index.set(uri.toString(), idx);
res.push([uri, []]);
}
res[idx][1] = res[idx][1].concat(...diagnostics);
});
}
return res;
}
}
private _getDiagnostics(resource: vscode.Uri): vscode.Diagnostic[] {
let res: vscode.Diagnostic[] = [];
for (const collection of this._collections) {
if (collection.has(resource)) {
res = res.concat(collection.get(resource));
}
}
return res;
}
}

View File

@@ -14,6 +14,7 @@ import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, ExtHostDocumentContentProvidersShape, MainThreadDocumentContentProvidersShape, IMainContext } from './extHost.protocol';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
import { Schemas } from 'vs/base/common/network';
import { ILogService } from 'vs/platform/log/common/log';
export class ExtHostDocumentContentProvider implements ExtHostDocumentContentProvidersShape {
@@ -21,11 +22,13 @@ export class ExtHostDocumentContentProvider implements ExtHostDocumentContentPro
private readonly _documentContentProviders = new Map<number, vscode.TextDocumentContentProvider>();
private readonly _proxy: MainThreadDocumentContentProvidersShape;
private readonly _documentsAndEditors: ExtHostDocumentsAndEditors;
constructor(mainContext: IMainContext, documentsAndEditors: ExtHostDocumentsAndEditors) {
constructor(
mainContext: IMainContext,
private readonly _documentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _logService: ILogService,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadDocumentContentProviders);
this._documentsAndEditors = documentsAndEditors;
}
dispose(): void {
@@ -47,6 +50,10 @@ export class ExtHostDocumentContentProvider implements ExtHostDocumentContentPro
let subscription: IDisposable;
if (typeof provider.onDidChange === 'function') {
subscription = provider.onDidChange(uri => {
if (uri.scheme !== scheme) {
this._logService.warn(`Provider for scheme '${scheme}' is firing event for schema '${uri.scheme}' which will be IGNORED`);
return;
}
if (this._documentsAndEditors.getDocument(uri.toString())) {
this.$provideTextDocumentContent(handle, uri).then(value => {

View File

@@ -13,6 +13,7 @@ import * as vscode from 'vscode';
import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper';
import { MainThreadDocumentsShape } from './extHost.protocol';
import { TPromise } from 'vs/base/common/winjs.base';
import { Schemas } from 'vs/base/common/network';
const _modeId2WordDefinition = new Map<string, RegExp>();
export function setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void {
@@ -68,9 +69,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
this._document = {
get uri() { return data._uri; },
get fileName() { return data._uri.fsPath; },
// todo@remote
// documents from other fs-provider must not be untitled
get isUntitled() { return data._uri.scheme !== 'file'; },
get isUntitled() { return data._uri.scheme === Schemas.untitled; },
get languageId() { return data._languageId; },
get version() { return data._versionId; },
get isClosed() { return data._isDisposed; },

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event from 'vs/base/common/event';
import { Event } from 'vs/base/common/event';
import URI, { UriComponents } from 'vs/base/common/uri';
import { sequence, always } from 'vs/base/common/async';
import { illegalState } from 'vs/base/common/errors';

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import URI, { UriComponents } from 'vs/base/common/uri';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as TypeConverters from './extHostTypeConverters';
@@ -137,6 +137,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
contentChanges: events.changes.map((change) => {
return {
range: TypeConverters.toRange(change.range),
rangeOffset: change.rangeOffset,
rangeLength: change.rangeLength,
text: change.text
};

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
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';
@@ -12,7 +12,6 @@ import { ExtHostTextEditor } from './extHostTextEditor';
import * as assert from 'assert';
import * as typeConverters from './extHostTypeConverters';
import URI from 'vs/base/common/uri';
import { ExtHostWebview, ExtHostWebviews } from './extHostWebview';
import { Disposable } from './extHostTypes';
export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape {
@@ -20,7 +19,6 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
private _disposables: Disposable[] = [];
private _activeEditorId: string;
private _activeWebview: ExtHostWebview;
private readonly _editors = new Map<string, ExtHostTextEditor>();
private readonly _documents = new Map<string, ExtHostDocumentData>();
@@ -29,31 +27,15 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
private readonly _onDidRemoveDocuments = new Emitter<ExtHostDocumentData[]>();
private readonly _onDidChangeVisibleTextEditors = new Emitter<ExtHostTextEditor[]>();
private readonly _onDidChangeActiveTextEditor = new Emitter<ExtHostTextEditor>();
private readonly _onDidChangeActiveEditor = new Emitter<ExtHostTextEditor | ExtHostWebview>();
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;
readonly onDidChangeActiveEditor: Event<ExtHostTextEditor | ExtHostWebview> = this._onDidChangeActiveEditor.event;
constructor(
private readonly _mainContext: IMainContext,
_extHostWebviews?: ExtHostWebviews
) {
if (_extHostWebviews) {
_extHostWebviews.onDidChangeActiveWebview(webview => {
if (webview) {
if (webview !== this._activeWebview) {
this._onDidChangeActiveEditor.fire(webview);
this._activeWebview = webview;
}
} else {
this._activeWebview = webview;
}
}, this, this._disposables);
}
}
) { }
dispose() {
this._disposables = dispose(this._disposables);
@@ -143,9 +125,6 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
}
if (delta.newActiveEditor !== undefined) {
this._onDidChangeActiveTextEditor.fire(this.activeEditor());
const activeEditor = this.activeEditor();
this._onDidChangeActiveEditor.fire(activeEditor || this._activeWebview);
}
}

View File

@@ -28,6 +28,7 @@ export interface IExtensionContext {
storagePath: string;
asAbsolutePath(relativePath: string): string;
logger: ExtHostLogger;
readonly logDirectory: string;
}
/**
@@ -126,6 +127,7 @@ export class ExtensionActivationTimesBuilder {
export class ActivatedExtension {
public readonly activationFailed: boolean;
public readonly activationFailedError: Error;
public readonly activationTimes: ExtensionActivationTimes;
public readonly module: IExtensionModule;
public readonly exports: IExtensionAPI;
@@ -133,12 +135,14 @@ export class ActivatedExtension {
constructor(
activationFailed: boolean,
activationFailedError: Error,
activationTimes: ExtensionActivationTimes,
module: IExtensionModule,
exports: IExtensionAPI,
subscriptions: IDisposable[]
) {
this.activationFailed = activationFailed;
this.activationFailedError = activationFailedError;
this.activationTimes = activationTimes;
this.module = module;
this.exports = exports;
@@ -148,13 +152,13 @@ export class ActivatedExtension {
export class EmptyExtension extends ActivatedExtension {
constructor(activationTimes: ExtensionActivationTimes) {
super(false, activationTimes, { activate: undefined, deactivate: undefined }, undefined, []);
super(false, null, activationTimes, { activate: undefined, deactivate: undefined }, undefined, []);
}
}
export class FailedExtension extends ActivatedExtension {
constructor(activationTimes: ExtensionActivationTimes) {
super(true, activationTimes, { activate: undefined, deactivate: undefined }, undefined, []);
constructor(activationError: Error) {
super(true, activationError, ExtensionActivationTimes.NONE, { activate: undefined, deactivate: undefined }, undefined, []);
}
}
@@ -243,7 +247,8 @@ export class ExtensionsActivator {
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);
const error = new Error(`Unknown dependency '${depId}'`);
this._activatedExtensions[currentExtension.id] = new FailedExtension(error);
return;
}
@@ -252,7 +257,9 @@ export class ExtensionsActivator {
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);
const error = new Error(`Dependency ${depId} failed to activate`);
(<any>error).detail = dep.activationFailedError;
this._activatedExtensions[currentExtension.id] = new FailedExtension(error);
return;
}
} else {
@@ -285,7 +292,8 @@ export class ExtensionsActivator {
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);
const error = new Error('More than 10 levels of dependencies (most likely a dependency loop)');
this._activatedExtensions[extensionDescriptions[i].id] = new FailedExtension(error);
}
return TPromise.as(void 0);
}
@@ -333,7 +341,7 @@ export class ExtensionsActivator {
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);
return new FailedExtension(err);
}).then((x: ActivatedExtension) => {
this._activatedExtensions[extensionDescription.id] = x;
delete this._activatingExtensions[extensionDescription.id];

View File

@@ -22,7 +22,6 @@ import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { TernarySearchTree } from 'vs/base/common/map';
import { Barrier } from 'vs/base/common/async';
import { ILogService } from 'vs/platform/log/common/log';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import URI from 'vs/base/common/uri';
@@ -142,8 +141,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
extHostContext: IExtHostContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
extHostLogService: ExtHostLogService,
environmentService: IEnvironmentService
extHostLogService: ExtHostLogService
) {
this._barrier = new Barrier();
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
@@ -212,6 +210,17 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
}
public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): TPromise<void> {
return this.activateById(extensionId, reason).then(() => {
const extension = this._activator.getActivatedExtension(extensionId);
if (extension.activationFailed) {
// activation failed => bubble up the error as the promise result
return TPromise.wrapError(extension.activationFailedError);
}
return void 0;
});
}
public getAllExtensionDescriptions(): IExtensionDescription[] {
return this._registry.getAllExtensionDescriptions();
}
@@ -358,6 +367,10 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
get logger() {
checkProposedApiEnabled(extensionDescription);
return that._extHostLogService.getExtLogger(extensionDescription.id);
},
get logDirectory() {
checkProposedApiEnabled(extensionDescription);
return that._extHostLogService.getLogDirectory(extensionDescription.id);
}
});
});
@@ -371,7 +384,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
};
return this._callActivateOptional(logService, extensionId, extensionModule, context, activationTimesBuilder).then((extensionExports) => {
return new ActivatedExtension(false, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions);
return new ActivatedExtension(false, null, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions);
});
}
@@ -423,7 +436,7 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription
"TelemetryActivationEvent" : {
"id": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"publisherDisplayName": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"activationEvents": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}

View File

@@ -6,15 +6,17 @@
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystemShape } from './extHost.protocol';
import { Event, mapEvent } from 'vs/base/common/event';
import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystemShape, IFileChangeDto } from './extHost.protocol';
import * as vscode from 'vscode';
import { IStat } from 'vs/platform/files/common/files';
import * as files from 'vs/platform/files/common/files';
import * as path from 'path';
import { IDisposable } from 'vs/base/common/lifecycle';
import { asWinJsPromise } from 'vs/base/common/async';
import { IPatternInfo } from 'vs/platform/search/common/search';
import { values } from 'vs/base/common/map';
import { Range } from 'vs/workbench/api/node/extHostTypes';
import { Range, DeprecatedFileType, DeprecatedFileChangeType, FileChangeType } from 'vs/workbench/api/node/extHostTypes';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { Schemas } from 'vs/base/common/network';
class FsLinkProvider implements vscode.DocumentLinkProvider {
@@ -55,102 +57,245 @@ class FsLinkProvider implements vscode.DocumentLinkProvider {
}
}
class FileSystemProviderShim implements vscode.FileSystemProvider {
onDidChangeFile: vscode.Event<vscode.FileChangeEvent[]>;
constructor(private readonly _delegate: vscode.DeprecatedFileSystemProvider) {
if (!this._delegate.onDidChange) {
this.onDidChangeFile = Event.None;
} else {
this.onDidChangeFile = mapEvent(this._delegate.onDidChange, old => old.map(FileSystemProviderShim._modernizeFileChange));
}
}
watch(uri: vscode.Uri, options: {}): vscode.Disposable {
// does nothing because in the old API there was no notion of
// watch and provider decide what file events to generate...
return { dispose() { } };
}
stat(resource: vscode.Uri): Thenable<vscode.FileStat> {
return this._delegate.stat(resource).then(stat => FileSystemProviderShim._modernizeFileStat(stat));
}
rename(oldUri: vscode.Uri, newUri: vscode.Uri): Thenable<void> {
return this._delegate.move(oldUri, newUri).then(stat => void 0);
}
readDirectory(resource: vscode.Uri): Thenable<[string, vscode.FileType][]> {
return this._delegate.readdir(resource).then(tuples => {
return tuples.map(tuple => <[string, vscode.FileType]>[path.posix.basename(tuple[0].path), FileSystemProviderShim._modernizeFileStat(tuple[1]).type]);
});
}
private static _modernizeFileStat(stat: vscode.DeprecatedFileStat): vscode.FileStat {
let { mtime, size, type } = stat;
let newType: files.FileType;
// no support for bitmask, effectively no support for symlinks
switch (type) {
case DeprecatedFileType.Dir:
newType = files.FileType.Directory;
break;
case DeprecatedFileType.File:
newType = files.FileType.File;
break;
case DeprecatedFileType.Symlink:
newType = files.FileType.File & files.FileType.SymbolicLink;
break;
}
return { type: newType, ctime: 0, mtime, size };
}
private static _modernizeFileChange(e: vscode.DeprecatedFileChange): vscode.FileChangeEvent {
let { resource, type } = e;
let newType: vscode.FileChangeType;
switch (type) {
case DeprecatedFileChangeType.Updated:
newType = FileChangeType.Changed;
break;
case DeprecatedFileChangeType.Added:
newType = FileChangeType.Created;
break;
case DeprecatedFileChangeType.Deleted:
newType = FileChangeType.Deleted;
break;
}
return { uri: resource, type: newType };
}
// --- delete/create file or folder
delete(resource: vscode.Uri): Thenable<void> {
return this._delegate.stat(resource).then(stat => {
if (stat.type === DeprecatedFileType.Dir) {
return this._delegate.rmdir(resource);
} else {
return this._delegate.unlink(resource);
}
});
}
createDirectory(resource: vscode.Uri): Thenable<void> {
return this._delegate.mkdir(resource).then(stat => void 0);
}
// --- read/write
readFile(resource: vscode.Uri): Thenable<Uint8Array> {
let chunks: Buffer[] = [];
return this._delegate.read(resource, 0, -1, {
report(data) {
chunks.push(Buffer.from(data));
}
}).then(() => {
return Buffer.concat(chunks);
});
}
writeFile(resource: vscode.Uri, content: Uint8Array, options: files.FileWriteOptions): Thenable<void> {
return this._delegate.write(resource, content);
}
}
export class ExtHostFileSystem implements ExtHostFileSystemShape {
private readonly _proxy: MainThreadFileSystemShape;
private readonly _provider = new Map<number, vscode.FileSystemProvider>();
private readonly _linkProvider = new FsLinkProvider();
private readonly _fsProvider = new Map<number, vscode.FileSystemProvider>();
private readonly _usedSchemes = new Set<string>();
private readonly _watches = new Map<number, IDisposable>();
private _handlePool: number = 0;
constructor(mainContext: IMainContext, extHostLanguageFeatures: ExtHostLanguageFeatures) {
this._proxy = mainContext.getProxy(MainContext.MainThreadFileSystem);
this._usedSchemes.add(Schemas.file);
this._usedSchemes.add(Schemas.untitled);
this._usedSchemes.add(Schemas.vscode);
this._usedSchemes.add(Schemas.inMemory);
this._usedSchemes.add(Schemas.internal);
this._usedSchemes.add(Schemas.http);
this._usedSchemes.add(Schemas.https);
this._usedSchemes.add(Schemas.mailto);
this._usedSchemes.add(Schemas.data);
extHostLanguageFeatures.registerDocumentLinkProvider('*', this._linkProvider);
}
registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider) {
registerDeprecatedFileSystemProvider(scheme: string, provider: vscode.DeprecatedFileSystemProvider) {
return this.registerFileSystemProvider(scheme, new FileSystemProviderShim(provider), { isCaseSensitive: false });
}
registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean }) {
if (this._usedSchemes.has(scheme)) {
throw new Error(`a provider for the scheme '${scheme}' is already registered`);
}
const handle = this._handlePool++;
this._linkProvider.add(scheme);
this._provider.set(handle, provider);
this._proxy.$registerFileSystemProvider(handle, scheme);
if (provider.root) {
// todo@remote
this._proxy.$onDidAddFileSystemRoot(provider.root);
this._usedSchemes.add(scheme);
this._fsProvider.set(handle, provider);
let capabilites = files.FileSystemProviderCapabilities.FileReadWrite;
if (options.isCaseSensitive) {
capabilites += files.FileSystemProviderCapabilities.PathCaseSensitive;
}
let reg: IDisposable;
if (provider.onDidChange) {
reg = provider.onDidChange(event => this._proxy.$onFileSystemChange(handle, <any>event));
if (typeof provider.copy === 'function') {
capabilites += files.FileSystemProviderCapabilities.FileFolderCopy;
}
this._proxy.$registerFileSystemProvider(handle, scheme, capabilites);
const subscription = provider.onDidChangeFile(event => {
let mapped: IFileChangeDto[] = [];
for (const e of event) {
let { uri: resource, type } = e;
if (resource.scheme !== scheme) {
// dropping events for wrong scheme
continue;
}
let newType: files.FileChangeType;
switch (type) {
case FileChangeType.Changed:
newType = files.FileChangeType.UPDATED;
break;
case FileChangeType.Created:
newType = files.FileChangeType.ADDED;
break;
case FileChangeType.Deleted:
newType = files.FileChangeType.DELETED;
break;
}
mapped.push({ resource, type: newType });
}
this._proxy.$onFileSystemChange(handle, mapped);
});
return {
dispose: () => {
if (reg) {
reg.dispose();
}
subscription.dispose();
this._linkProvider.delete(scheme);
this._provider.delete(handle);
this._proxy.$unregisterFileSystemProvider(handle);
this._usedSchemes.delete(scheme);
this._fsProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
}
};
}
$utimes(handle: number, resource: UriComponents, mtime: number, atime: number): TPromise<IStat, any> {
return asWinJsPromise(token => this._provider.get(handle).utimes(URI.revive(resource), mtime, atime));
private static _asIStat(stat: vscode.FileStat): files.IStat {
const { type, ctime, mtime, size } = stat;
return { type, ctime, mtime, size };
}
$stat(handle: number, resource: UriComponents): TPromise<IStat, any> {
return asWinJsPromise(token => this._provider.get(handle).stat(URI.revive(resource)));
$stat(handle: number, resource: UriComponents): TPromise<files.IStat, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat);
}
$read(handle: number, session: number, offset: number, count: number, resource: UriComponents): TPromise<number> {
const progress = {
report: chunk => {
this._proxy.$reportFileChunk(handle, session, [].slice.call(chunk));
}
};
return asWinJsPromise(token => this._provider.get(handle).read(URI.revive(resource), offset, count, progress));
$readdir(handle: number, resource: UriComponents): TPromise<[string, files.FileType][], any> {
return asWinJsPromise(token => this._fsProvider.get(handle).readDirectory(URI.revive(resource)));
}
$write(handle: number, resource: UriComponents, content: number[]): TPromise<void, any> {
return asWinJsPromise(token => this._provider.get(handle).write(URI.revive(resource), Buffer.from(content)));
$readFile(handle: number, resource: UriComponents): TPromise<string> {
return asWinJsPromise(token => {
return this._fsProvider.get(handle).readFile(URI.revive(resource));
}).then(data => {
return Buffer.isBuffer(data) ? data.toString('base64') : Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('base64');
});
}
$unlink(handle: number, resource: UriComponents): TPromise<void, any> {
return asWinJsPromise(token => this._provider.get(handle).unlink(URI.revive(resource)));
$writeFile(handle: number, resource: UriComponents, base64Content: string, opts: files.FileWriteOptions): TPromise<void, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).writeFile(URI.revive(resource), Buffer.from(base64Content, 'base64'), opts));
}
$move(handle: number, resource: UriComponents, target: UriComponents): TPromise<IStat, any> {
return asWinJsPromise(token => this._provider.get(handle).move(URI.revive(resource), URI.revive(target)));
$delete(handle: number, resource: UriComponents): TPromise<void, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).delete(URI.revive(resource), { recursive: true }));
}
$mkdir(handle: number, resource: UriComponents): TPromise<IStat, any> {
return asWinJsPromise(token => this._provider.get(handle).mkdir(URI.revive(resource)));
$rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise<void, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts));
}
$readdir(handle: number, resource: UriComponents): TPromise<[UriComponents, IStat][], any> {
return asWinJsPromise(token => this._provider.get(handle).readdir(URI.revive(resource)));
$copy(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise<void, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).copy(URI.revive(oldUri), URI.revive(newUri), opts));
}
$rmdir(handle: number, resource: UriComponents): TPromise<void, any> {
return asWinJsPromise(token => this._provider.get(handle).rmdir(URI.revive(resource)));
$mkdir(handle: number, resource: UriComponents): TPromise<void, any> {
return asWinJsPromise(token => this._fsProvider.get(handle).createDirectory(URI.revive(resource)));
}
$findFiles(handle: number, session: number, query: string): TPromise<void> {
const provider = this._provider.get(handle);
if (!provider.findFiles) {
return TPromise.as(undefined);
$watch(handle: number, session: number, resource: UriComponents, opts: files.IWatchOptions): void {
asWinJsPromise(token => {
let subscription = this._fsProvider.get(handle).watch(URI.revive(resource), opts);
this._watches.set(session, subscription);
});
}
$unwatch(handle: number, session: number): void {
let subscription = this._watches.get(session);
if (subscription) {
subscription.dispose();
this._watches.delete(session);
}
const progress = {
report: (uri) => {
this._proxy.$handleFindMatch(handle, session, uri);
}
};
return asWinJsPromise(token => provider.findFiles(query, progress, token));
}
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, options: { includes: string[], excludes: string[] }): TPromise<void> {
const provider = this._provider.get(handle);
if (!provider.provideTextSearchResults) {
return TPromise.as(undefined);
}
const progress = {
report: (data: vscode.TextSearchResult) => {
this._proxy.$handleFindMatch(handle, session, [data.uri, {
lineNumber: 1 + data.range.start.line,
preview: data.preview.leading + data.preview.matching + data.preview.trailing,
offsetAndLengths: [[data.preview.leading.length, data.preview.matching.length]]
}]);
}
};
return asWinJsPromise(token => provider.provideTextSearchResults(pattern, options, progress, token));
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from './extHostTypes';
import { parse, IRelativePattern } from 'vs/base/common/glob';
import { Uri, FileSystemWatcher as _FileSystemWatcher } from 'vscode';

View File

@@ -9,19 +9,20 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { mixin } from 'vs/base/common/objects';
import * as vscode from 'vscode';
import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { Range, Disposable, CompletionList, SnippetString, Color, CodeActionKind } from 'vs/workbench/api/node/extHostTypes';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, HierarchicalSymbolInformation } from 'vs/workbench/api/node/extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostDiagnostics, DiagnosticCollection } from 'vs/workbench/api/node/extHostDiagnostics';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, SymbolInformationDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto } from './extHost.protocol';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, SymbolInformationDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter } from './extHost.protocol';
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { isObject } from 'vs/base/common/types';
// --- adapter
@@ -38,6 +39,9 @@ class OutlineAdapter {
provideDocumentSymbols(resource: URI): TPromise<SymbolInformationDto[]> {
let doc = this._documents.getDocumentData(resource).document;
return asWinJsPromise(token => this._provider.provideDocumentSymbols(doc, token)).then(value => {
if (value instanceof HierarchicalSymbolInformation) {
value = HierarchicalSymbolInformation.toFlatSymbolInformation(value);
}
if (Array.isArray(value)) {
return value.map(symbol => IdObject.mixin(TypeConverters.fromSymbolInformation(symbol)));
}
@@ -280,15 +284,11 @@ class CodeActionAdapter {
const ran = <vscode.Range>TypeConverters.toRange(range);
const allDiagnostics: vscode.Diagnostic[] = [];
this._diagnostics.forEach(collection => {
if (collection.has(resource)) {
for (let diagnostic of collection.get(resource)) {
if (ran.contains(diagnostic.range)) {
allDiagnostics.push(diagnostic);
}
}
for (const diagnostic of this._diagnostics.getDiagnostics(resource)) {
if (ran.contains(diagnostic.range)) {
allDiagnostics.push(diagnostic);
}
});
}
const codeActionContext: vscode.CodeActionContext = {
diagnostics: allDiagnostics,
@@ -317,7 +317,7 @@ class CodeActionAdapter {
result.push({
title: candidate.title,
command: candidate.command && this._commands.toInternal(candidate.command),
diagnostics: candidate.diagnostics && candidate.diagnostics.map(DiagnosticCollection.toMarkerData),
diagnostics: candidate.diagnostics && candidate.diagnostics.map(TypeConverters.fromDiagnostic),
edit: candidate.edit && TypeConverters.WorkspaceEdit.from(candidate.edit),
kind: candidate.kind && candidate.kind.value
});
@@ -468,20 +468,16 @@ class NavigateTypeAdapter {
}
}
interface RenameProvider2 extends vscode.RenameProvider {
resolveInitialRenameValue?(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.ProviderResult<any>;
}
class RenameAdapter {
static supportsResolving(provider: RenameProvider2): boolean {
return typeof provider.resolveInitialRenameValue === 'function';
static supportsResolving(provider: vscode.RenameProvider): boolean {
return typeof provider.prepareRename === 'function';
}
private _documents: ExtHostDocuments;
private _provider: RenameProvider2;
private _provider: vscode.RenameProvider;
constructor(documents: ExtHostDocuments, provider: RenameProvider2) {
constructor(documents: ExtHostDocuments, provider: vscode.RenameProvider) {
this._documents = documents;
this._provider = provider;
}
@@ -514,24 +510,40 @@ class RenameAdapter {
});
}
resolveInitialRenameValue(resource: URI, position: IPosition): TPromise<modes.RenameInitialValue> {
if (typeof this._provider.resolveInitialRenameValue !== 'function') {
resolveRenameLocation(resource: URI, position: IPosition): TPromise<modes.RenameLocation> {
if (typeof this._provider.prepareRename !== 'function') {
return TPromise.as(undefined);
}
let doc = this._documents.getDocumentData(resource).document;
let pos = TypeConverters.toPosition(position);
return asWinJsPromise(token => this._provider.resolveInitialRenameValue(doc, pos, token)).then((value) => {
return <modes.RenameInitialValue>{
range: TypeConverters.fromRange(value.range),
text: value.text
};
return asWinJsPromise(token => this._provider.prepareRename(doc, pos, token)).then(rangeOrLocation => {
let range: vscode.Range;
let text: string;
if (Range.isRange(rangeOrLocation)) {
range = rangeOrLocation;
text = doc.getText(rangeOrLocation);
} else if (isObject(rangeOrLocation)) {
range = rangeOrLocation.range;
text = rangeOrLocation.placeholder;
}
if (!range) {
return undefined;
}
if (!range.contains(pos)) {
console.warn('INVALID rename location: range must contain position');
return undefined;
}
return { range: TypeConverters.fromRange(range), text };
});
}
}
class SuggestAdapter {
static supportsResolving(provider: vscode.CompletionItemProvider): boolean {
@@ -785,7 +797,7 @@ class ColorProviderAdapter {
const colorInfos: IRawColorInfo[] = colors.map(ci => {
return {
color: [ci.color.red, ci.color.green, ci.color.blue, ci.color.alpha] as [number, number, number, number],
color: TypeConverters.Color.from(ci.color),
range: TypeConverters.fromRange(ci.range)
};
});
@@ -797,7 +809,7 @@ class ColorProviderAdapter {
provideColorPresentations(resource: URI, raw: IRawColorInfo): TPromise<modes.IColorPresentation[]> {
const document = this._documents.getDocumentData(resource).document;
const range = TypeConverters.toRange(raw.range);
const color = new Color(raw.color[0], raw.color[1], raw.color[2], raw.color[3]);
const color = TypeConverters.Color.to(raw.color);
return asWinJsPromise(token => this._provider.provideColorPresentations(color, { document, range }, token)).then(value => {
return value.map(TypeConverters.ColorPresentation.from);
});
@@ -808,16 +820,16 @@ class FoldingProviderAdapter {
constructor(
private _documents: ExtHostDocuments,
private _provider: vscode.FoldingProvider
private _provider: vscode.FoldingRangeProvider
) { }
provideFoldingRanges(resource: URI): TPromise<modes.IFoldingRangeList> {
provideFoldingRanges(resource: URI, context: modes.FoldingContext): TPromise<modes.FoldingRange[]> {
const doc = this._documents.getDocumentData(resource).document;
return asWinJsPromise(token => this._provider.provideFoldingRanges(doc, token)).then(list => {
if (!Array.isArray(list.ranges)) {
return asWinJsPromise(token => this._provider.provideFoldingRanges(doc, context, token)).then(ranges => {
if (!Array.isArray(ranges)) {
return void 0;
}
return TypeConverters.FoldingRangeList.from(list);
return ranges.map(TypeConverters.FoldingRange.from);
});
}
}
@@ -828,10 +840,15 @@ type Adapter = OutlineAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapt
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter
| ColorProviderAdapter | FoldingProviderAdapter;
export interface ISchemeTransformer {
transformOutgoing(scheme: string): string;
}
export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
private static _handlePool: number = 0;
private readonly _schemeTransformer: ISchemeTransformer;
private _proxy: MainThreadLanguageFeaturesShape;
private _documents: ExtHostDocuments;
private _commands: ExtHostCommands;
@@ -841,11 +858,13 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
constructor(
mainContext: IMainContext,
schemeTransformer: ISchemeTransformer,
documents: ExtHostDocuments,
commands: ExtHostCommands,
heapMonitor: ExtHostHeapService,
diagnostics: ExtHostDiagnostics
) {
this._schemeTransformer = schemeTransformer;
this._proxy = mainContext.getProxy(MainContext.MainThreadLanguageFeatures);
this._documents = documents;
this._commands = commands;
@@ -853,6 +872,42 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
this._diagnostics = diagnostics;
}
private _transformDocumentSelector(selector: vscode.DocumentSelector): ISerializedDocumentFilter[] {
if (Array.isArray(selector)) {
return selector.map(sel => this._doTransformDocumentSelector(sel));
}
return [this._doTransformDocumentSelector(selector)];
}
private _doTransformDocumentSelector(selector: string | vscode.DocumentFilter): ISerializedDocumentFilter {
if (typeof selector === 'string') {
return {
$serialized: true,
language: selector
};
}
if (selector) {
return {
$serialized: true,
language: selector.language,
scheme: this._transformScheme(selector.scheme),
pattern: selector.pattern,
exclusive: selector.exclusive
};
}
return undefined;
}
private _transformScheme(scheme: string): string {
if (this._schemeTransformer && typeof scheme === 'string') {
return this._schemeTransformer.transformOutgoing(scheme);
}
return scheme;
}
private _createDisposable(handle: number): Disposable {
return new Disposable(() => {
this._adapter.delete(handle);
@@ -882,7 +937,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable {
const handle = this._addNewAdapter(new OutlineAdapter(this._documents, provider));
this._proxy.$registerOutlineSupport(handle, selector);
this._proxy.$registerOutlineSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -897,7 +952,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
const eventHandle = typeof provider.onDidChangeCodeLenses === 'function' ? this._nextHandle() : undefined;
this._adapter.set(handle, new CodeLensAdapter(this._documents, this._commands.converter, this._heapService, provider));
this._proxy.$registerCodeLensSupport(handle, selector, eventHandle);
this._proxy.$registerCodeLensSupport(handle, this._transformDocumentSelector(selector), eventHandle);
let result = this._createDisposable(handle);
if (eventHandle !== undefined) {
@@ -920,7 +975,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
const handle = this._addNewAdapter(new DefinitionAdapter(this._documents, provider));
this._proxy.$registerDeclaractionSupport(handle, selector);
this._proxy.$registerDeclaractionSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -930,7 +985,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable {
const handle = this._addNewAdapter(new ImplementationAdapter(this._documents, provider));
this._proxy.$registerImplementationSupport(handle, selector);
this._proxy.$registerImplementationSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -940,7 +995,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable {
const handle = this._addNewAdapter(new TypeDefinitionAdapter(this._documents, provider));
this._proxy.$registerTypeDefinitionSupport(handle, selector);
this._proxy.$registerTypeDefinitionSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -952,7 +1007,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: string): vscode.Disposable {
const handle = this._addNewAdapter(new HoverAdapter(this._documents, provider));
this._proxy.$registerHoverProvider(handle, selector);
this._proxy.$registerHoverProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -964,7 +1019,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
const handle = this._addNewAdapter(new DocumentHighlightAdapter(this._documents, provider));
this._proxy.$registerDocumentHighlightProvider(handle, selector);
this._proxy.$registerDocumentHighlightProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -976,7 +1031,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable {
const handle = this._addNewAdapter(new ReferenceAdapter(this._documents, provider));
this._proxy.$registerReferenceSupport(handle, selector);
this._proxy.$registerReferenceSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -986,9 +1041,9 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- quick fix
registerCodeActionProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider): vscode.Disposable {
registerCodeActionProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider));
this._proxy.$registerQuickFixSupport(handle, selector);
this._proxy.$registerQuickFixSupport(handle, this._transformDocumentSelector(selector), metadata && metadata.providedCodeActionKinds ? metadata.providedCodeActionKinds.map(kind => kind.value) : undefined);
return this._createDisposable(handle);
}
@@ -1001,7 +1056,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable {
const handle = this._addNewAdapter(new DocumentFormattingAdapter(this._documents, provider));
this._proxy.$registerDocumentFormattingSupport(handle, selector);
this._proxy.$registerDocumentFormattingSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -1011,7 +1066,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable {
const handle = this._addNewAdapter(new RangeFormattingAdapter(this._documents, provider));
this._proxy.$registerRangeFormattingSupport(handle, selector);
this._proxy.$registerRangeFormattingSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -1021,7 +1076,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, triggerCharacters: string[]): vscode.Disposable {
const handle = this._addNewAdapter(new OnTypeFormattingAdapter(this._documents, provider));
this._proxy.$registerOnTypeFormattingSupport(handle, selector, triggerCharacters);
this._proxy.$registerOnTypeFormattingSupport(handle, this._transformDocumentSelector(selector), triggerCharacters);
return this._createDisposable(handle);
}
@@ -1051,9 +1106,9 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- rename
registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider, canUseProposedApi = false): vscode.Disposable {
registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider));
this._proxy.$registerRenameSupport(handle, selector, canUseProposedApi && RenameAdapter.supportsResolving(provider));
this._proxy.$registerRenameSupport(handle, this._transformDocumentSelector(selector), RenameAdapter.supportsResolving(provider));
return this._createDisposable(handle);
}
@@ -1061,15 +1116,15 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.provideRenameEdits(URI.revive(resource), position, newName));
}
$resolveInitialRenameValue(handle: number, resource: URI, position: IPosition): TPromise<modes.RenameInitialValue> {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.resolveInitialRenameValue(resource, position));
$resolveRenameLocation(handle: number, resource: URI, position: IPosition): TPromise<modes.RenameLocation> {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.resolveRenameLocation(URI.revive(resource), position));
}
// --- suggestion
registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable {
const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider));
this._proxy.$registerSuggestSupport(handle, selector, triggerCharacters, SuggestAdapter.supportsResolving(provider));
this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider));
return this._createDisposable(handle);
}
@@ -1089,7 +1144,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, triggerCharacters: string[]): vscode.Disposable {
const handle = this._addNewAdapter(new SignatureHelpAdapter(this._documents, provider));
this._proxy.$registerSignatureHelpProvider(handle, selector, triggerCharacters);
this._proxy.$registerSignatureHelpProvider(handle, this._transformDocumentSelector(selector), triggerCharacters);
return this._createDisposable(handle);
}
@@ -1101,7 +1156,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
const handle = this._addNewAdapter(new LinkProviderAdapter(this._documents, this._heapService, provider));
this._proxy.$registerDocumentLinkProvider(handle, selector);
this._proxy.$registerDocumentLinkProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -1115,7 +1170,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable {
const handle = this._addNewAdapter(new ColorProviderAdapter(this._documents, provider));
this._proxy.$registerDocumentColorProvider(handle, selector);
this._proxy.$registerDocumentColorProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
@@ -1127,14 +1182,14 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo));
}
registerFoldingProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingProvider): vscode.Disposable {
registerFoldingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable {
const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider));
this._proxy.$registerFoldingProvider(handle, selector);
this._proxy.$registerFoldingRangeProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideFoldingRanges(handle: number, resource: UriComponents): TPromise<modes.IFoldingRangeList> {
return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource)));
$provideFoldingRanges(handle: number, resource: UriComponents, context: vscode.FoldingContext): TPromise<modes.FoldingRange[]> {
return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource), context));
}
// --- configuration

View File

@@ -5,15 +5,10 @@
'use strict';
import * as vscode from 'vscode';
import { TPromise } from 'vs/base/common/winjs.base';
import { join } from 'vs/base/common/paths';
import { mkdirp, dirExists } from 'vs/base/node/pfs';
import Event from 'vs/base/common/event';
import { LogLevel } from 'vs/workbench/api/node/extHostTypes';
import { ILogService, DelegatedLogService } from 'vs/platform/log/common/log';
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { memoize } from 'vs/base/common/decorators';
import { ExtHostLogServiceShape } from 'vs/workbench/api/node/extHost.protocol';
@@ -24,9 +19,9 @@ export class ExtHostLogService extends DelegatedLogService implements ILogServic
constructor(
private _windowId: number,
logLevel: LogLevel,
private _environmentService: IEnvironmentService
private _logsPath: string
) {
super(createSpdLogService(`exthost${_windowId}`, logLevel, _environmentService.logsPath));
super(createSpdLogService(`exthost${_windowId}`, logLevel, _logsPath));
}
$setLevel(level: LogLevel): void {
@@ -42,40 +37,23 @@ export class ExtHostLogService extends DelegatedLogService implements ILogServic
return logger;
}
getLogDirectory(extensionID: string): string {
return join(this._logsPath, `${extensionID}_${this._windowId}`);
}
private createLogger(extensionID: string): ExtHostLogger {
const logsDirPath = join(this._environmentService.logsPath, `${extensionID}_${this._windowId}`);
const logsDirPath = this.getLogDirectory(extensionID);
const logService = createSpdLogService(extensionID, this.getLevel(), logsDirPath);
this._register(this.onDidChangeLogLevel(level => logService.setLevel(level)));
return new ExtHostLogger(logService, logsDirPath);
return new ExtHostLogger(logService);
}
}
export class ExtHostLogger implements vscode.Logger {
constructor(
private readonly _logService: ILogService,
private readonly _logDirectory: string
) {
}
get onDidChangeLogLevel(): Event<LogLevel> {
return this._logService.onDidChangeLogLevel;
}
get currentLevel(): LogLevel { return this._logService.getLevel(); }
@memoize
get logDirectory(): TPromise<string> {
return dirExists(this._logDirectory).then(exists => {
if (exists) {
return TPromise.wrap(null);
} else {
return mkdirp(this._logDirectory);
}
}).then(() => {
return this._logDirectory;
});
}
private readonly _logService: ILogService
) { }
trace(message: string, ...args: any[]): void {
return this._logService.trace(message, ...args);

View File

@@ -5,7 +5,7 @@
'use strict';
import Severity from 'vs/base/common/severity';
import vscode = require('vscode');
import * as vscode from 'vscode';
import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';

View File

@@ -4,16 +4,20 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Progress, ProgressOptions, CancellationToken } from 'vscode';
import { MainThreadProgressShape } from './extHost.protocol';
import { ProgressOptions } from 'vscode';
import { MainThreadProgressShape, ExtHostProgressShape } from './extHost.protocol';
import { ProgressLocation } from './extHostTypeConverters';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IProgressStep } from 'vs/platform/progress/common/progress';
import { IProgressStep, Progress } from 'vs/platform/progress/common/progress';
import { localize } from 'vs/nls';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import { debounce } from 'vs/base/common/decorators';
export class ExtHostProgress {
export class ExtHostProgress implements ExtHostProgressShape {
private _proxy: MainThreadProgressShape;
private _handles: number = 0;
private _mapHandleToCancellationSource: Map<number, CancellationTokenSource> = new Map();
constructor(proxy: MainThreadProgressShape) {
this._proxy = proxy;
@@ -21,30 +25,66 @@ export class ExtHostProgress {
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);
const { title, location, cancellable } = options;
const source = localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
this._proxy.$startProgress(handle, { location: ProgressLocation.from(location), title, source, cancellable });
return this._withProgress(handle, task, cancellable);
}
private _withProgress<R>(handle: number, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Thenable<R> {
private _withProgress<R>(handle: number, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>, cancellable: boolean): Thenable<R> {
let source: CancellationTokenSource;
if (cancellable) {
source = new CancellationTokenSource();
this._mapHandleToCancellationSource.set(handle, source);
}
const progress = {
report: (p: IProgressStep) => {
this._proxy.$progressReport(handle, p);
const progressEnd = (handle: number): void => {
this._proxy.$progressEnd(handle);
this._mapHandleToCancellationSource.delete(handle);
if (source) {
source.dispose();
}
};
let p: Thenable<R>;
try {
p = task(progress, null);
p = task(new ProgressCallback(this._proxy, handle), cancellable ? source.token : CancellationToken.None);
} catch (err) {
this._proxy.$progressEnd(handle);
progressEnd(handle);
throw err;
}
p.then(result => this._proxy.$progressEnd(handle), err => this._proxy.$progressEnd(handle));
p.then(result => progressEnd(handle), err => progressEnd(handle));
return p;
}
public $acceptProgressCanceled(handle: number): void {
const source = this._mapHandleToCancellationSource.get(handle);
if (source) {
source.cancel();
this._mapHandleToCancellationSource.delete(handle);
}
}
}
function mergeProgress(result: IProgressStep, currentValue: IProgressStep): IProgressStep {
result.message = currentValue.message;
if (typeof currentValue.increment === 'number' && typeof result.message === 'number') {
result.increment += currentValue.increment;
} else if (typeof currentValue.increment === 'number') {
result.increment = currentValue.increment;
}
return result;
}
class ProgressCallback extends Progress<IProgressStep> {
constructor(private _proxy: MainThreadProgressShape, private _handle: number) {
super(p => this.throttledReport(p));
}
@debounce(100, (result: IProgressStep, currentValue: IProgressStep) => mergeProgress(result, currentValue), () => Object.create(null))
throttledReport(p: IProgressStep): void {
this._proxy.$progressReport(this._handle, p);
}
}

View File

@@ -29,9 +29,10 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
this._commands = commands;
}
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options: QuickPickOptions & { canSelectMany: true; }, token?: CancellationToken): Thenable<QuickPickItem[] | undefined>;
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> {
showQuickPick(itemsOrItemsPromise: Item[] | Thenable<Item[]>, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable<Item | Item[] | undefined> {
// clear state from last invocation
this._onDidSelectItem = undefined;
@@ -43,7 +44,8 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
placeHolder: options && options.placeHolder,
matchOnDescription: options && options.matchOnDescription,
matchOnDetail: options && options.matchOnDetail,
ignoreFocusLost: options && options.ignoreFocusOut
ignoreFocusLost: options && options.ignoreFocusOut,
canSelectMany: options && options.canPickMany
});
const promise = TPromise.any(<TPromise<number | Item[]>[]>[quickPickWidget, itemsPromise]).then(values => {
@@ -60,6 +62,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
let label: string;
let description: string;
let detail: string;
let picked: boolean;
if (typeof item === 'string') {
label = item;
@@ -67,12 +70,14 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
label = item.label;
description = item.description;
detail = item.detail;
picked = item.picked;
}
pickItems.push({
label,
description,
handle,
detail
detail,
picked
});
}
@@ -89,6 +94,8 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
return quickPickWidget.then(handle => {
if (typeof handle === 'number') {
return items[handle];
} else if (Array.isArray(handle)) {
return handle.map(h => items[h]);
}
return undefined;
});
@@ -98,7 +105,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
return TPromise.wrapError(err);
});
});
return wireCancellationToken<Item>(token, promise, true);
return wireCancellationToken<Item | Item[]>(token, promise, true);
}
$onItemSelected(handle: number): void {

View File

@@ -6,7 +6,7 @@
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import Event, { Emitter, once } from 'vs/base/common/event';
import { Event, Emitter, once } from 'vs/base/common/event';
import { debounce } from 'vs/base/common/decorators';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { asWinJsPromise } from 'vs/base/common/async';

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 { asWinJsPromise } from 'vs/base/common/async';
import { TPromise } from 'vs/base/common/winjs.base';
import { IPatternInfo } from 'vs/platform/search/common/search';
import * as vscode from 'vscode';
import { ExtHostSearchShape, IMainContext, MainContext, MainThreadSearchShape } from './extHost.protocol';
export class ExtHostSearch implements ExtHostSearchShape {
private readonly _proxy: MainThreadSearchShape;
private readonly _searchProvider = new Map<number, vscode.SearchProvider>();
private _handlePool: number = 0;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadSearch);
}
registerSearchProvider(scheme: string, provider: vscode.SearchProvider) {
const handle = this._handlePool++;
this._searchProvider.set(handle, provider);
this._proxy.$registerSearchProvider(handle, scheme);
return {
dispose: () => {
this._searchProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
}
};
}
$provideFileSearchResults(handle: number, session: number, query: string): TPromise<void> {
const provider = this._searchProvider.get(handle);
if (!provider.provideFileSearchResults) {
return TPromise.as(undefined);
}
const progress = {
report: (uri) => {
this._proxy.$handleFindMatch(handle, session, uri);
}
};
return asWinJsPromise(token => provider.provideFileSearchResults(query, progress, token));
}
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, options: { includes: string[], excludes: string[] }): TPromise<void> {
const provider = this._searchProvider.get(handle);
if (!provider.provideTextSearchResults) {
return TPromise.as(undefined);
}
const progress = {
report: (data: vscode.TextSearchResult) => {
this._proxy.$handleFindMatch(handle, session, [data.uri, {
lineNumber: data.range.start.line,
preview: data.preview.leading + data.preview.matching + data.preview.trailing,
offsetAndLengths: [[data.preview.leading.length, data.preview.matching.length]]
}]);
}
};
return asWinJsPromise(token => provider.provideTextSearchResults(pattern, options, progress, token));
}
}

View File

@@ -4,11 +4,12 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import URI, { UriComponents } from 'vs/base/common/uri';
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 { Event, Emitter } from 'vs/base/common/event';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as TaskSystem from 'vs/workbench/parts/tasks/common/tasks';
@@ -18,7 +19,12 @@ import { MainContext, MainThreadTaskShape, ExtHostTaskShape, IMainContext } from
import * as types from 'vs/workbench/api/node/extHostTypes';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import * as vscode from 'vscode';
import {
TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO, ProcessExecutionOptionsDTO, ProcessExecutionDTO,
ShellExecutionOptionsDTO, ShellExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO
} from '../shared/tasks';
export { TaskExecutionDTO };
/*
namespace ProblemPattern {
@@ -278,20 +284,43 @@ namespace CommandOptions {
}
}
namespace ShellQuoteOptions {
export function from(value: vscode.ShellQuotingOptions): TaskSystem.ShellQuotingOptions {
if (value === void 0 || value === null) {
return undefined;
}
return {
escape: value.escape,
strong: value.strong,
weak: value.strong
};
}
}
namespace ShellConfiguration {
export function from(value: { executable?: string, shellArgs?: string[] }): TaskSystem.ShellConfiguration {
export function from(value: { executable?: string, shellArgs?: string[], quotes?: vscode.ShellQuotingOptions }): TaskSystem.ShellConfiguration {
if (value === void 0 || value === null || !value.executable) {
return undefined;
}
let result: TaskSystem.ShellConfiguration = {
executable: value.executable,
args: Strings.from(value.shellArgs)
args: Strings.from(value.shellArgs),
quoting: ShellQuoteOptions.from(value.quotes)
};
return result;
}
}
namespace ShellString {
export function from(value: (string | vscode.ShellQuotedString)[]): TaskSystem.CommandString[] {
if (value === void 0 || value === null) {
return undefined;
}
return value.slice(0);
}
}
namespace Tasks {
export function from(tasks: vscode.Task[], rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): TaskSystem.ContributedTask[] {
@@ -396,19 +425,292 @@ namespace Tasks {
}
function getShellCommand(value: vscode.ShellExecution): TaskSystem.CommandConfiguration {
if (typeof value.commandLine !== 'string') {
if (value.args) {
if (typeof value.command !== 'string' && typeof value.command.value !== 'string') {
return undefined;
}
let result: TaskSystem.CommandConfiguration = {
name: value.command,
args: ShellString.from(value.args),
runtime: TaskSystem.RuntimeType.Shell,
presentation: undefined
};
if (value.options) {
result.options = CommandOptions.from(value.options);
}
return result;
} else {
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;
}
}
}
namespace TaskDefinitionDTO {
export function from(value: vscode.TaskDefinition): TaskDefinitionDTO {
if (value === void 0 || value === null) {
return undefined;
}
let result: TaskSystem.CommandConfiguration = {
name: value.commandLine,
runtime: TaskSystem.RuntimeType.Shell,
presentation: undefined
return value;
}
export function to(value: TaskDefinitionDTO): vscode.TaskDefinition {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
}
namespace TaskPresentationOptionsDTO {
export function from(value: vscode.TaskPresentationOptions): TaskPresentationOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
export function to(value: TaskPresentationOptionsDTO): vscode.TaskPresentationOptions {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
}
namespace ProcessExecutionOptionsDTO {
export function from(value: vscode.ProcessExecutionOptions): ProcessExecutionOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
export function to(value: ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ProcessExecutionDTO {
let candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
}
export function from(value: vscode.ProcessExecution): ProcessExecutionDTO {
if (value === void 0 || value === null) {
return undefined;
}
let result: ProcessExecutionDTO = {
process: value.process,
args: value.args
};
if (value.options) {
result.options = CommandOptions.from(value.options);
result.options = ProcessExecutionOptionsDTO.from(value.options);
}
return result;
}
export function to(value: ProcessExecutionDTO): types.ProcessExecution {
if (value === void 0 || value === null) {
return undefined;
}
return new types.ProcessExecution(value.process, value.args, value.options);
}
}
namespace ShellExecutionOptionsDTO {
export function from(value: vscode.ShellExecutionOptions): ShellExecutionOptionsDTO {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
export function to(value: ShellExecutionOptionsDTO): vscode.ShellExecutionOptions {
if (value === void 0 || value === null) {
return undefined;
}
return value;
}
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ShellExecutionDTO {
let candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
}
export function from(value: vscode.ShellExecution): ShellExecutionDTO {
if (value === void 0 || value === null) {
return undefined;
}
let result: ShellExecutionDTO = {
};
if (value.commandLine !== void 0) {
result.commandLine = value.commandLine;
} else {
result.command = value.command;
result.args = value.args;
}
if (value.options) {
result.options = ShellExecutionOptionsDTO.from(value.options);
}
return result;
}
export function to(value: ShellExecutionDTO): types.ShellExecution {
if (value === void 0 || value === null) {
return undefined;
}
if (value.commandLine) {
return new types.ShellExecution(value.commandLine, value.options);
} else {
return new types.ShellExecution(value.command, value.args ? value.args : [], value.options);
}
}
}
namespace TaskHandleDTO {
export function from(value: types.Task): TaskHandleDTO {
let folder: UriComponents;
if (value.scope !== void 0 && typeof value.scope !== 'number') {
folder = value.scope.uri;
}
return {
id: value._id,
workspaceFolder: folder
};
}
}
namespace TaskDTO {
export function from(value: vscode.Task, extension: IExtensionDescription): TaskDTO {
if (value === void 0 || value === null) {
return undefined;
}
let execution: ShellExecutionDTO | ProcessExecutionDTO;
if (value.execution instanceof types.ProcessExecution) {
execution = ProcessExecutionDTO.from(value.execution);
} else if (value.execution instanceof types.ShellExecution) {
execution = ShellExecutionDTO.from(value.execution);
}
let definition: TaskDefinitionDTO = TaskDefinitionDTO.from(value.definition);
let scope: number | UriComponents;
if (value.scope) {
if (typeof value.scope === 'number') {
scope = value.scope;
} else {
scope = value.scope.uri.toJSON();
}
}
if (!definition || !scope) {
return undefined;
}
let group = (value.group as types.TaskGroup) ? (value.group as types.TaskGroup).id : undefined;
let result: TaskDTO = {
_id: (value as types.Task)._id,
definition,
name: value.name,
source: {
extensionId: extension.id,
label: value.source,
scope: scope
},
execution,
isBackground: value.isBackground,
group: group,
presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions),
problemMatchers: value.problemMatchers,
hasDefinedMatchers: (value as types.Task).hasDefinedMatchers
};
return result;
}
export function to(value: TaskDTO, workspace: ExtHostWorkspace): types.Task {
if (value === void 0 || value === null) {
return undefined;
}
let execution: types.ShellExecution | types.ProcessExecution;
if (ProcessExecutionDTO.is(value.execution)) {
execution = ProcessExecutionDTO.to(value.execution);
} else if (ShellExecutionDTO.is(value.execution)) {
execution = ShellExecutionDTO.to(value.execution);
}
let definition: vscode.TaskDefinition = TaskDefinitionDTO.to(value.definition);
let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder;
if (value.source) {
if (value.source.scope !== void 0) {
if (typeof value.source.scope === 'number') {
scope = value.source.scope;
} else {
scope = workspace.resolveWorkspaceFolder(URI.revive(value.source.scope));
}
} else {
scope = types.TaskScope.Workspace;
}
}
if (!definition || !scope) {
return undefined;
}
let result = new types.Task(definition, scope, value.name, value.source.label, execution, value.problemMatchers);
if (value.isBackground !== void 0) {
result.isBackground = value.isBackground;
}
if (value.group !== void 0) {
result.group = types.TaskGroup.from(value.group);
}
if (value.presentationOptions) {
result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions);
}
if (value._id) {
result._id = value._id;
}
return result;
}
}
namespace TaskFilterDTO {
export function from(value: vscode.TaskFilter): TaskFilterDTO {
return value;
}
export function to(value: TaskFilterDTO): vscode.TaskFilter {
if (!value) {
return undefined;
}
return Objects.assign(Object.create(null), value);
}
}
class TaskExecutionImpl implements vscode.TaskExecution {
constructor(readonly _id: string, private readonly _task: vscode.Task, private readonly _tasks: ExtHostTask) {
}
get task(): vscode.Task {
return this._task;
}
public terminate(): void {
this._tasks.terminateTask(this);
}
}
namespace TaskExecutionDTO {
export function to(value: TaskExecutionDTO, tasks: ExtHostTask): vscode.TaskExecution {
return new TaskExecutionImpl(value.id, TaskDTO.to(value.task, tasks.extHostWorkspace), tasks);
}
export function from(value: vscode.TaskExecution): TaskExecutionDTO {
return {
id: (value as TaskExecutionImpl)._id,
task: undefined
};
}
}
interface HandlerData {
@@ -422,12 +724,21 @@ export class ExtHostTask implements ExtHostTaskShape {
private _extHostWorkspace: ExtHostWorkspace;
private _handleCounter: number;
private _handlers: Map<number, HandlerData>;
private _taskExecutions: Map<string, TaskExecutionImpl>;
private readonly _onDidExecuteTask: Emitter<vscode.TaskStartEvent> = new Emitter<vscode.TaskStartEvent>();
private readonly _onDidTerminateTask: Emitter<vscode.TaskEndEvent> = new Emitter<vscode.TaskEndEvent>();
constructor(mainContext: IMainContext, extHostWorkspace: ExtHostWorkspace) {
this._proxy = mainContext.getProxy(MainContext.MainThreadTask);
this._extHostWorkspace = extHostWorkspace;
this._handleCounter = 0;
this._handlers = new Map<number, HandlerData>();
this._taskExecutions = new Map<string, TaskExecutionImpl>();
}
public get extHostWorkspace(): ExtHostWorkspace {
return this._extHostWorkspace;
}
public registerTaskProvider(extension: IExtensionDescription, provider: vscode.TaskProvider): vscode.Disposable {
@@ -443,6 +754,69 @@ export class ExtHostTask implements ExtHostTaskShape {
});
}
public fetchTasks(filter?: vscode.TaskFilter): Thenable<vscode.Task[]> {
return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then((values) => {
let result: vscode.Task[] = [];
for (let value of values) {
let task = TaskDTO.to(value, this._extHostWorkspace);
if (task) {
result.push(task);
}
}
return result;
});
}
public executeTask(extension: IExtensionDescription, task: vscode.Task): Thenable<vscode.TaskExecution> {
let tTask = (task as types.Task);
// We have a preserved ID. So the task didn't change.
if (tTask._id !== void 0) {
return this._proxy.$executeTask(TaskHandleDTO.from(tTask)).then(value => this.getTaskExecution(value, task));
} else {
let dto = TaskDTO.from(task, extension);
if (dto === void 0) {
// {{ SQL CARBON EDIT }} - Add type assertion to fix build break
return Promise.reject<vscode.TaskExecution>(new Error('Task is not valid'));
}
return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task));
}
}
public $taskStarted(execution: TaskExecutionDTO): void {
this._onDidExecuteTask.fire({
execution: this.getTaskExecution(execution)
});
}
get taskExecutions(): vscode.TaskExecution[] {
let result: vscode.TaskExecution[] = [];
this._taskExecutions.forEach(value => result.push(value));
return result;
}
get onDidStartTask(): Event<vscode.TaskStartEvent> {
return this._onDidExecuteTask.event;
}
public terminateTask(execution: vscode.TaskExecution): TPromise<void> {
if (!(execution instanceof TaskExecutionImpl)) {
throw new Error('No valid task execution provided');
}
return this._proxy.$terminateTask((execution as TaskExecutionImpl)._id);
}
public $taskEnded(execution: TaskExecutionDTO): void {
const _execution = this.getTaskExecution(execution);
this._taskExecutions.delete(execution.id);
this._onDidTerminateTask.fire({
execution: _execution
});
}
get onDidEndTask(): Event<vscode.TaskEndEvent> {
return this._onDidTerminateTask.event;
}
public $provideTasks(handle: number): TPromise<TaskSystem.TaskSet> {
let handler = this._handlers.get(handle);
if (!handler) {
@@ -460,4 +834,14 @@ export class ExtHostTask implements ExtHostTaskShape {
private nextHandle(): number {
return this._handleCounter++;
}
private getTaskExecution(execution: TaskExecutionDTO, task?: vscode.Task): TaskExecutionImpl {
let result: TaskExecutionImpl = this._taskExecutions.get(execution.id);
if (result) {
return result;
}
result = new TaskExecutionImpl(execution.id, task ? task : TaskDTO.to(execution.task, this._extHostWorkspace), this);
this._taskExecutions.set(execution.id, result);
return result;
}
}

View File

@@ -4,12 +4,19 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import vscode = require('vscode');
import Event, { Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
import * as cp from 'child_process';
import * as os from 'os';
import * as platform from 'vs/base/common/platform';
import * as terminalEnvironment from 'vs/workbench/parts/terminal/node/terminalEnvironment';
import Uri from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol';
import { IMessageFromTerminalProcess } from 'vs/workbench/parts/terminal/node/terminal';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ILogService } from 'vs/platform/log/common/log';
export class ExtHostTerminal implements vscode.Terminal {
private _name: string;
private _id: number;
private _proxy: MainThreadTerminalServiceShape;
@@ -18,23 +25,37 @@ export class ExtHostTerminal implements vscode.Terminal {
private _pidPromise: Promise<number>;
private _pidPromiseComplete: (value: number) => any;
private readonly _onData: Emitter<string> = new Emitter<string>();
public get onData(): Event<string> {
// Tell the main side to start sending data if it's not already
this._proxy.$registerOnDataListener(this._id);
return this._onData && this._onData.event;
}
constructor(
proxy: MainThreadTerminalServiceShape,
name?: string,
name: string = '',
id?: number
) {
this._proxy = proxy;
this._name = name;
if (id) {
this._id = id;
}
this._queuedRequests = [];
this._pidPromise = new Promise<number>(c => {
this._pidPromiseComplete = c;
});
}
public create(
shellPath?: string,
shellArgs?: string[],
cwd?: string,
env?: { [key: string]: string },
waitOnExit?: boolean
) {
this._name = name;
this._queuedRequests = [];
this._proxy = proxy;
this._pidPromise = new Promise<number>(c => {
this._pidPromiseComplete = c;
});
this._proxy.$createTerminal(name, shellPath, shellArgs, cwd, env, waitOnExit).then((id) => {
): void {
this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit).then((id) => {
this._id = id;
this._queuedRequests.forEach((r) => {
r.run(this._proxy, this._id);
@@ -44,12 +65,10 @@ export class ExtHostTerminal implements vscode.Terminal {
}
public get name(): string {
this._checkDisposed();
return this._name;
}
public get processId(): Thenable<number> {
this._checkDisposed();
return this._pidPromise;
}
@@ -76,8 +95,15 @@ export class ExtHostTerminal implements vscode.Terminal {
}
public _setProcessId(processId: number): void {
this._pidPromiseComplete(processId);
this._pidPromiseComplete = null;
// The event may fire 2 times when the panel is restored
if (this._pidPromiseComplete) {
this._pidPromiseComplete(processId);
this._pidPromiseComplete = null;
}
}
public _fireOnData(data: string): void {
this._onData.fire(data);
}
private _queueApiRequest(callback: (...args: any[]) => void, args: any[]) {
@@ -97,43 +123,69 @@ export class ExtHostTerminal implements vscode.Terminal {
}
export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _onDidCloseTerminal: Emitter<vscode.Terminal>;
private _proxy: MainThreadTerminalServiceShape;
private _terminals: ExtHostTerminal[];
private _terminals: ExtHostTerminal[] = [];
private _terminalProcesses: { [id: number]: cp.ChildProcess } = {};
constructor(mainContext: IMainContext) {
this._onDidCloseTerminal = new Emitter<vscode.Terminal>();
public get terminals(): ExtHostTerminal[] { return this._terminals; }
private readonly _onDidCloseTerminal: Emitter<vscode.Terminal> = new Emitter<vscode.Terminal>();
public get onDidCloseTerminal(): Event<vscode.Terminal> { return this._onDidCloseTerminal && this._onDidCloseTerminal.event; }
private readonly _onDidOpenTerminal: Emitter<vscode.Terminal> = new Emitter<vscode.Terminal>();
public get onDidOpenTerminal(): Event<vscode.Terminal> { return this._onDidOpenTerminal && this._onDidOpenTerminal.event; }
constructor(
mainContext: IMainContext,
private _extHostConfiguration: ExtHostConfiguration,
private _logService: ILogService
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService);
this._terminals = [];
}
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
let terminal = new ExtHostTerminal(this._proxy, name, shellPath, shellArgs);
let terminal = new ExtHostTerminal(this._proxy, name);
terminal.create(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.cwd, options.env /*, options.waitOnExit*/);
let terminal = new ExtHostTerminal(this._proxy, options.name);
terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env /*, options.waitOnExit*/);
this._terminals.push(terminal);
return terminal;
}
public get onDidCloseTerminal(): Event<vscode.Terminal> {
return this._onDidCloseTerminal && this._onDidCloseTerminal.event;
public $acceptTerminalProcessData(id: number, data: string): void {
let index = this._getTerminalIndexById(id);
if (index === null) {
return;
}
const terminal = this._terminals[index];
terminal._fireOnData(data);
}
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 $acceptTerminalOpened(id: number, name: string): void {
let index = this._getTerminalIndexById(id);
if (index !== null) {
// The terminal has already been created (via createTerminal*), only fire the event
this._onDidOpenTerminal.fire(this.terminals[index]);
return;
}
let terminal = new ExtHostTerminal(this._proxy, name, id);
this._terminals.push(terminal);
this._onDidOpenTerminal.fire(terminal);
}
public $acceptTerminalProcessId(id: number, processId: number): void {
let terminal = this._getTerminalById(id);
if (terminal) {
@@ -141,6 +193,99 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
}
public $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, cols: number, rows: number): void {
// TODO: This function duplicates a lot of TerminalProcessManager.createProcess, ideally
// they would be merged into a single implementation.
const terminalConfig = this._extHostConfiguration.getConfiguration('terminal.integrated');
const locale = terminalConfig.get('setLocaleVariables') ? platform.locale : undefined;
if (!shellLaunchConfig.executable) {
// TODO: This duplicates some of TerminalConfigHelper.mergeDefaultShellPathAndArgs and should be merged
// this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
const platformKey = platform.isWindows ? 'windows' : platform.isMacintosh ? 'osx' : 'linux';
const shellConfigValue: string = terminalConfig.get(`shell.${platformKey}`);
const shellArgsConfigValue: string = terminalConfig.get(`shellArgs.${platformKey}`);
shellLaunchConfig.executable = shellConfigValue;
shellLaunchConfig.args = shellArgsConfigValue;
}
// TODO: Base the cwd on the last active workspace root
// const lastActiveWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot('file');
// this.initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, lastActiveWorkspaceRootUri, this._configHelper);
const initialCwd = os.homedir();
// TODO: Pull in and resolve config settings
// // Resolve env vars from config and shell
// const lastActiveWorkspaceRoot = this._workspaceContextService.getWorkspaceFolder(lastActiveWorkspaceRootUri);
// const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
// const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot);
// const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
// shellLaunchConfig.env = envFromShell;
// Merge process env with the env from config
const parentEnv = { ...process.env };
// terminalEnvironment.mergeEnvironments(parentEnv, envFromConfig);
// Continue env initialization, merging in the env from the launch
// config and adding keys that are needed to create the process
const env = terminalEnvironment.createTerminalEnv(parentEnv, shellLaunchConfig, initialCwd, locale, cols, rows);
let cwd = Uri.parse(require.toUrl('../../parts/terminal/node')).fsPath;
const options = { env, cwd, execArgv: [] };
// Fork the process and listen for messages
this._logService.debug(`Terminal process launching on ext host`, options);
this._terminalProcesses[id] = cp.fork(Uri.parse(require.toUrl('bootstrap')).fsPath, ['--type=terminal'], options);
this._terminalProcesses[id].on('message', (message: IMessageFromTerminalProcess) => {
switch (message.type) {
case 'pid': this._proxy.$sendProcessPid(id, <number>message.content); break;
case 'title': this._proxy.$sendProcessTitle(id, <string>message.content); break;
case 'data': this._proxy.$sendProcessData(id, <string>message.content); break;
}
});
this._terminalProcesses[id].on('exit', (exitCode) => this._onProcessExit(id, exitCode));
}
public $acceptProcessInput(id: number, data: string): void {
if (this._terminalProcesses[id].connected) {
this._terminalProcesses[id].send({ event: 'input', data });
}
}
public $acceptProcessResize(id: number, cols: number, rows: number): void {
if (this._terminalProcesses[id].connected) {
try {
this._terminalProcesses[id].send({ event: 'resize', cols, rows });
} catch (error) {
// We tried to write to a closed pipe / channel.
if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') {
throw (error);
}
}
}
}
public $acceptProcessShutdown(id: number): void {
if (this._terminalProcesses[id].connected) {
this._terminalProcesses[id].send({ event: 'shutdown' });
}
}
private _onProcessExit(id: number, exitCode: number): void {
// Remove listeners
const process = this._terminalProcesses[id];
process.removeAllListeners('message');
process.removeAllListeners('exit');
// Remove process reference
delete this._terminalProcesses[id];
// Send exit event to main side
this._proxy.$sendProcessExit(id, exitCode);
}
private _getTerminalById(id: number): ExtHostTerminal {
let index = this._getTerminalIndexById(id);
return index !== null ? this._terminals[index] : null;
@@ -149,6 +294,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _getTerminalIndexById(id: number): number {
let index: number = null;
this._terminals.some((terminal, i) => {
// TODO: This shouldn't be cas
let thisId = (<any>terminal)._id;
if (thisId === id) {
index = i;
@@ -161,7 +307,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
class ApiRequest {
private _callback: (...args: any[]) => void;
private _args: any[];

View File

@@ -313,8 +313,6 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
export class ExtHostTextEditor implements vscode.TextEditor {
public readonly editorType = 'texteditor';
private readonly _proxy: MainThreadTextEditorsShape;
private readonly _id: string;
private readonly _documentData: ExtHostDocumentData;

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
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';

View File

@@ -38,14 +38,22 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
});
}
registerTreeDataProvider<T>(id: string, dataProvider: vscode.TreeDataProvider<T>, proposedApiFunction: <U>(fn: U) => U): vscode.TreeView<T> {
const treeView = this.createExtHostTreeViewer(id, dataProvider);
registerTreeDataProvider<T>(id: string, treeDataProvider: vscode.TreeDataProvider<T>): vscode.Disposable {
const treeView = this.createTreeView(id, { treeDataProvider });
return { dispose: () => treeView.dispose() };
}
createTreeView<T>(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider<T> }): vscode.TreeView<T> {
if (!options || !options.treeDataProvider) {
throw new Error('Options with treeDataProvider is mandatory');
}
const treeView = this.createExtHostTreeViewer(viewId, options.treeDataProvider);
return {
reveal: proposedApiFunction((element: T, options?: { select?: boolean }): Thenable<void> => {
reveal: (element: T, options?: { select?: boolean }): Thenable<void> => {
return treeView.reveal(element, options);
}),
},
dispose: () => {
this.treeViews.delete(id);
this.treeViews.delete(viewId);
treeView.dispose();
}
};
@@ -144,7 +152,7 @@ class ExtHostTreeView<T> extends Disposable {
private resolveTreeNode(element: T, parent?: TreeNode): TPromise<TreeNode> {
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => this.createHandle(element, extTreeItem, parent))
.then(extTreeItem => this.createHandle(element, extTreeItem, parent, true))
.then(handle => this.getChildren(parent ? parent.item.handle : null)
.then(() => {
const cachedElement = this.getExtensionElement(handle);
@@ -295,7 +303,7 @@ class ExtHostTreeView<T> extends Disposable {
return item;
}
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent?: TreeNode): TreeItemHandle {
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent: TreeNode, first?: boolean): TreeItemHandle {
if (id) {
return `${ExtHostTreeView.ID_HANDLE_PREFIX}/${id}`;
}
@@ -308,7 +316,7 @@ class ExtHostTreeView<T> extends Disposable {
for (let counter = 0; counter <= childrenNodes.length; counter++) {
const handle = `${prefix}/${counter}:${elementId}`;
if (!this.elements.has(handle) || existingHandle === handle) {
if (first || !this.elements.has(handle) || existingHandle === handle) {
return handle;
}
}

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
'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, ITextEditorOptions } from 'vs/platform/editor/common/editor';
@@ -21,6 +20,7 @@ import * as htmlContent from 'vs/base/common/htmlContent';
import { IRelativePattern } from 'vs/base/common/glob';
import { LanguageSelector, LanguageFilter } from 'vs/editor/common/modes/languageSelector';
import { WorkspaceEditDto, ResourceTextEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { MarkerSeverity, IRelatedInformation, IMarkerData } from 'vs/platform/markers/common/markers';
export interface PositionLike {
line: number;
@@ -83,34 +83,58 @@ 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 fromDiagnostic(value: vscode.Diagnostic): IMarkerData {
return {
...fromRange(value.range),
message: value.message,
source: value.source,
code: String(value.code),
severity: fromDiagnosticSeverity(value.severity),
relatedInformation: value.relatedInformation && value.relatedInformation.map(fromDiagnosticRelatedInformation)
};
}
export function toDiagnosticSeverty(value: Severity): types.DiagnosticSeverity {
export function fromDiagnosticRelatedInformation(value: types.DiagnosticRelatedInformation): IRelatedInformation {
return {
...fromRange(value.location.range),
message: value.message,
resource: value.location.uri
};
}
export function toDiagnosticRelatedInformation(value: IRelatedInformation): types.DiagnosticRelatedInformation {
return new types.DiagnosticRelatedInformation(new types.Location(value.resource, toRange(value)), value.message);
}
export function fromDiagnosticSeverity(value: number): MarkerSeverity {
switch (value) {
case Severity.Info:
case types.DiagnosticSeverity.Error:
return MarkerSeverity.Error;
case types.DiagnosticSeverity.Warning:
return MarkerSeverity.Warning;
case types.DiagnosticSeverity.Information:
return MarkerSeverity.Info;
case types.DiagnosticSeverity.Hint:
return MarkerSeverity.Hint;
}
return MarkerSeverity.Error;
}
export function toDiagnosticSeverty(value: MarkerSeverity): types.DiagnosticSeverity {
switch (value) {
case MarkerSeverity.Info:
return types.DiagnosticSeverity.Information;
case Severity.Warning:
case MarkerSeverity.Warning:
return types.DiagnosticSeverity.Warning;
case Severity.Error:
case MarkerSeverity.Error:
return types.DiagnosticSeverity.Error;
case Severity.Ignore:
case MarkerSeverity.Hint:
return types.DiagnosticSeverity.Hint;
}
return types.DiagnosticSeverity.Error;
}
export function fromViewColumn(column?: vscode.ViewColumn): EditorPosition {
let editorColumn = EditorPosition.ONE;
if (typeof column !== 'number') {
@@ -220,7 +244,7 @@ export const TextEdit = {
range: fromRange(edit.range)
};
},
to(edit: modes.TextEdit): vscode.TextEdit {
to(edit: modes.TextEdit): types.TextEdit {
let result = new types.TextEdit(toRange(edit.range), edit.text);
result.newEol = EndOfLine.to(edit.eol);
return result;
@@ -522,12 +546,15 @@ export namespace DocumentLink {
}
export namespace ColorPresentation {
export function to(colorPresentation: modes.IColorPresentation): vscode.ColorPresentation {
return {
label: colorPresentation.label,
textEdit: colorPresentation.textEdit ? TextEdit.to(colorPresentation.textEdit) : undefined,
additionalTextEdits: colorPresentation.additionalTextEdits ? colorPresentation.additionalTextEdits.map(value => TextEdit.to(value)) : undefined
};
export function to(colorPresentation: modes.IColorPresentation): types.ColorPresentation {
let cp = new types.ColorPresentation(colorPresentation.label);
if (colorPresentation.textEdit) {
cp.textEdit = TextEdit.to(colorPresentation.textEdit);
}
if (colorPresentation.additionalTextEdits) {
cp.additionalTextEdits = colorPresentation.additionalTextEdits.map(value => TextEdit.to(value));
}
return cp;
}
export function from(colorPresentation: vscode.ColorPresentation): modes.IColorPresentation {
@@ -539,6 +566,15 @@ export namespace ColorPresentation {
}
}
export namespace Color {
export function to(c: [number, number, number, number]): types.Color {
return new types.Color(c[0], c[1], c[2], c[3]);
}
export function from(color: types.Color): [number, number, number, number] {
return [color.red, color.green, color.blue, color.alpha];
}
}
export namespace TextDocumentSaveReason {
export function to(reason: SaveReason): vscode.TextDocumentSaveReason {
@@ -581,16 +617,35 @@ export namespace ProgressLocation {
switch (loc) {
case types.ProgressLocation.SourceControl: return MainProgressLocation.Scm;
case types.ProgressLocation.Window: return MainProgressLocation.Window;
case types.ProgressLocation.Notification: return MainProgressLocation.Notification;
}
return undefined;
}
}
export namespace FoldingRangeList {
export function from(rangeList: vscode.FoldingRangeList): modes.IFoldingRangeList {
return {
ranges: rangeList.ranges.map(r => ({ startLineNumber: r.startLine + 1, endLineNumber: r.endLine + 1, type: r.type }))
};
export namespace FoldingRange {
export function from(r: vscode.FoldingRange): modes.FoldingRange {
let range: modes.FoldingRange = { start: r.start + 1, end: r.end + 1 };
if (r.kind) {
range.kind = FoldingRangeKind.from(r.kind);
}
return range;
}
}
export namespace FoldingRangeKind {
export function from(kind: vscode.FoldingRangeKind | undefined): modes.FoldingRangeKind | undefined {
if (kind) {
switch (kind) {
case types.FoldingRangeKind.Comment:
return modes.FoldingRangeKind.Comment;
case types.FoldingRangeKind.Imports:
return modes.FoldingRangeKind.Imports;
case types.FoldingRangeKind.Region:
return modes.FoldingRangeKind.Region;
}
}
return void 0;
}
}
@@ -641,7 +696,8 @@ function doToLanguageSelector(selector: string | vscode.DocumentFilter): string
return {
language: selector.language,
scheme: selector.scheme,
pattern: toGlobPattern(selector.pattern)
pattern: toGlobPattern(selector.pattern),
exclusive: selector.exclusive
};
}

View File

@@ -208,7 +208,7 @@ export class Position {
export class Range {
static isRange(thing: any): thing is Range {
static isRange(thing: any): thing is vscode.Range {
if (thing instanceof Range) {
return true;
}
@@ -718,6 +718,27 @@ export class Location {
}
}
export class DiagnosticRelatedInformation {
static is(thing: any): thing is DiagnosticRelatedInformation {
if (!thing) {
return false;
}
return typeof (<DiagnosticRelatedInformation>thing).message === 'string'
&& (<DiagnosticRelatedInformation>thing).location
&& Range.isRange((<DiagnosticRelatedInformation>thing).location.range)
&& URI.isUri((<DiagnosticRelatedInformation>thing).location.uri);
}
location: Location;
message: string;
constructor(location: Location, message: string) {
this.location = location;
this.message = message;
}
}
export class Diagnostic {
range: Range;
@@ -725,6 +746,7 @@ export class Diagnostic {
source: string;
code: string | number;
severity: DiagnosticSeverity;
relatedInformation: DiagnosticRelatedInformation[];
constructor(range: Range, message: string, severity: DiagnosticSeverity = DiagnosticSeverity.Error) {
this.range = range;
@@ -854,6 +876,38 @@ export class SymbolInformation {
}
}
export class HierarchicalSymbolInformation {
name: string;
location: Location;
kind: SymbolKind;
range: Range;
children: HierarchicalSymbolInformation[];
constructor(name: string, kind: SymbolKind, location: Location, range: Range) {
this.name = name;
this.kind = kind;
this.location = location;
this.range = range;
this.children = [];
}
static toFlatSymbolInformation(info: HierarchicalSymbolInformation): SymbolInformation[] {
let result: SymbolInformation[] = [];
HierarchicalSymbolInformation._toFlatSymbolInformation(info, undefined, result);
return result;
}
private static _toFlatSymbolInformation(info: HierarchicalSymbolInformation, containerName: string, bucket: SymbolInformation[]): void {
bucket.push(new SymbolInformation(info.name, info.kind, containerName, new Location(info.location.uri, info.range)));
if (Array.isArray(info.children)) {
for (const child of info.children) {
HierarchicalSymbolInformation._toFlatSymbolInformation(child, info.name, bucket);
}
}
}
}
export class CodeAction {
title: string;
@@ -881,6 +935,8 @@ export class CodeActionKind {
public static readonly RefactorExtract = CodeActionKind.Refactor.append('extract');
public static readonly RefactorInline = CodeActionKind.Refactor.append('inline');
public static readonly RefactorRewrite = CodeActionKind.Refactor.append('rewrite');
public static readonly Source = CodeActionKind.Empty.append('source');
public static readonly SourceOrganizeImports = CodeActionKind.Source.append('organizeImports');
constructor(
public readonly value: string
@@ -1235,6 +1291,21 @@ export class TaskGroup implements vscode.TaskGroup {
public static Test: TaskGroup = new TaskGroup('test', 'Test');
public static from(value: string) {
switch (value) {
case 'clean':
return TaskGroup.Clean;
case 'build':
return TaskGroup.Build;
case 'rebuild':
return TaskGroup.Rebuild;
case 'test':
return TaskGroup.Test;
default:
return undefined;
}
}
constructor(id: string, _label: string) {
if (typeof id !== 'string') {
throw illegalArgument('name');
@@ -1306,19 +1377,49 @@ export class ProcessExecution implements vscode.ProcessExecution {
set options(value: vscode.ProcessExecutionOptions) {
this._options = value;
}
public computeId(): string {
const hash = crypto.createHash('md5');
hash.update('process');
if (this._process !== void 0) {
hash.update(this._process);
}
if (this._args && this._args.length > 0) {
for (let arg of this._args) {
hash.update(arg);
}
}
return hash.digest('hex');
}
}
export class ShellExecution implements vscode.ShellExecution {
private _commandLine: string;
private _command: string | vscode.ShellQuotedString;
private _args: (string | vscode.ShellQuotedString)[];
private _options: vscode.ShellExecutionOptions;
constructor(commandLine: string, options?: vscode.ShellExecutionOptions) {
if (typeof commandLine !== 'string') {
throw illegalArgument('commandLine');
constructor(commandLine: string, options?: vscode.ShellExecutionOptions);
constructor(command: string | vscode.ShellQuotedString, args: (string | vscode.ShellQuotedString)[], options?: vscode.ShellExecutionOptions);
constructor(arg0: string | vscode.ShellQuotedString, arg1?: vscode.ShellExecutionOptions | (string | vscode.ShellQuotedString)[], arg2?: vscode.ShellExecutionOptions) {
if (Array.isArray(arg1)) {
if (!arg0) {
throw illegalArgument('command can\'t be undefined or null');
}
if (typeof arg0 !== 'string' && typeof arg0.value !== 'string') {
throw illegalArgument('command');
}
this._command = arg0;
this._args = arg1 as (string | vscode.ShellQuotedString)[];
this._options = arg2;
} else {
if (typeof arg0 !== 'string') {
throw illegalArgument('commandLine');
}
this._commandLine = arg0;
this._options = arg1;
}
this._commandLine = commandLine;
this._options = options;
}
get commandLine(): string {
@@ -1332,6 +1433,25 @@ export class ShellExecution implements vscode.ShellExecution {
this._commandLine = value;
}
get command(): string | vscode.ShellQuotedString {
return this._command;
}
set command(value: string | vscode.ShellQuotedString) {
if (typeof value !== 'string' && typeof value.value !== 'string') {
throw illegalArgument('command');
}
this._command = value;
}
get args(): (string | vscode.ShellQuotedString)[] {
return this._args;
}
set args(value: (string | vscode.ShellQuotedString)[]) {
this._args = value || [];
}
get options(): vscode.ShellExecutionOptions {
return this._options;
}
@@ -1339,6 +1459,29 @@ export class ShellExecution implements vscode.ShellExecution {
set options(value: vscode.ShellExecutionOptions) {
this._options = value;
}
public computeId(): string {
const hash = crypto.createHash('md5');
hash.update('shell');
if (this._commandLine !== void 0) {
hash.update(this._commandLine);
}
if (this._command !== void 0) {
hash.update(typeof this._command === 'string' ? this._command : this._command.value);
}
if (this._args && this._args.length > 0) {
for (let arg of this._args) {
hash.update(typeof arg === 'string' ? arg : arg.value);
}
}
return hash.digest('hex');
}
}
export enum ShellQuoting {
Escape = 1,
Strong = 2,
Weak = 3
}
export enum TaskScope {
@@ -1348,6 +1491,8 @@ export enum TaskScope {
export class Task implements vscode.Task {
private __id: string;
private _definition: vscode.TaskDefinition;
private _definitionKey: string;
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder;
@@ -1396,6 +1541,35 @@ export class Task implements vscode.Task {
this._isBackground = false;
}
get _id(): string {
return this.__id;
}
set _id(value: string) {
this.__id = value;
}
private clear(): void {
if (this.__id === void 0) {
return;
}
this.__id = undefined;
this._scope = undefined;
this._definitionKey = undefined;
this._definition = undefined;
if (this._execution instanceof ProcessExecution) {
this._definition = {
type: 'process',
id: this._execution.computeId()
};
} else if (this._execution instanceof ShellExecution) {
this._definition = {
type: 'shell',
id: this._execution.computeId()
};
}
}
get definition(): vscode.TaskDefinition {
return this._definition;
}
@@ -1404,6 +1578,7 @@ export class Task implements vscode.Task {
if (value === void 0 || value === null) {
throw illegalArgument('Kind can\'t be undefined or null');
}
this.clear();
this._definitionKey = undefined;
this._definition = value;
}
@@ -1422,6 +1597,7 @@ export class Task implements vscode.Task {
}
set target(value: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder) {
this.clear();
this._scope = value;
}
@@ -1433,6 +1609,7 @@ export class Task implements vscode.Task {
if (typeof value !== 'string') {
throw illegalArgument('name');
}
this.clear();
this._name = value;
}
@@ -1444,6 +1621,7 @@ export class Task implements vscode.Task {
if (value === null) {
value = undefined;
}
this.clear();
this._execution = value;
}
@@ -1457,6 +1635,7 @@ export class Task implements vscode.Task {
this._hasDefinedMatchers = false;
return;
}
this.clear();
this._problemMatchers = value;
this._hasDefinedMatchers = true;
}
@@ -1473,6 +1652,7 @@ export class Task implements vscode.Task {
if (value !== true && value !== false) {
value = false;
}
this.clear();
this._isBackground = value;
}
@@ -1484,6 +1664,7 @@ export class Task implements vscode.Task {
if (typeof value !== 'string' || value.length === 0) {
throw illegalArgument('source must be a string of length > 0');
}
this.clear();
this._source = value;
}
@@ -1496,6 +1677,7 @@ export class Task implements vscode.Task {
this._group = undefined;
return;
}
this.clear();
this._group = value;
}
@@ -1507,6 +1689,7 @@ export class Task implements vscode.Task {
if (value === null) {
value = undefined;
}
this.clear();
this._presentationOptions = value;
}
}
@@ -1515,6 +1698,7 @@ export class Task implements vscode.Task {
export enum ProgressLocation {
SourceControl = 1,
Window = 10,
Notification = 15
}
export class TreeItem {
@@ -1600,8 +1784,9 @@ export class Breakpoint {
readonly enabled: boolean;
readonly condition?: string;
readonly hitCondition?: string;
readonly logMessage?: string;
protected constructor(enabled?: boolean, condition?: string, hitCondition?: string) {
protected constructor(enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) {
this.enabled = typeof enabled === 'boolean' ? enabled : true;
if (typeof condition === 'string') {
this.condition = condition;
@@ -1609,14 +1794,17 @@ export class Breakpoint {
if (typeof hitCondition === 'string') {
this.hitCondition = hitCondition;
}
if (typeof logMessage === 'string') {
this.logMessage = logMessage;
}
}
}
export class SourceBreakpoint extends Breakpoint {
readonly location: Location;
constructor(location: Location, enabled?: boolean, condition?: string, hitCondition?: string) {
super(enabled, condition, hitCondition);
constructor(location: Location, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) {
super(enabled, condition, hitCondition, logMessage);
if (location === null) {
throw illegalArgument('location');
}
@@ -1627,8 +1815,8 @@ export class SourceBreakpoint extends Breakpoint {
export class FunctionBreakpoint extends Breakpoint {
readonly functionName: string;
constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string) {
super(enabled, condition, hitCondition);
constructor(functionName: string, enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string) {
super(enabled, condition, hitCondition, logMessage);
if (!functionName) {
throw illegalArgument('functionName');
}
@@ -1661,59 +1849,85 @@ export enum LogLevel {
//#region file api
// todo@remote
export enum FileChangeType {
export enum DeprecatedFileChangeType {
Updated = 0,
Added = 1,
Deleted = 2
}
export enum FileType {
export enum FileChangeType {
Changed = 1,
Created = 2,
Deleted = 3,
}
export enum DeprecatedFileType {
File = 0,
Dir = 1,
Symlink = 2
}
export class FileSystemError extends Error {
static FileExists(messageOrUri?: string | URI): FileSystemError {
return new FileSystemError(messageOrUri, 'EntryExists', FileSystemError.FileExists);
}
static FileNotFound(messageOrUri?: string | URI): FileSystemError {
return new FileSystemError(messageOrUri, 'EntryNotFound', FileSystemError.FileNotFound);
}
static FileNotADirectory(messageOrUri?: string | URI): FileSystemError {
return new FileSystemError(messageOrUri, 'EntryNotADirectory', FileSystemError.FileNotADirectory);
}
static FileIsADirectory(messageOrUri?: string | URI): FileSystemError {
return new FileSystemError(messageOrUri, 'EntryIsADirectory', FileSystemError.FileIsADirectory);
}
static NoPermissions(messageOrUri?: string | URI): FileSystemError {
return new FileSystemError(messageOrUri, 'NoPermissions', FileSystemError.NoPermissions);
}
static Unavailable(messageOrUri?: string | URI): FileSystemError {
return new FileSystemError(messageOrUri, 'Unavailable', FileSystemError.Unavailable);
}
constructor(uriOrMessage?: string | URI, code?: string, terminator?: Function) {
super(URI.isUri(uriOrMessage) ? uriOrMessage.toString(true) : uriOrMessage);
this.name = code ? `${code} (FileSystemError)` : `FileSystemError`;
// workaround when extending builtin objects and when compiling to ES5, see:
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
if (typeof (<any>Object).setPrototypeOf === 'function') {
(<any>Object).setPrototypeOf(this, FileSystemError.prototype);
}
if (typeof Error.captureStackTrace === 'function' && typeof terminator === 'function') {
// nice stack traces
Error.captureStackTrace(this, terminator);
}
}
}
//#endregion
//#region folding api
export class FoldingRangeList {
ranges: FoldingRange[];
constructor(ranges: FoldingRange[]) {
this.ranges = ranges;
}
}
export class FoldingRange {
startLine: number;
start: number;
endLine: number;
end: number;
type?: FoldingRangeType | string;
kind?: FoldingRangeKind;
constructor(startLine: number, endLine: number, type?: FoldingRangeType | string) {
this.startLine = startLine;
this.endLine = endLine;
this.type = type;
constructor(start: number, end: number, kind?: FoldingRangeKind) {
this.start = start;
this.end = end;
this.kind = kind;
}
}
export enum FoldingRangeType {
/**
* Folding range for a comment
*/
Comment = 'comment',
/**
* Folding range for a imports or includes
*/
Imports = 'imports',
/**
* Folding range for a region (e.g. `#region`)
*/
Region = 'region'
export enum FoldingRangeKind {
Comment = 1,
Imports = 2,
Region = 3
}
//#endregion
//#endregion

View File

@@ -0,0 +1,53 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { MainContext, IMainContext, ExtHostUrlsShape, MainThreadUrlsShape } from './extHost.protocol';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { toDisposable } from 'vs/base/common/lifecycle';
export class ExtHostUrls implements ExtHostUrlsShape {
private static HandlePool = 0;
private readonly _proxy: MainThreadUrlsShape;
private handles = new Set<string>();
private handlers = new Map<number, vscode.ProtocolHandler>();
constructor(
mainContext: IMainContext
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadUrls);
}
registerProtocolHandler(extensionId: string, handler: vscode.ProtocolHandler): vscode.Disposable {
if (this.handles.has(extensionId)) {
throw new Error(`Protocol handler already registered for extension ${extensionId}`);
}
const handle = ExtHostUrls.HandlePool++;
this.handles.add(extensionId);
this.handlers.set(handle, handler);
this._proxy.$registerProtocolHandler(handle, extensionId);
return toDisposable(() => {
this.handles.delete(extensionId);
this.handlers.delete(handle);
this._proxy.$unregisterProtocolHandler(handle);
});
}
$handleExternalUri(handle: number, uri: UriComponents): TPromise<void> {
const handler = this.handlers.get(handle);
if (!handler) {
return TPromise.as(null);
}
handler.handleUri(URI.revive(uri));
return TPromise.as(null);
}
}

View File

@@ -3,71 +3,36 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainContext, MainThreadWebviewsShape, IMainContext, ExtHostWebviewsShape, WebviewHandle } from './extHost.protocol';
import { MainContext, MainThreadWebviewsShape, IMainContext, ExtHostWebviewsShape, WebviewPanelHandle } from './extHost.protocol';
import * as vscode from 'vscode';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { Position } from 'vs/platform/editor/common/editor';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from './extHostTypes';
export class ExtHostWebview implements vscode.Webview {
public readonly editorType = 'webview';
private _title: string;
private readonly _handle: WebviewPanelHandle;
private readonly _proxy: MainThreadWebviewsShape;
private _html: string;
private _options: vscode.WebviewOptions;
private _isDisposed: boolean = false;
private _viewColumn: vscode.ViewColumn;
public readonly onMessageEmitter = new Emitter<any>();
public readonly onDidReceiveMessage: Event<any> = this.onMessageEmitter.event;
public readonly onDisposeEmitter = new Emitter<void>();
public readonly onDidDispose: Event<void> = this.onDisposeEmitter.event;
public readonly onDidChangeViewColumnEmitter = new Emitter<vscode.ViewColumn>();
public readonly onDidChangeViewColumn: Event<vscode.ViewColumn> = this.onDidChangeViewColumnEmitter.event;
readonly _onMessageEmitter = new Emitter<any>();
public readonly onDidReceiveMessage: Event<any> = this._onMessageEmitter.event;
constructor(
private readonly _handle: WebviewHandle,
private readonly _proxy: MainThreadWebviewsShape,
private readonly _uri: vscode.Uri,
viewColumn: vscode.ViewColumn,
handle: WebviewPanelHandle,
proxy: MainThreadWebviewsShape,
options: vscode.WebviewOptions
) {
this._viewColumn = viewColumn;
this._handle = handle;
this._proxy = proxy;
this._options = options;
}
public dispose() {
if (this._isDisposed) {
return;
}
this._isDisposed = true;
this._proxy.$disposeWebview(this._handle);
this.onDisposeEmitter.dispose();
this.onMessageEmitter.dispose();
this.onDidChangeViewColumnEmitter.dispose();
}
get uri(): vscode.Uri {
this.assertNotDisposed();
return this._uri;
}
get title(): string {
this.assertNotDisposed();
return this._title;
}
set title(value: string) {
this.assertNotDisposed();
if (this._title !== value) {
this._title = value;
this._proxy.$setTitle(this._handle, value);
}
dispose() {
this._onMessageEmitter.dispose();
}
get html(): string {
@@ -88,22 +53,127 @@ export class ExtHostWebview implements vscode.Webview {
return this._options;
}
public postMessage(message: any): Thenable<boolean> {
this.assertNotDisposed();
return this._proxy.$postMessage(this._handle, message);
}
private assertNotDisposed() {
if (this._isDisposed) {
throw new Error('Webview is disposed');
}
}
}
export class ExtHostWebviewPanel implements vscode.WebviewPanel {
private readonly _handle: WebviewPanelHandle;
private readonly _proxy: MainThreadWebviewsShape;
private readonly _viewType: string;
private _title: string;
private readonly _options: vscode.WebviewPanelOptions;
private readonly _webview: ExtHostWebview;
private _isDisposed: boolean = false;
private _viewColumn: vscode.ViewColumn;
private _visible: boolean = true;
readonly _onDisposeEmitter = new Emitter<void>();
public readonly onDidDispose: Event<void> = this._onDisposeEmitter.event;
readonly _onDidChangeViewStateEmitter = new Emitter<vscode.WebviewPanelOnDidChangeViewStateEvent>();
public readonly onDidChangeViewState: Event<vscode.WebviewPanelOnDidChangeViewStateEvent> = this._onDidChangeViewStateEmitter.event;
constructor(
handle: WebviewPanelHandle,
proxy: MainThreadWebviewsShape,
viewType: string,
title: string,
viewColumn: vscode.ViewColumn,
editorOptions: vscode.WebviewPanelOptions,
webview: ExtHostWebview
) {
this._handle = handle;
this._proxy = proxy;
this._viewType = viewType;
this._options = editorOptions;
this._viewColumn = viewColumn;
this._title = title;
this._webview = webview;
}
public dispose() {
if (this._isDisposed) {
return;
}
this._isDisposed = true;
this._onDisposeEmitter.fire();
this._proxy.$disposeWebview(this._handle);
this._webview.dispose();
this._onDisposeEmitter.dispose();
this._onDidChangeViewStateEmitter.dispose();
}
get webview() {
this.assertNotDisposed();
return this._webview;
}
get viewType(): string {
this.assertNotDisposed();
return this._viewType;
}
get title(): string {
this.assertNotDisposed();
return this._title;
}
set title(value: string) {
this.assertNotDisposed();
if (this._title !== value) {
this._title = value;
this._proxy.$setTitle(this._handle, value);
}
}
get options() {
return this._options;
}
get viewColumn(): vscode.ViewColumn {
this.assertNotDisposed();
return this._viewColumn;
}
set viewColumn(value: vscode.ViewColumn) {
_setViewColumn(value: vscode.ViewColumn) {
this.assertNotDisposed();
this._viewColumn = value;
}
public postMessage(message: any): Thenable<boolean> {
return this._proxy.$sendMessage(this._handle, message);
get visible(): boolean {
this.assertNotDisposed();
return this._visible;
}
public show(viewColumn: vscode.ViewColumn): void {
this._proxy.$show(this._handle, typeConverters.fromViewColumn(viewColumn));
_setVisible(value: boolean) {
this.assertNotDisposed();
this._visible = value;
}
public postMessage(message: any): Thenable<boolean> {
this.assertNotDisposed();
return this._proxy.$postMessage(this._handle, message);
}
public reveal(viewColumn?: vscode.ViewColumn): void {
this.assertNotDisposed();
this._proxy.$reveal(this._handle, viewColumn ? typeConverters.fromViewColumn(viewColumn) : undefined);
}
private assertNotDisposed() {
@@ -114,11 +184,12 @@ export class ExtHostWebview implements vscode.Webview {
}
export class ExtHostWebviews implements ExtHostWebviewsShape {
private static handlePool = 0;
private static webviewHandlePool = 1;
private readonly _proxy: MainThreadWebviewsShape;
private readonly _webviews = new Map<WebviewHandle, ExtHostWebview>();
private readonly _webviewPanels = new Map<WebviewPanelHandle, ExtHostWebviewPanel>();
private readonly _serializers = new Map<string, vscode.WebviewPanelSerializer>();
constructor(
mainContext: IMainContext
@@ -127,54 +198,103 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
createWebview(
uri: vscode.Uri,
viewType: string,
title: string,
viewColumn: vscode.ViewColumn,
options: vscode.WebviewOptions
): vscode.Webview {
const handle = ExtHostWebviews.handlePool++;
this._proxy.$createWebview(handle, uri, title, typeConverters.fromViewColumn(viewColumn), options);
options: (vscode.WebviewPanelOptions & vscode.WebviewOptions) | undefined,
extensionFolderPath: string
): vscode.WebviewPanel {
options = options || {};
const handle = ExtHostWebviews.webviewHandlePool++ + '';
this._proxy.$createWebviewPanel(handle, viewType, title, typeConverters.fromViewColumn(viewColumn), options, extensionFolderPath);
const webview = new ExtHostWebview(handle, this._proxy, uri, viewColumn, options);
this._webviews.set(handle, webview);
return webview;
const webview = new ExtHostWebview(handle, this._proxy, options);
const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview);
this._webviewPanels.set(handle, panel);
return panel;
}
$onMessage(handle: WebviewHandle, message: any): void {
const webview = this.getWebview(handle);
if (webview) {
webview.onMessageEmitter.fire(message);
registerWebviewPanelSerializer(
viewType: string,
serializer: vscode.WebviewPanelSerializer
): vscode.Disposable {
if (this._serializers.has(viewType)) {
throw new Error(`Serializer for '${viewType}' already registered`);
}
this._serializers.set(viewType, serializer);
this._proxy.$registerSerializer(viewType);
return new Disposable(() => {
this._serializers.delete(viewType);
this._proxy.$unregisterSerializer(viewType);
});
}
$onMessage(handle: WebviewPanelHandle, message: any): void {
const panel = this.getWebviewPanel(handle);
if (panel) {
panel.webview._onMessageEmitter.fire(message);
}
}
$onDidChangeActiveWeview(handle: WebviewHandle | undefined): void {
this._onDidChangeActiveWebview.fire(this.getWebview(handle));
}
$onDidDisposeWeview(handle: WebviewHandle): Thenable<void> {
const webview = this.getWebview(handle);
if (webview) {
webview.onDisposeEmitter.fire();
this._webviews.delete(handle);
}
return TPromise.as(void 0);
}
$onDidChangePosition(handle: WebviewHandle, newPosition: Position): void {
const webview = this.getWebview(handle);
if (webview) {
const newViewColumn = typeConverters.toViewColumn(newPosition);
if (webview.viewColumn !== newViewColumn) {
webview.viewColumn = newViewColumn;
webview.onDidChangeViewColumnEmitter.fire(newViewColumn);
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, visible: boolean, position: Position): void {
const panel = this.getWebviewPanel(handle);
if (panel) {
const viewColumn = typeConverters.toViewColumn(position);
if (panel.visible !== visible || panel.viewColumn !== viewColumn) {
panel._setVisible(visible);
panel._setViewColumn(viewColumn);
panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel });
}
}
}
private readonly _onDidChangeActiveWebview = new Emitter<ExtHostWebview | undefined>();
public readonly onDidChangeActiveWebview = this._onDidChangeActiveWebview.event;
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Thenable<void> {
const panel = this.getWebviewPanel(handle);
if (panel) {
panel.dispose();
this._webviewPanels.delete(handle);
}
return TPromise.as(void 0);
}
private getWebview(handle: WebviewHandle) {
return this._webviews.get(handle);
$deserializeWebviewPanel(
webviewHandle: WebviewPanelHandle,
viewType: string,
title: string,
state: any,
position: Position,
options: vscode.WebviewOptions & vscode.WebviewPanelOptions
): Thenable<void> {
const serializer = this._serializers.get(viewType);
if (!serializer) {
return TPromise.wrapError(new Error(`No serializer found for '${viewType}'`));
}
const webview = new ExtHostWebview(webviewHandle, this._proxy, options);
const revivedPanel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeConverters.toViewColumn(position), options, webview);
this._webviewPanels.set(webviewHandle, revivedPanel);
return serializer.deserializeWebviewPanel(revivedPanel, state);
}
$serializeWebviewPanel(
webviewHandle: WebviewPanelHandle
): Thenable<any> {
const panel = this.getWebviewPanel(webviewHandle);
if (!panel) {
return TPromise.as(undefined);
}
const serialzer = this._serializers.get(panel.viewType);
if (!serialzer) {
return TPromise.as(undefined);
}
return serialzer.serializeWebviewPanel(panel);
}
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewPanel | undefined {
return this._webviewPanels.get(handle);
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostWindowShape, MainContext, MainThreadWindowShape, IMainContext } from './extHost.protocol';
import { WindowState } from 'vscode';

View File

@@ -5,10 +5,10 @@
'use strict';
import URI from 'vs/base/common/uri';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { normalize } from 'vs/base/common/paths';
import { delta as arrayDelta } from 'vs/base/common/arrays';
import { relative, dirname } from 'path';
import { relative, posix } from 'path';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext, MainThreadMessageServiceShape } from './extHost.protocol';
import * as vscode from 'vscode';
@@ -121,10 +121,14 @@ class ExtHostWorkspaceImpl extends Workspace {
getWorkspaceFolder(uri: URI, resolveParent?: boolean): vscode.WorkspaceFolder {
if (resolveParent && this._structure.get(uri.toString())) {
// `uri` is a workspace folder so we check for its parent
uri = uri.with({ path: dirname(uri.path) });
uri = uri.with({ path: posix.dirname(uri.path) });
}
return this._structure.findSubstr(uri.toString());
}
resolveWorkspaceFolder(uri: URI): vscode.WorkspaceFolder {
return this._structure.get(uri.toString());
}
}
export class ExtHostWorkspace implements ExtHostWorkspaceShape {
@@ -239,6 +243,13 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
return this._actualWorkspace.getWorkspaceFolder(uri, resolveParent);
}
resolveWorkspaceFolder(uri: vscode.Uri): vscode.WorkspaceFolder {
if (!this._actualWorkspace) {
return undefined;
}
return this._actualWorkspace.resolveWorkspaceFolder(uri);
}
getPath(): string {
// this is legacy from the days before having
@@ -352,7 +363,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
if (token) {
token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId));
}
return result.then(data => data.map(URI.revive));
return result.then(data => Array.isArray(data) ? data.map(URI.revive) : []);
}
saveAll(includeUntitled?: boolean): Thenable<boolean> {

View File

@@ -0,0 +1,94 @@
/*---------------------------------------------------------------------------------------------
* 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 { UriComponents } from 'vs/base/common/uri';
export interface TaskDefinitionDTO {
type: string;
[name: string]: any;
}
export interface TaskPresentationOptionsDTO {
reveal?: number;
echo?: boolean;
focus?: boolean;
panel?: number;
}
export interface ExecutionOptionsDTO {
cwd?: string;
env?: { [key: string]: string };
}
export interface ProcessExecutionOptionsDTO extends ExecutionOptionsDTO {
}
export interface ProcessExecutionDTO {
process: string;
args: string[];
options?: ProcessExecutionOptionsDTO;
}
export interface ShellQuotingOptionsDTO {
escape?: string | {
escapeChar: string;
charsToEscape: string;
};
strong?: string;
weak?: string;
}
export interface ShellExecutionOptionsDTO extends ExecutionOptionsDTO {
executable?: string;
shellArgs?: string[];
shellQuoting?: ShellQuotingOptionsDTO;
}
export interface ShellQuotedStringDTO {
value: string;
quoting: number;
}
export interface ShellExecutionDTO {
commandLine?: string;
command?: string | ShellQuotedStringDTO;
args?: (string | ShellQuotedStringDTO)[];
options?: ShellExecutionOptionsDTO;
}
export interface TaskSourceDTO {
label: string;
extensionId?: string;
scope?: number | UriComponents;
}
export interface TaskHandleDTO {
id: string;
workspaceFolder: UriComponents;
}
export interface TaskDTO {
_id: string;
name: string;
execution: ProcessExecutionDTO | ShellExecutionDTO;
definition: TaskDefinitionDTO;
isBackground: boolean;
source: TaskSourceDTO;
group?: string;
presentationOptions: TaskPresentationOptionsDTO;
problemMatchers: string[];
hasDefinedMatchers: boolean;
}
export interface TaskExecutionDTO {
id: string;
task: TaskDTO;
}
export interface TaskFilterDTO {
version?: string;
type?: string;
}

View File

@@ -6,7 +6,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
import types = require('vs/base/common/types');
import * as types from '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';

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from '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';

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
@@ -26,11 +26,11 @@ class ToggleCenteredLayout extends Action {
}
public run(): TPromise<any> {
this.partService.toggleCenteredEditorLayout();
this.partService.centerEditorLayout(!this.partService.isEditorLayoutCentered());
return TPromise.as(null);
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', nls.localize('view', "View"));
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleCenteredLayout, ToggleCenteredLayout.ID, ToggleCenteredLayout.LABEL), 'View: Toggle Centered Layout', nls.localize('view', "View"));

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from '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';

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from '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';

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from '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';

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from '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';

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from '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';

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from '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';

View File

@@ -7,7 +7,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';

View File

@@ -6,7 +6,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';

View File

@@ -4,18 +4,17 @@
*--------------------------------------------------------------------------------------------*/
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 { IComposite } from 'vs/workbench/common/composite';
import { IEditorControl } from 'vs/platform/editor/common/editor';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import DOM = require('vs/base/browser/dom');
import { IDisposable } from 'vs/base/common/lifecycle';
import { IFocusTracker, trackFocus, Dimension } from 'vs/base/browser/dom';
/**
* Composites are layed out in the sidebar and panel part of the workbench. At a time only one composite
@@ -28,14 +27,14 @@ import { IDisposable } from 'vs/base/common/lifecycle';
* layout and focus call, but only one create and dispose call.
*/
export abstract class Composite extends Component implements IComposite {
private _onTitleAreaUpdate: Emitter<void>;
private _onDidFocus: Emitter<void>;
private readonly _onTitleAreaUpdate: Emitter<void>;
private readonly _onDidFocus: Emitter<void>;
private _focusTracker?: DOM.IFocusTracker;
private _focusTracker?: IFocusTracker;
private _focusListenerDisposable?: IDisposable;
private visible: boolean;
private parent: Builder;
private parent: HTMLElement;
protected actionRunner: IActionRunner;
@@ -75,7 +74,7 @@ export abstract class Composite extends Component implements IComposite {
* 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> {
public create(parent: HTMLElement): TPromise<void> {
this.parent = parent;
return TPromise.as(null);
@@ -88,12 +87,12 @@ export abstract class Composite extends Component implements IComposite {
/**
* Returns the container this composite is being build in.
*/
public getContainer(): Builder {
public getContainer(): HTMLElement {
return this.parent;
}
public get onDidFocus(): Event<any> {
this._focusTracker = DOM.trackFocus(this.getContainer().getHTMLElement());
this._focusTracker = trackFocus(this.getContainer());
this._focusListenerDisposable = this._focusTracker.onDidFocus(() => {
this._onDidFocus.fire();
});
@@ -243,6 +242,10 @@ export abstract class CompositeDescriptor<T extends Composite> {
}
export abstract class CompositeRegistry<T extends Composite> {
private readonly _onDidRegister: Emitter<CompositeDescriptor<T>> = new Emitter<CompositeDescriptor<T>>();
readonly onDidRegister: Event<CompositeDescriptor<T>> = this._onDidRegister.event;
private composites: CompositeDescriptor<T>[];
constructor() {
@@ -255,6 +258,7 @@ export abstract class CompositeRegistry<T extends Composite> {
}
this.composites.push(descriptor);
this._onDidRegister.fire(descriptor);
}
public getComposite(id: string): CompositeDescriptor<T> {

View File

@@ -7,7 +7,7 @@
import { WORKSPACE_EXTENSION, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { extname, basename } from 'vs/base/common/paths';
import { IFileService, IFileStat } from 'vs/platform/files/common/files';
import { IFileService } from 'vs/platform/files/common/files';
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
import URI from 'vs/base/common/uri';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
@@ -348,7 +348,7 @@ export class SimpleFileResourceDragAndDrop extends DefaultDragAndDrop {
}
}
export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: (URI | IFileStat)[], event: DragMouseEvent | DragEvent): void {
export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: (URI | { resource: URI, isDirectory: boolean })[], event: DragMouseEvent | DragEvent): void {
if (resources.length === 0) {
return;
}
@@ -455,4 +455,4 @@ export class LocalSelectionTransfer<T> {
this.proto = proto;
}
}
}
}

View File

@@ -6,7 +6,7 @@
'use strict';
import uri from 'vs/base/common/uri';
import resources = require('vs/base/common/resources');
import * as resources from 'vs/base/common/resources';
import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IModeService } from 'vs/editor/common/services/modeService';
@@ -25,6 +25,7 @@ import { Schemas } from 'vs/base/common/network';
import { FileKind, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files';
import { ITextModel } from 'vs/editor/common/model';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Event, Emitter } from 'vs/base/common/event';
export interface IResourceLabel {
name: string;
@@ -38,11 +39,16 @@ export interface IResourceLabelOptions extends IIconLabelValueOptions {
}
export class ResourceLabel extends IconLabel {
private toDispose: IDisposable[];
private label: IResourceLabel;
private options: IResourceLabelOptions;
private computedIconClasses: string[];
private lastKnownConfiguredLangId: string;
private computedPathLabel: string;
private _onDidRender = new Emitter<void>();
readonly onDidRender: Event<void> = this._onDidRender.event;
constructor(
container: HTMLElement,
@@ -121,6 +127,10 @@ export class ResourceLabel extends IconLabel {
this.label = label;
this.options = options;
if (hasResourceChanged) {
this.computedPathLabel = void 0; // reset path label due to resource change
}
this.render(hasResourceChanged);
}
@@ -151,6 +161,7 @@ export class ResourceLabel extends IconLabel {
this.options = void 0;
this.lastKnownConfiguredLangId = void 0;
this.computedIconClasses = void 0;
this.computedPathLabel = void 0;
this.setValue();
}
@@ -184,7 +195,11 @@ export class ResourceLabel extends IconLabel {
if (this.options && typeof this.options.title === 'string') {
iconLabelOptions.title = this.options.title;
} else if (resource && resource.scheme !== Schemas.data /* do not accidentally inline Data URIs */) {
iconLabelOptions.title = getPathLabel(resource, void 0, this.environmentService);
if (!this.computedPathLabel) {
this.computedPathLabel = getPathLabel(resource, void 0, this.environmentService);
}
iconLabelOptions.title = this.computedPathLabel;
}
if (!this.computedIconClasses) {
@@ -197,7 +212,7 @@ export class ResourceLabel extends IconLabel {
}
if (this.options && this.options.fileDecorations && resource) {
let deco = this.decorationsService.getDecoration(
const deco = this.decorationsService.getDecoration(
resource,
this.options.fileKind !== FileKind.FILE,
this.options.fileDecorations.data
@@ -207,9 +222,11 @@ export class ResourceLabel extends IconLabel {
if (deco.tooltip) {
iconLabelOptions.title = `${iconLabelOptions.title}${deco.tooltip}`;
}
if (this.options.fileDecorations.colors) {
iconLabelOptions.extraClasses.push(deco.labelClassName);
}
if (this.options.fileDecorations.badges) {
iconLabelOptions.extraClasses.push(deco.badgeClassName);
}
@@ -217,6 +234,8 @@ export class ResourceLabel extends IconLabel {
}
this.setValue(label, this.label.description, iconLabelOptions);
this._onDidRender.fire();
}
public dispose(): void {
@@ -227,6 +246,7 @@ export class ResourceLabel extends IconLabel {
this.options = void 0;
this.lastKnownConfiguredLangId = void 0;
this.computedIconClasses = void 0;
this.computedPathLabel = void 0;
}
}

View File

@@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
'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 { QuickInputService } from 'vs/workbench/browser/parts/quickinput/quickInput';
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';
@@ -22,11 +22,14 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { memoize } from 'vs/base/common/decorators';
import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter';
import { NotificationsToasts } from 'vs/workbench/browser/parts/notifications/notificationsToasts';
import { Dimension, getClientArea, size, position, hide, show } from 'vs/base/browser/dom';
const MIN_SIDEBAR_PART_WIDTH = 170;
const DEFAULT_SIDEBAR_PART_WIDTH = 300;
const MIN_EDITOR_PART_HEIGHT = 70;
const MIN_EDITOR_PART_WIDTH = 220;
const MIN_PANEL_PART_HEIGHT = 77;
const DEFAULT_PANEL_PART_SIZE = 350;
const MIN_PANEL_PART_WIDTH = 300;
const DEFAULT_PANEL_SIZE_COEFFICIENT = 0.4;
const PANEL_SIZE_BEFORE_MAXIMIZED_BOUNDARY = 0.7;
@@ -56,8 +59,8 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
private static readonly sashYHeightSettingsKey = 'workbench.panel.height';
private static readonly panelSizeBeforeMaximizedKey = 'workbench.panel.sizeBeforeMaximized';
private parent: Builder;
private workbenchContainer: Builder;
private parent: HTMLElement;
private workbenchContainer: HTMLElement;
private titlebar: Part;
private activitybar: Part;
private editor: Part;
@@ -65,6 +68,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
private panel: Part;
private statusbar: Part;
private quickopen: QuickOpenController;
private quickInput: QuickInputService;
private notificationsCenter: NotificationsCenter;
private notificationsToasts: NotificationsToasts;
private toUnbind: IDisposable[];
@@ -84,8 +88,8 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
// Take parts as an object bag since instatation service does not have typings for constructors with 9+ arguments
constructor(
parent: Builder,
workbenchContainer: Builder,
parent: HTMLElement,
workbenchContainer: HTMLElement,
parts: {
titlebar: Part,
activitybar: Part,
@@ -95,6 +99,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
statusbar: Part
},
quickopen: QuickOpenController,
quickInput: QuickInputService,
notificationsCenter: NotificationsCenter,
notificationsToasts: NotificationsToasts,
@IStorageService private storageService: IStorageService,
@@ -114,28 +119,29 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
this.panel = parts.panel;
this.statusbar = parts.statusbar;
this.quickopen = quickopen;
this.quickInput = quickInput;
this.notificationsCenter = notificationsCenter;
this.notificationsToasts = notificationsToasts;
this.toUnbind = [];
this.panelSizeBeforeMaximized = this.storageService.getInteger(WorkbenchLayout.panelSizeBeforeMaximizedKey, StorageScope.GLOBAL, 0);
this.panelMaximized = false;
this.sashXOne = new Sash(this.workbenchContainer.getHTMLElement(), this, {
this.sashXOne = new Sash(this.workbenchContainer, this, {
baseSize: 5
});
this.sashXTwo = new Sash(this.workbenchContainer.getHTMLElement(), this, {
this.sashXTwo = new Sash(this.workbenchContainer, this, {
baseSize: 5
});
this.sashY = new Sash(this.workbenchContainer.getHTMLElement(), this, {
this.sashY = new Sash(this.workbenchContainer, this, {
baseSize: 4,
orientation: Orientation.HORIZONTAL
});
this._sidebarWidth = this.storageService.getInteger(WorkbenchLayout.sashXOneWidthSettingsKey, StorageScope.GLOBAL, -1);
this._panelHeight = this.storageService.getInteger(WorkbenchLayout.sashYHeightSettingsKey, StorageScope.GLOBAL, 0);
this._panelWidth = this.storageService.getInteger(WorkbenchLayout.sashXTwoWidthSettingsKey, StorageScope.GLOBAL, 0);
this._sidebarWidth = Math.max(this.partLayoutInfo.sidebar.minWidth, this.storageService.getInteger(WorkbenchLayout.sashXOneWidthSettingsKey, StorageScope.GLOBAL, DEFAULT_SIDEBAR_PART_WIDTH));
this._panelHeight = Math.max(this.partLayoutInfo.panel.minHeight, this.storageService.getInteger(WorkbenchLayout.sashYHeightSettingsKey, StorageScope.GLOBAL, DEFAULT_PANEL_PART_SIZE));
this._panelWidth = Math.max(this.partLayoutInfo.panel.minWidth, this.storageService.getInteger(WorkbenchLayout.sashXTwoWidthSettingsKey, StorageScope.GLOBAL, DEFAULT_PANEL_PART_SIZE));
this.layoutEditorGroupsVertically = (this.editorGroupService.getGroupOrientation() !== 'horizontal');
@@ -397,7 +403,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
this.toUnbind.push(this.sashXOne.onDidReset(() => {
let activeViewlet = this.viewletService.getActiveViewlet();
let optimalWidth = activeViewlet && activeViewlet.getOptimalWidth();
this.sidebarWidth = optimalWidth || 0;
this.sidebarWidth = Math.max(optimalWidth, DEFAULT_SIDEBAR_PART_WIDTH);
this.storageService.store(WorkbenchLayout.sashXOneWidthSettingsKey, this.sidebarWidth, StorageScope.GLOBAL);
this.partService.setSideBarHidden(false).done(() => this.layout(), errors.onUnexpectedError);
}));
@@ -441,7 +447,7 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
}
public layout(options?: ILayoutOptions): void {
this.workbenchSize = this.parent.getClientArea();
this.workbenchSize = getClientArea(this.parent);
const isActivityBarHidden = !this.partService.isVisible(Parts.ACTIVITYBAR_PART);
const isTitlebarHidden = !this.partService.isVisible(Parts.TITLEBAR_PART);
@@ -572,12 +578,11 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
}
// Workbench
this.workbenchContainer
.position(0, 0, 0, 0, 'relative')
.size(this.workbenchSize.width, this.workbenchSize.height);
position(this.workbenchContainer, 0, 0, 0, 0, 'relative');
size(this.workbenchContainer, 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();
const workbenchContainer = this.workbenchContainer;
if (workbenchContainer.scrollTop > 0) {
workbenchContainer.scrollTop = 0;
}
@@ -586,69 +591,78 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
}
// Title Part
const titleContainer = this.titlebar.getContainer();
if (isTitlebarHidden) {
this.titlebar.getContainer().hide();
hide(titleContainer);
} else {
this.titlebar.getContainer().show();
show(titleContainer);
}
// Editor Part and Panel part
this.editor.getContainer().size(editorSize.width, editorSize.height);
this.panel.getContainer().size(panelDimension.width, panelDimension.height);
const editorContainer = this.editor.getContainer();
const panelContainer = this.panel.getContainer();
size(editorContainer, editorSize.width, editorSize.height);
size(panelContainer, panelDimension.width, panelDimension.height);
if (panelPosition === Position.BOTTOM) {
if (sidebarPosition === Position.LEFT) {
this.editor.getContainer().position(this.titlebarHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width);
this.panel.getContainer().position(editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
position(editorContainer, this.titlebarHeight, 0, this.statusbarHeight + panelDimension.height, sidebarSize.width + activityBarSize.width);
position(panelContainer, editorSize.height + this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
} else {
this.editor.getContainer().position(this.titlebarHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0);
this.panel.getContainer().position(editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0);
position(editorContainer, this.titlebarHeight, sidebarSize.width, this.statusbarHeight + panelDimension.height, 0);
position(panelContainer, editorSize.height + this.titlebarHeight, sidebarSize.width, this.statusbarHeight, 0);
}
} else {
if (sidebarPosition === Position.LEFT) {
this.editor.getContainer().position(this.titlebarHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
this.panel.getContainer().position(this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width);
position(editorContainer, this.titlebarHeight, panelDimension.width, this.statusbarHeight, sidebarSize.width + activityBarSize.width);
position(panelContainer, this.titlebarHeight, 0, this.statusbarHeight, sidebarSize.width + activityBarSize.width + editorSize.width);
} else {
this.editor.getContainer().position(this.titlebarHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0);
this.panel.getContainer().position(this.titlebarHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width);
position(editorContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width + panelWidth, this.statusbarHeight, 0);
position(panelContainer, this.titlebarHeight, sidebarSize.width + activityBarSize.width, this.statusbarHeight, editorSize.width);
}
}
// Activity Bar Part
this.activitybar.getContainer().size(null, activityBarSize.height);
const activitybarContainer = this.activitybar.getContainer();
size(activitybarContainer, null, activityBarSize.height);
if (sidebarPosition === Position.LEFT) {
this.activitybar.getContainer().getHTMLElement().style.right = '';
this.activitybar.getContainer().position(this.titlebarHeight, null, 0, 0);
this.activitybar.getContainer().style.right = '';
position(activitybarContainer, this.titlebarHeight, null, 0, 0);
} else {
this.activitybar.getContainer().getHTMLElement().style.left = '';
this.activitybar.getContainer().position(this.titlebarHeight, 0, 0, null);
this.activitybar.getContainer().style.left = '';
position(activitybarContainer, this.titlebarHeight, 0, 0, null);
}
if (isActivityBarHidden) {
this.activitybar.getContainer().hide();
hide(activitybarContainer);
} else {
this.activitybar.getContainer().show();
show(activitybarContainer);
}
// Sidebar Part
this.sidebar.getContainer().size(sidebarSize.width, sidebarSize.height);
const sidebarContainer = this.sidebar.getContainer();
size(sidebarContainer, sidebarSize.width, sidebarSize.height);
const editorAndPanelWidth = editorSize.width + (panelPosition === Position.RIGHT ? panelWidth : 0);
if (sidebarPosition === Position.LEFT) {
this.sidebar.getContainer().position(this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width);
position(sidebarContainer, this.titlebarHeight, editorAndPanelWidth, this.statusbarHeight, activityBarSize.width);
} else {
this.sidebar.getContainer().position(this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth);
position(sidebarContainer, this.titlebarHeight, activityBarSize.width, this.statusbarHeight, editorAndPanelWidth);
}
// Statusbar Part
this.statusbar.getContainer().position(this.workbenchSize.height - this.statusbarHeight);
const statusbarContainer = this.statusbar.getContainer();
position(statusbarContainer, this.workbenchSize.height - this.statusbarHeight);
if (isStatusbarHidden) {
this.statusbar.getContainer().hide();
hide(statusbarContainer);
} else {
this.statusbar.getContainer().show();
show(statusbarContainer);
}
// Quick open
this.quickopen.layout(this.workbenchSize);
// Quick input
this.quickInput.layout(this.workbenchSize);
// Notifications
this.notificationsCenter.layout(this.workbenchSize);
this.notificationsToasts.layout(this.workbenchSize);
@@ -786,4 +800,4 @@ export class WorkbenchLayout implements IVerticalSashLayoutProvider, IHorizontal
this.toUnbind = null;
}
}
}
}

View File

@@ -68,7 +68,7 @@
font-size: 13px;
}
.monaco-workbench > .part > .content .progress-container {
.monaco-workbench > .part > .content .monaco-progress-container {
position: absolute;
left: 0;
top: 33px; /* at the bottom of the 35px height title container */
@@ -76,6 +76,6 @@
height: 2px;
}
.monaco-workbench > .part > .content .progress-container .progress-bit {
.monaco-workbench > .part > .content .monaco-progress-container .progress-bit {
height: 2px;
}

View File

@@ -95,7 +95,7 @@ export abstract class TogglePanelAction extends Action {
const activePanel = this.panelService.getActivePanel();
const activeElement = document.activeElement;
return activePanel && activeElement && DOM.isAncestor(activeElement, (<Panel>activePanel).getContainer().getHTMLElement());
return activePanel && activeElement && DOM.isAncestor(activeElement, (<Panel>activePanel).getContainer());
}
}

View File

@@ -6,9 +6,9 @@
'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';
import { Dimension, size } from 'vs/base/browser/dom';
export interface IPartOptions {
hasTitle?: boolean;
@@ -20,9 +20,9 @@ export interface IPartOptions {
* and mandatory content area to show content.
*/
export abstract class Part extends Component {
private parent: Builder;
private titleArea: Builder;
private contentArea: Builder;
private parent: HTMLElement;
private titleArea: HTMLElement;
private contentArea: HTMLElement;
private partLayout: PartLayout;
constructor(
@@ -47,7 +47,7 @@ export abstract class Part extends Component {
*
* Called to create title and content area of the part.
*/
public create(parent: Builder): void {
public create(parent: HTMLElement): void {
this.parent = parent;
this.titleArea = this.createTitleArea(parent);
this.contentArea = this.createContentArea(parent);
@@ -60,35 +60,35 @@ export abstract class Part extends Component {
/**
* Returns the overall part container.
*/
public getContainer(): Builder {
public getContainer(): HTMLElement {
return this.parent;
}
/**
* Subclasses override to provide a title area implementation.
*/
protected createTitleArea(parent: Builder): Builder {
protected createTitleArea(parent: HTMLElement): HTMLElement {
return null;
}
/**
* Returns the title area container.
*/
protected getTitleArea(): Builder {
protected getTitleArea(): HTMLElement {
return this.titleArea;
}
/**
* Subclasses override to provide a content area implementation.
*/
protected createContentArea(parent: Builder): Builder {
protected createContentArea(parent: HTMLElement): HTMLElement {
return null;
}
/**
* Returns the content area container.
*/
protected getContentArea(): Builder {
protected getContentArea(): HTMLElement {
return this.contentArea;
}
@@ -104,8 +104,7 @@ const TITLE_HEIGHT = 35;
export class PartLayout {
constructor(container: Builder, private options: IPartOptions, titleArea: Builder, private contentArea: Builder) {
}
constructor(container: HTMLElement, private options: IPartOptions, titleArea: HTMLElement, private contentArea: HTMLElement) { }
public layout(dimension: Dimension): Dimension[] {
const { width, height } = dimension;
@@ -133,7 +132,7 @@ export class PartLayout {
// Content
if (this.contentArea) {
this.contentArea.size(contentSize.width, contentSize.height);
size(this.contentArea, contentSize.width, contentSize.height);
}
return sizes;

View File

@@ -6,7 +6,7 @@
'use strict';
import 'vs/css!./media/activityaction';
import DOM = require('vs/base/browser/dom');
import * as DOM from 'vs/base/browser/dom';
import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
@@ -22,6 +22,7 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ActivityAction, ActivityActionItem, ICompositeBarColors } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
export class ViewletActivityAction extends ActivityAction {
@@ -32,7 +33,8 @@ export class ViewletActivityAction extends ActivityAction {
constructor(
activity: IActivity,
@IViewletService private viewletService: IViewletService,
@IPartService private partService: IPartService
@IPartService private partService: IPartService,
@ITelemetryService private telemetryService: ITelemetryService
) {
super(activity);
}
@@ -54,11 +56,23 @@ export class ViewletActivityAction extends ActivityAction {
// Hide sidebar if selected viewlet already visible
if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.activity.id) {
this.logAction('hide');
return this.partService.setSideBarHidden(true);
}
this.logAction('show');
return this.viewletService.openViewlet(this.activity.id, true).then(() => this.activate());
}
private logAction(action: string) {
/* __GDPR__
"activityBarAction" : {
"viewletId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"action": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('activityBarAction', { viewletId: this.activity.id, action });
}
}
export class ToggleViewletAction extends Action {

View File

@@ -6,10 +6,10 @@
'use strict';
import 'vs/css!./media/activitybarpart';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { illegalArgument } from 'vs/base/common/errors';
import { Builder, $, Dimension } from 'vs/base/browser/builder';
import { $ } from 'vs/base/browser/builder';
import { Action } from 'vs/base/common/actions';
import { ActionsOrientation, ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/common/activity';
@@ -30,8 +30,10 @@ import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { CompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBar';
import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
// {{SQL CARBON EDIT}}
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ViewLocation, ViewsRegistry } from 'vs/workbench/common/views';
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { Dimension } from 'vs/base/browser/dom';
export class ActivitybarPart extends Part {
@@ -66,6 +68,7 @@ export class ActivitybarPart extends Part {
super(id, { hasTitle: false }, themeService);
this.globalActivityIdToActions = Object.create(null);
this.compositeBar = this.instantiationService.createInstance(CompositeBar, {
icon: true,
storageId: ActivitybarPart.PINNED_VIEWLETS,
@@ -80,7 +83,9 @@ export class ActivitybarPart extends Part {
colors: ActivitybarPart.COLORS,
overflowActionSize: ActivitybarPart.ACTION_HEIGHT
});
this.registerListeners();
this.updateCompositebar();
}
// {{SQL CARBON EDIT}}
@@ -97,6 +102,10 @@ export class ActivitybarPart extends Part {
private registerListeners(): void {
this.toUnbind.push(this.viewletService.onDidViewletRegister(() => this.updateCompositebar()));
this.toUnbind.push(ViewsRegistry.onViewsRegistered(() => this.updateCompositebar()));
this.toUnbind.push(ViewsRegistry.onViewsDeregistered(() => this.updateCompositebar()));
// Activate viewlet action on opening of a viewlet
this.toUnbind.push(this.viewletService.onDidViewletOpen(viewlet => this.compositeBar.activateComposite(viewlet.getId())));
@@ -105,7 +114,7 @@ export class ActivitybarPart extends Part {
this.toUnbind.push(this.compositeBar.onDidContextMenu(e => this.showContextMenu(e)));
this.toUnbind.push(this.viewletService.onDidViewletEnablementChange(({ id, enabled }) => {
if (enabled) {
this.compositeBar.addComposite(this.viewletService.getViewlet(id));
this.compositeBar.addComposite(this.viewletService.getViewlet(id), true);
} else {
this.compositeBar.removeComposite(id);
}
@@ -135,7 +144,7 @@ export class ActivitybarPart extends Part {
return toDisposable(() => action.setBadge(undefined));
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
const $el = $(parent);
const $result = $('.content').appendTo($el);
@@ -145,14 +154,14 @@ export class ActivitybarPart extends Part {
// Top Actionbar with action items for each viewlet action
this.createGlobalActivityActionBar($('.global-activity').appendTo($result).getHTMLElement());
return $result;
return $result.getHTMLElement();
}
public updateStyles(): void {
super.updateStyles();
// Part container
const container = this.getContainer();
const container = $(this.getContainer());
const background = this.getColor(ACTIVITY_BAR_BACKGROUND);
container.style('background-color', background);
@@ -170,7 +179,9 @@ export class ActivitybarPart extends Part {
private showContextMenu(e: MouseEvent): void {
const event = new StandardMouseEvent(e);
const actions: Action[] = this.viewletService.getViewlets().map(viewlet => this.instantiationService.createInstance(ToggleCompositePinnedAction, viewlet, this.compositeBar));
const actions: Action[] = this.viewletService.getViewlets()
.filter(viewlet => this.canShow(viewlet))
.map(viewlet => this.instantiationService.createInstance(ToggleCompositePinnedAction, viewlet, this.compositeBar));
actions.push(new Separator());
actions.push(this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar")));
@@ -202,6 +213,26 @@ export class ActivitybarPart extends Part {
});
}
private updateCompositebar(): void {
const viewlets = this.viewletService.getViewlets();
for (const viewlet of viewlets) {
const canShow = this.canShow(viewlet);
if (canShow) {
this.compositeBar.addComposite(viewlet, false);
} else {
this.compositeBar.removeComposite(viewlet.id);
}
}
}
private canShow(viewlet: ViewletDescriptor): boolean {
const viewLocation = ViewLocation.get(viewlet.id);
if (viewLocation) {
return ViewsRegistry.getViews(viewLocation).length > 0;
}
return true;
}
public getPinned(): string[] {
return this.viewletService.getViewlets().map(v => v.id).filter(id => this.compositeBar.isPinned(id));
}
@@ -229,6 +260,11 @@ export class ActivitybarPart extends Part {
return sizes;
}
public shutdown(): void {
this.compositeBar.shutdown();
super.shutdown();
}
public dispose(): void {
if (this.compositeBar) {
this.compositeBar.dispose();
@@ -242,4 +278,4 @@ export class ActivitybarPart extends Part {
super.dispose();
}
}
}

View File

@@ -19,7 +19,7 @@
background-color: inherit;
}
.monaco-workbench > .activitybar [tabindex="0"]:focus {
.monaco-workbench > .activitybar .action-item:focus {
outline: 0 !important; /* activity bar indicates focus custom */
}

View File

@@ -6,22 +6,20 @@
'use strict';
import 'vs/css!./media/compositepart';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import strings = require('vs/base/common/strings');
import { Builder, $ } from 'vs/base/browser/builder';
import * as strings from '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 * as types from 'vs/base/common/types';
import * as errors from 'vs/base/common/errors';
import { 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 { prepareActions } from 'vs/workbench/browser/actions';
import { Action, IAction, IRunEvent } from 'vs/base/common/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';
@@ -37,6 +35,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension } from 'vs/base/browser/dom';
export interface ICompositeTitleLabel {
@@ -74,10 +73,10 @@ export abstract class CompositePart<T extends Composite> extends Part {
private telemetryService: ITelemetryService,
protected contextMenuService: IContextMenuService,
protected partService: IPartService,
private keybindingService: IKeybindingService,
protected keybindingService: IKeybindingService,
protected instantiationService: IInstantiationService,
themeService: IThemeService,
private registry: CompositeRegistry<T>,
protected readonly registry: CompositeRegistry<T>,
private activeCompositeSettingsKey: string,
private defaultCompositeId: string,
private nameForTelemetry: string,
@@ -223,8 +222,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
compositeContainer = $().div({
'class': ['composite', this.compositeCSSClass],
id: composite.getId()
}, (div: Builder) => {
createCompositePromise = composite.create(div).then(() => {
}, div => {
createCompositePromise = composite.create(div.getHTMLElement()).then(() => {
composite.updateStyles();
});
});
@@ -279,7 +278,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
}
// Action Run Handling
this.telemetryActionsListener = this.toolBar.actionRunner.onDidRun((e: IRunEvent) => {
this.telemetryActionsListener = this.toolBar.actionRunner.onDidRun(e => {
// Check for Error
if (e.error && !errors.isPromiseCanceledError(e.error)) {
@@ -311,7 +310,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
composite.layout(this.contentAreaSize);
}
});
}, (error: any) => this.onError(error));
}, error => this.onError(error));
}
protected onTitleAreaUpdate(compositeId: string): void {
@@ -391,7 +390,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
compositeContainer.hide();
// Clear any running Progress
this.progressBar.stop().getContainer().hide();
this.progressBar.stop().hide();
// Empty Actions
this.toolBar.setActions([])();
@@ -401,39 +400,37 @@ export abstract class CompositePart<T extends Composite> extends Part {
});
}
public createTitleArea(parent: Builder): Builder {
public createTitleArea(parent: HTMLElement): HTMLElement {
// Title Area Container
const 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);
this.titleLabel = this.createTitleLabel(titleArea.getHTMLElement());
// Right Actions Container
$(titleArea).div({
'class': 'title-actions'
}, (div) => {
}, div => {
// Toolbar
this.toolBar = new ToolBar(div.getHTMLElement(), this.contextMenuService, {
actionItemProvider: (action: Action) => this.actionItemProvider(action),
actionItemProvider: action => this.actionItemProvider(action as Action),
orientation: ActionsOrientation.HORIZONTAL,
getKeyBinding: (action) => this.keybindingService.lookupKeybinding(action.id)
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id)
});
});
return titleArea;
return titleArea.getHTMLElement();
}
protected createTitleLabel(parent: Builder): ICompositeTitleLabel {
protected createTitleLabel(parent: HTMLElement): ICompositeTitleLabel {
let titleLabel: Builder;
$(parent).div({
'class': 'title-label'
}, (div) => {
}, div => {
titleLabel = div.span();
});
@@ -456,27 +453,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
this.titleLabel.updateStyles();
}
private onTitleAreaContextMenu(event: StandardMouseEvent): void {
if (this.activeComposite) {
const contextMenuActions = this.getTitleAreaContextMenuActions();
if (contextMenuActions.length) {
const 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 actionItemProvider(action: Action): IActionItem {
protected getTitleAreaContextMenuActions(): IAction[] {
return this.activeComposite ? this.activeComposite.getContextMenuActions() : [];
}
private actionItemProvider(action: Action): IActionItem {
// Check Active Composite
if (this.activeComposite) {
return this.activeComposite.getActionItem(action);
@@ -485,14 +463,14 @@ export abstract class CompositePart<T extends Composite> extends Part {
return undefined;
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
return $(parent).div({
'class': 'content'
}, (div: Builder) => {
this.progressBar = new ProgressBar(div);
}, div => {
this.progressBar = new ProgressBar(div.getHTMLElement());
this.toUnbind.push(attachProgressBarStyler(this.progressBar, this.themeService));
this.progressBar.getContainer().hide();
});
this.progressBar.hide();
}).getHTMLElement();
}
private onError(error: any): void {

View File

@@ -5,26 +5,25 @@
'use strict';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { illegalArgument } from 'vs/base/common/errors';
import * as dom from 'vs/base/browser/dom';
import * as arrays from 'vs/base/common/arrays';
import { Dimension } from 'vs/base/browser/builder';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IBadge } from 'vs/workbench/services/activity/common/activity';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ActionBar, IActionItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { CompositeActionItem, CompositeOverflowActivityAction, ICompositeActivity, CompositeOverflowActivityActionItem, ActivityAction, ICompositeBar, ICompositeBarColors } from 'vs/workbench/browser/parts/compositebar/compositeBarActions';
import { TPromise } from 'vs/base/common/winjs.base';
import { Dimension, $, addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom';
export interface ICompositeBarOptions {
icon: boolean;
storageId: string;
orientation: ActionsOrientation;
composites: { id: string, name: string }[];
composites: { id: string, name: string, order: number }[];
colors: ICompositeBarColors;
overflowActionSize: number;
getActivityAction: (compositeId: string) => ActivityAction;
@@ -35,9 +34,14 @@ export interface ICompositeBarOptions {
hidePart: () => TPromise<any>;
}
interface CompositeState {
id: string;
pinned: boolean;
}
export class CompositeBar implements ICompositeBar {
private _onDidContextMenu: Emitter<MouseEvent>;
private readonly _onDidContextMenu: Emitter<MouseEvent>;
private dimension: Dimension;
private toDispose: IDisposable[];
@@ -51,6 +55,7 @@ export class CompositeBar implements ICompositeBar {
private compositeIdToActivityStack: { [compositeId: string]: ICompositeActivity[]; };
private compositeSizeInBar: Map<string, number>;
private initialCompositesStates: CompositeState[];
private pinnedComposites: string[];
private activeCompositeId: string;
private activeUnpinnedCompositeId: string;
@@ -67,27 +72,36 @@ export class CompositeBar implements ICompositeBar {
this.compositeSizeInBar = new Map<string, number>();
this._onDidContextMenu = new Emitter<MouseEvent>();
const pinnedComposites = JSON.parse(this.storageService.get(this.options.storageId, StorageScope.GLOBAL, null)) as string[];
if (pinnedComposites) {
const compositeIds = this.options.composites.map(c => c.id);
this.pinnedComposites = pinnedComposites.filter(pcid => compositeIds.indexOf(pcid) >= 0);
} else {
this.pinnedComposites = this.options.composites.map(c => c.id);
}
this.initialCompositesStates = this.loadCompositesStates();
this.pinnedComposites = this.initialCompositesStates
.filter(c => c.pinned)
.map(c => c.id)
.filter(id => this.options.composites.some(c => c.id === id));
}
public get onDidContextMenu(): Event<MouseEvent> {
return this._onDidContextMenu.event;
}
public addComposite(compositeData: { id: string; name: string }): void {
public addComposite(compositeData: { id: string; name: string, order: number }, activate: boolean): void {
if (this.options.composites.filter(c => c.id === compositeData.id).length) {
return;
}
this.options.composites.push(compositeData);
this.pin(compositeData.id);
const compositeState = this.initialCompositesStates.filter(c => c.id === compositeData.id)[0];
if (!compositeState /* new composites are pinned by default */ || compositeState.pinned) {
let index;
if (compositeState) {
index = this.initialCompositesStates.indexOf(compositeState);
} else {
index = 0;
while (index < this.options.composites.length && this.options.composites[index].order < compositeData.order) {
index++;
}
}
this.pin(compositeData.id, true, index, activate);
}
}
public removeComposite(id: string): void {
@@ -97,6 +111,9 @@ export class CompositeBar implements ICompositeBar {
this.options.composites = this.options.composites.filter(c => c.id !== id);
this.unpin(id);
this.pullComposite(id);
// Only at the end deactivate composite so the unpin and pull properly finish
this.deactivateComposite(id);
}
public activateComposite(id: string): void {
@@ -119,6 +136,13 @@ export class CompositeBar implements ICompositeBar {
if (this.compositeIdToActions[id]) {
this.compositeIdToActions[id].deactivate();
}
if (this.activeCompositeId === id) {
this.activeCompositeId = undefined;
}
if (this.activeUnpinnedCompositeId === id) {
this.updateCompositeSwitcher();
this.activeUnpinnedCompositeId = undefined;
}
}
public showActivity(compositeId: string, badge: IBadge, clazz?: string, priority?: number): IDisposable {
@@ -191,7 +215,7 @@ export class CompositeBar implements ICompositeBar {
}
public create(parent: HTMLElement): HTMLElement {
const actionBarDiv = parent.appendChild(dom.$('.composite-bar'));
const actionBarDiv = parent.appendChild($('.composite-bar'));
this.compositeSwitcherBar = new ActionBar(actionBarDiv, {
actionItemProvider: (action: Action) => action instanceof CompositeOverflowActivityAction ? this.compositeOverflowActionItem : this.compositeIdToActionItems[action.id],
orientation: this.options.orientation,
@@ -201,16 +225,16 @@ export class CompositeBar implements ICompositeBar {
this.toDispose.push(this.compositeSwitcherBar);
// Contextmenu for composites
this.toDispose.push(dom.addDisposableListener(parent, dom.EventType.CONTEXT_MENU, (e: MouseEvent) => {
dom.EventHelper.stop(e, true);
this.toDispose.push(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => {
EventHelper.stop(e, true);
this._onDidContextMenu.fire(e);
}));
// Allow to drop at the end to move composites to the end
this.toDispose.push(dom.addDisposableListener(parent, dom.EventType.DROP, (e: DragEvent) => {
this.toDispose.push(addDisposableListener(parent, EventType.DROP, (e: DragEvent) => {
const draggedCompositeId = CompositeActionItem.getDraggedCompositeId();
if (draggedCompositeId) {
dom.EventHelper.stop(e, true);
EventHelper.stop(e, true);
CompositeActionItem.clearDraggedComposite();
const targetId = this.pinnedComposites[this.pinnedComposites.length - 1];
@@ -424,20 +448,21 @@ export class CompositeBar implements ICompositeBar {
});
// Persist
this.savePinnedComposites();
this.saveCompositesStates();
}
public isPinned(compositeId: string): boolean {
return this.pinnedComposites.indexOf(compositeId) >= 0;
}
public pin(compositeId: string, update = true): void {
public pin(compositeId: string, update = true, index = this.pinnedComposites.length, activate: boolean = true): void {
if (this.isPinned(compositeId)) {
return;
}
this.options.openComposite(compositeId).then(() => {
this.pinnedComposites.push(compositeId);
const activatePromise = activate ? this.options.openComposite(compositeId) : TPromise.as(null);
activatePromise.then(() => {
this.pinnedComposites.splice(index, 0, compositeId);
this.pinnedComposites = arrays.distinct(this.pinnedComposites);
if (update) {
@@ -445,7 +470,7 @@ export class CompositeBar implements ICompositeBar {
}
// Persist
this.savePinnedComposites();
this.saveCompositesStates();
});
}
@@ -477,7 +502,7 @@ export class CompositeBar implements ICompositeBar {
}, 0);
// Persist
this.savePinnedComposites();
this.saveCompositesStates();
}
public layout(dimension: Dimension): void {
@@ -501,8 +526,33 @@ export class CompositeBar implements ICompositeBar {
this.updateCompositeSwitcher();
}
private savePinnedComposites(): void {
this.storageService.store(this.options.storageId, JSON.stringify(this.pinnedComposites), StorageScope.GLOBAL);
private loadCompositesStates(): CompositeState[] {
const storedStates = <Array<string | CompositeState>>JSON.parse(this.storageService.get(this.options.storageId, StorageScope.GLOBAL, '[]'));
const isOldData = storedStates && storedStates.length && typeof storedStates[0] === 'string';
const compositeStates = <CompositeState[]>storedStates.map(c =>
typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true } : c);
if (!isOldData) { /* Add new composites only if it is new data */
const newComposites = this.options.composites.filter(c => compositeStates.every(s => s.id !== c.id));
newComposites.sort((c1, c2) => c1.order < c2.order ? -1 : 1);
newComposites.forEach(c => compositeStates.push({ id: c.id, pinned: true /* new composites are pinned by default */ }));
}
return compositeStates;
}
private saveCompositesStates(): void {
const toSave = this.pinnedComposites.map(id => (<CompositeState>{ id, pinned: true }));
for (const composite of this.options.composites) {
if (this.pinnedComposites.indexOf(composite.id) === -1) { // Unpinned composites
toSave.push({ id: composite.id, pinned: false });
}
}
this.storageService.store(this.options.storageId, JSON.stringify(toSave), StorageScope.GLOBAL);
}
public shutdown(): void {
this.saveCompositesStates();
}
public dispose(): void {

View File

@@ -5,7 +5,7 @@
'use strict';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import * as dom from 'vs/base/browser/dom';
@@ -21,7 +21,7 @@ import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { DelayedDragHandler } from 'vs/base/browser/dnd';
import { IActivity } from 'vs/workbench/common/activity';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
export interface ICompositeActivity {
badge: IBadge;
@@ -193,7 +193,7 @@ export class ActivityActionItem extends BaseActionItem {
this.$label.text(this.getAction().label);
}
this.$badge = this.builder.clone().div({ 'class': 'badge' }, (badge: Builder) => {
this.$badge = this.builder.clone().div({ 'class': 'badge' }, badge => {
this.$badgeContent = badge.div({ 'class': 'badge-content' });
});
@@ -207,6 +207,10 @@ export class ActivityActionItem extends BaseActionItem {
}
protected updateBadge(badge: IBadge, clazz?: string): void {
if (!this.$badge || !this.$badgeContent) {
return;
}
this.badgeDisposable.dispose();
this.badgeDisposable = empty;

View File

@@ -5,7 +5,6 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { Builder } from 'vs/base/browser/builder';
import { Panel } from 'vs/workbench/browser/panel';
import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { IEditor, Position } from 'vs/platform/editor/common/editor';
@@ -64,9 +63,9 @@ export abstract class BaseEditor extends Panel implements IEditor {
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> {
public create(parent: HTMLElement): void; // create is sync for editors
public create(parent: HTMLElement): TPromise<void>;
public create(parent: HTMLElement): TPromise<void> {
const res = super.create(parent);
// Create Editor
@@ -76,9 +75,16 @@ export abstract class BaseEditor extends Panel implements IEditor {
}
/**
* Called to create the editor in the parent builder.
* Called to create the editor in the parent HTMLElement.
*/
protected abstract createEditor(parent: Builder): void;
protected abstract createEditor(parent: HTMLElement): void;
/**
* Subclasses can set this to false if it does not make sense to center editor input.
*/
public supportsCenteredLayout(): boolean {
return true;
}
/**
* Overload this function to allow for passing in a position argument.
@@ -119,4 +125,4 @@ export abstract class BaseEditor extends Panel implements IEditor {
// Super Dispose
super.dispose();
}
}
}

View File

@@ -5,7 +5,7 @@
'use strict';
import nls = require('vs/nls');
import * as nls from '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';

View File

@@ -5,41 +5,51 @@
'use strict';
import nls = require('vs/nls');
import Event, { Emitter } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import { EditorModel, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { Builder, $ } from 'vs/base/browser/builder';
import { 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';
import { ResourceViewerContext, ResourceViewer } from 'vs/workbench/browser/parts/editor/resourceViewer';
import URI from 'vs/base/common/uri';
import { Dimension } from 'vs/base/browser/dom';
export interface IOpenCallbacks {
openInternal: (input: EditorInput, options: EditorOptions) => void;
openExternal: (uri: URI) => void;
}
/*
* 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 readonly _onMetadataChanged: Emitter<void>;
private callbacks: IOpenCallbacks;
private metadata: string;
private binaryContainer: Builder;
private scrollbar: DomScrollableElement;
private resourceViewerContext: ResourceViewerContext;
constructor(
id: string,
callbacks: IOpenCallbacks,
telemetryService: ITelemetryService,
themeService: IThemeService,
private windowsService: IWindowsService
themeService: IThemeService
) {
super(id, telemetryService, themeService);
this._onMetadataChanged = new Emitter<void>();
this.toUnbind.push(this._onMetadataChanged);
this.callbacks = callbacks;
}
public get onMetadataChanged(): Event<void> {
@@ -50,7 +60,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
return this.input ? this.input.getName() : nls.localize('binaryEditor', "Binary Viewer");
}
protected createEditor(parent: Builder): void {
protected createEditor(parent: HTMLElement): void {
// Container for Binary
const binaryContainerElement = document.createElement('div');
@@ -61,7 +71,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
// Custom Scrollbars
this.scrollbar = new DomScrollableElement(binaryContainerElement, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto });
parent.getHTMLElement().appendChild(this.scrollbar.getDomNode());
parent.appendChild(this.scrollbar.getDomNode());
}
public setInput(input: EditorInput, options?: EditorOptions): TPromise<void> {
@@ -74,10 +84,10 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
// Otherwise set input and resolve
return super.setInput(input, options).then(() => {
return input.resolve(true).then((resolvedModel: EditorModel) => {
return input.resolve(true).then(model => {
// Assert Model instance
if (!(resolvedModel instanceof BinaryEditorModel)) {
if (!(model instanceof BinaryEditorModel)) {
return TPromise.wrapError<void>(new Error('Unable to open file as binary'));
}
@@ -87,21 +97,14 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
}
// Render Input
const model = <BinaryEditorModel>resolvedModel;
this.resourceViewerContext = ResourceViewer.show(
{ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() },
this.binaryContainer,
this.binaryContainer.getHTMLElement(),
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));
resource => this.callbacks.openInternal(input, options),
resource => this.callbacks.openExternal(resource),
meta => this.handleMetadataChanged(meta)
);
return TPromise.as<void>(null);
});
@@ -117,6 +120,10 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
return this.metadata;
}
public supportsCenteredLayout(): boolean {
return false;
}
public clearInput(): void {
// Clear Meta

View File

@@ -5,7 +5,7 @@
'use strict';
import { Registry } from 'vs/platform/registry/common/platform';
import nls = require('vs/nls');
import * as nls from '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';
@@ -225,7 +225,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(ChangeEncodingAction,
export class QuickOpenActionContributor extends ActionBarContributor {
private openToSideActionInstance: OpenToSideAction;
constructor( @IInstantiationService private instantiationService: IInstantiationService) {
constructor(@IInstantiationService private instantiationService: IInstantiationService) {
super();
}

View File

@@ -5,7 +5,7 @@
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { mixin } from 'vs/base/common/objects';
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
@@ -543,6 +543,50 @@ export class CloseEditorAction extends Action {
}
}
export class CloseOneEditorAction extends Action {
public static readonly ID = 'workbench.action.closeActiveEditor';
public static readonly LABEL = nls.localize('closeOneEditor', "Close");
constructor(
id: string,
label: string,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService
) {
super(id, label, 'close-editor-action');
}
public run(context?: IEditorCommandsContext): TPromise<any> {
const model = this.editorGroupService.getStacksModel();
const group = context ? model.getGroup(context.groupId) : null;
const position = group ? model.positionOfGroup(group) : null;
// Close Active Editor
if (typeof position !== 'number') {
const activeEditor = this.editorService.getActiveEditor();
if (activeEditor) {
return this.editorService.closeEditor(activeEditor.position, activeEditor.input);
}
}
// Close Specific Editor
const editor = group && context && typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : null;
if (editor) {
return this.editorService.closeEditor(position, editor);
}
// Close First Editor at Position
const visibleEditors = this.editorService.getVisibleEditors();
if (visibleEditors[position]) {
return this.editorService.closeEditor(position, visibleEditors[position].input);
}
return TPromise.as(false);
}
}
export class RevertAndCloseEditorAction extends Action {
public static readonly ID = 'workbench.action.revertAndCloseActiveEditor';

View File

@@ -70,7 +70,7 @@ function registerActiveEditorMoveCommand(): void {
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: EditorCommands.MoveActiveEditor,
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: EditorContextKeys.textFocus,
when: EditorContextKeys.editorTextFocus,
primary: null,
handler: (accessor, args: any) => moveActiveEditor(args, accessor),
description: {
@@ -269,7 +269,7 @@ function registerEditorCommands() {
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: void 0,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const model = editorGroupService.getStacksModel();
const editorService = accessor.get(IWorkbenchEditorService);
@@ -299,7 +299,7 @@ function registerEditorCommands() {
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
when: void 0,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));
@@ -324,7 +324,7 @@ function registerEditorCommands() {
when: void 0,
primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
@@ -373,7 +373,7 @@ function registerEditorCommands() {
when: void 0,
primary: void 0,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
const editorGroupService = accessor.get(IEditorGroupService);
const editorService = accessor.get(IWorkbenchEditorService);
const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService));

View File

@@ -6,16 +6,16 @@
'use strict';
import 'vs/css!./media/editorGroupsControl';
import arrays = require('vs/base/common/arrays');
import Event, { Emitter } from 'vs/base/common/event';
import * as arrays from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import types = require('vs/base/common/types');
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import * as types from 'vs/base/common/types';
import { Builder, $ } from 'vs/base/browser/builder';
import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutProvider, Orientation } from 'vs/base/browser/ui/sash/sash';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import DOM = require('vs/base/browser/dom');
import errors = require('vs/base/common/errors');
import * as DOM from 'vs/base/browser/dom';
import * as errors from 'vs/base/common/errors';
import { RunOnceScheduler } from 'vs/base/common/async';
import { isMacintosh } from 'vs/base/common/platform';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
@@ -29,7 +29,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl';
import { ITitleAreaControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { NoTabsTitleControl } from 'vs/workbench/browser/parts/editor/noTabsTitleControl';
import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup, EditorOptions, TextEditorOptions, IEditorIdentifier, EditorInput, PREFERENCES_EDITOR_ID, TEXT_DIFF_EDITOR_ID } from 'vs/workbench/common/editor';
import { IEditorStacksModel, IStacksModelChangeEvent, IEditorGroup, EditorOptions, TextEditorOptions, IEditorIdentifier, EditorInput } from 'vs/workbench/common/editor';
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { editorBackground, contrastBorder, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
@@ -40,6 +40,7 @@ import { ResourcesDropHandler, LocalSelectionTransfer, DraggedEditorIdentifier }
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IPartService } from 'vs/workbench/services/part/common/partService';
export enum Rochade {
NONE,
TWO_TO_ONE,
@@ -74,7 +75,7 @@ export interface IEditorGroupsControl {
updateProgress(position: Position, state: ProgressState): void;
updateTitleAreas(refreshActive?: boolean): void;
layout(dimension: Dimension): void;
layout(dimension: DOM.Dimension): void;
layout(position: Position): void;
arrangeGroups(arrangement: GroupArrangement): void;
@@ -121,8 +122,8 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private stacks: IEditorStacksModel;
private parent: Builder;
private dimension: Dimension;
private parent: HTMLElement;
private dimension: DOM.Dimension;
private dragging: boolean;
private layoutVertically: boolean;
@@ -151,7 +152,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private centeredEditorActive: boolean;
private centeredEditorSashLeft: Sash;
private centeredEditorSashRight: Sash;
private centeredEditorPreferedSize: number;
private centeredEditorPreferredSize: number;
private centeredEditorLeftMarginRatio: number;
private centeredEditorDragStartPosition: number;
private centeredEditorDragStartSize: number;
@@ -163,7 +164,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private visibleEditorFocusTrackerDisposable: IDisposable[];
private _onGroupFocusChanged: Emitter<void>;
private readonly _onGroupFocusChanged: Emitter<void>;
private onStacksChangeScheduler: RunOnceScheduler;
private stacksChangedBuffer: IStacksModelChangeEvent[];
@@ -171,12 +172,12 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private transfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
constructor(
parent: Builder,
parent: HTMLElement,
groupOrientation: GroupOrientation,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IEditorGroupService private editorGroupService: IEditorGroupService,
@IPartService private partService: IPartService,
@IStorageService private storageServise: IStorageService,
@IStorageService private storageService: IStorageService,
@IContextKeyService private contextKeyService: IContextKeyService,
@IExtensionService private extensionService: IExtensionService,
@IInstantiationService private instantiationService: IInstantiationService,
@@ -188,7 +189,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.stacks = editorGroupService.getStacksModel();
this.parent = parent;
this.dimension = new Dimension(0, 0);
this.dimension = new DOM.Dimension(0, 0);
this.silos = [];
this.silosSize = [];
@@ -291,6 +292,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
titleControl.dispose();
titleContainer.empty();
this.createTitleControl(this.stacks.groupAt(position), this.silos[position], titleContainer, this.getInstantiationService(position));
this.layoutTitleControl(position);
}
// Refresh title when layout options change
@@ -369,8 +371,8 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.trackFocus(editor, position);
// Find target container and build into
const target = this.silos[position].child();
editor.getContainer().build(target);
const target = this.silos[position].child().getHTMLElement();
target.appendChild(editor.getContainer());
// Adjust layout according to provided ratios (used when restoring multiple editors at once)
if (ratio && (ratio.length === 2 || ratio.length === 3)) {
@@ -442,7 +444,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
// Show editor container
editor.getContainer().show();
DOM.show(editor.getContainer());
}
private getVisibleEditorCount(): number {
@@ -554,7 +556,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.clearPosition(position);
// Take editor container offdom and hide
editor.getContainer().offDOM().hide();
const editorContainer = editor.getContainer();
if (editorContainer.parentNode) {
editorContainer.parentNode.removeChild(editorContainer);
}
DOM.hide(editorContainer);
// Adjust layout and rochade if instructed to do so
if (layoutAndRochade) {
@@ -780,10 +786,10 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.layoutVertically = (orientation !== 'horizontal');
// Editor Layout
const verticalLayouting = this.parent.hasClass('vertical-layout');
const verticalLayouting = DOM.hasClass(this.parent, 'vertical-layout');
if (verticalLayouting !== this.layoutVertically) {
this.parent.removeClass('vertical-layout', 'horizontal-layout');
this.parent.addClass(this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
DOM.removeClasses(this.parent, 'vertical-layout', 'horizontal-layout');
DOM.addClass(this.parent, this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
this.sashOne.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL);
this.sashTwo.setOrientation(this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL);
@@ -968,16 +974,16 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
private create(): void {
// Store layout as class property
this.parent.addClass(this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
DOM.addClass(this.parent, this.layoutVertically ? 'vertical-layout' : 'horizontal-layout');
// Allow to drop into container to open
this.enableDropTarget(this.parent.getHTMLElement());
this.enableDropTarget(this.parent);
// Silo One
this.silos[Position.ONE] = $(this.parent).div({ class: 'one-editor-silo editor-one' });
// Sash One
this.sashOne = new Sash(this.parent.getHTMLElement(), this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.sashOne = new Sash(this.parent, this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.toUnbind.push(this.sashOne.onDidStart(() => this.onSashOneDragStart()));
this.toUnbind.push(this.sashOne.onDidChange((e: ISashEvent) => this.onSashOneDrag(e)));
this.toUnbind.push(this.sashOne.onDidEnd(() => this.onSashOneDragEnd()));
@@ -988,7 +994,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.silos[Position.TWO] = $(this.parent).div({ class: 'one-editor-silo editor-two' });
// Sash Two
this.sashTwo = new Sash(this.parent.getHTMLElement(), this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.sashTwo = new Sash(this.parent, this, { baseSize: 5, orientation: this.layoutVertically ? Orientation.VERTICAL : Orientation.HORIZONTAL });
this.toUnbind.push(this.sashTwo.onDidStart(() => this.onSashTwoDragStart()));
this.toUnbind.push(this.sashTwo.onDidChange((e: ISashEvent) => this.onSashTwoDrag(e)));
this.toUnbind.push(this.sashTwo.onDidEnd(() => this.onSashTwoDragEnd()));
@@ -1033,9 +1039,9 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.createTitleControl(this.stacks.groupAt(position), silo, titleContainer, instantiationService);
// Progress Bar
const progressBar = new ProgressBar($(container));
const progressBar = new ProgressBar(container.getHTMLElement());
this.toUnbind.push(attachProgressBarStyler(progressBar, this.themeService));
progressBar.getContainer().hide();
progressBar.hide();
container.setProperty(EditorGroupsControl.PROGRESS_BAR_CONTROL_KEY, progressBar); // associate with container
// Sash for first position to support centered editor layout
@@ -1059,11 +1065,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
this.centeredEditorLeftMarginRatio = 0.5;
// Restore centered layout position and size
const centeredLayoutDataString = this.storageServise.get(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
const centeredLayoutDataString = this.storageService.get(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
if (centeredLayoutDataString) {
const centeredLayout = <CenteredEditorLayoutData>JSON.parse(centeredLayoutDataString);
this.centeredEditorLeftMarginRatio = centeredLayout.leftMarginRatio;
this.centeredEditorPreferedSize = centeredLayout.size;
this.centeredEditorPreferredSize = centeredLayout.size;
}
}
}
@@ -1284,7 +1290,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
if (!overlay) {
const containers = $this.visibleEditors.filter(e => !!e).map(e => e.getContainer());
containers.forEach((container, index) => {
if (container && DOM.isAncestor(target, container.getHTMLElement())) {
if (container && DOM.isAncestor(target, container)) {
const activeContrastBorderColor = $this.getColor(activeContrastBorder);
overlay = $('div').style({
top: $this.tabOptions.showTabs ? `${EditorGroupsControl.EDITOR_TITLE_HEIGHT}px` : 0,
@@ -1596,8 +1602,8 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
// TODO@Ben remove me after a while
/* __GDPR__
"editorGroupMoved" : {
"source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"to": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"source" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"to": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('editorGroupMoved', { source: position, to: moveTo });
@@ -1627,11 +1633,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
let borderColor = null;
if (isDragging) {
this.parent.addClass('dragging');
DOM.addClass(this.parent, 'dragging');
silo.addClass('dragging');
borderColor = this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder);
} else {
this.parent.removeClass('dragging');
DOM.removeClass(this.parent, 'dragging');
silo.removeClass('dragging');
}
@@ -1930,11 +1936,11 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
private get centeredEditorAvailableSize(): number {
return this.silosSize[Position.ONE] - EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN * 2;
return this.dimension.width - EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN * 2;
}
private get centeredEditorSize(): number {
return Math.min(this.centeredEditorAvailableSize, this.centeredEditorPreferedSize);
return Math.min(this.centeredEditorAvailableSize, this.centeredEditorPreferredSize);
}
private get centeredEditorPosition(): number {
@@ -1956,7 +1962,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
if (size > 3 * this.minSize && size < this.centeredEditorAvailableSize) {
this.centeredEditorPreferedSize = size;
this.centeredEditorPreferredSize = size;
position -= EditorGroupsControl.CENTERED_EDITOR_MIN_MARGIN;
position = Math.min(position, this.centeredEditorAvailableSize - this.centeredEditorSize);
position = Math.max(0, position);
@@ -1971,7 +1977,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
leftMarginRatio: this.centeredEditorLeftMarginRatio,
size: this.centeredEditorSize
};
this.storageServise.store(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, JSON.stringify(data), StorageScope.GLOBAL);
this.storageService.store(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, JSON.stringify(data), StorageScope.GLOBAL);
}
public getVerticalSashTop(sash: Sash): number {
@@ -2013,17 +2019,17 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
return this.dragging;
}
public layout(dimension: Dimension): void;
public layout(dimension: DOM.Dimension): void;
public layout(position: Position): void;
public layout(arg: any): void {
if (arg instanceof Dimension) {
this.layoutControl(<Dimension>arg);
if (arg instanceof DOM.Dimension) {
this.layoutControl(<DOM.Dimension>arg);
} else {
this.layoutEditor(<Position>arg);
}
}
private layoutControl(dimension: Dimension): void {
private layoutControl(dimension: DOM.Dimension): void {
let oldDimension = this.dimension;
this.dimension = dimension;
@@ -2141,14 +2147,14 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
});
// Layout centered Editor (only in vertical layout when one group is opened)
const id = this.visibleEditors[Position.ONE] ? this.visibleEditors[Position.ONE].getId() : undefined;
const doCentering = this.layoutVertically && this.stacks.groups.length === 1 && this.partService.isEditorLayoutCentered() && id !== PREFERENCES_EDITOR_ID && id !== TEXT_DIFF_EDITOR_ID;
const doCentering = this.partService.isEditorLayoutCentered() && this.stacks.groups.length === 1 &&
this.visibleEditors[Position.ONE] && this.visibleEditors[Position.ONE].supportsCenteredLayout();
if (doCentering && !this.centeredEditorActive) {
this.centeredEditorSashLeft.show();
this.centeredEditorSashRight.show();
// no size set yet. Calculate a default value
if (!this.centeredEditorPreferedSize) {
if (!this.centeredEditorPreferredSize) {
this.resetCenteredEditor(false);
}
} else if (!doCentering && this.centeredEditorActive) {
@@ -2169,16 +2175,18 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
});
// Layout title controls
POSITIONS.forEach(position => {
const siloWidth = this.layoutVertically ? this.silosSize[position] : this.dimension.width;
this.getTitleAreaControl(position).layout(new Dimension(siloWidth, EditorGroupsControl.EDITOR_TITLE_HEIGHT));
});
POSITIONS.forEach(position => this.layoutTitleControl(position));
// Update minimized state
this.updateMinimizedState();
}
private layoutTitleControl(position: Position): void {
const siloWidth = this.layoutVertically ? this.silosSize[position] : this.dimension.width;
this.getTitleAreaControl(position).layout(new DOM.Dimension(siloWidth, EditorGroupsControl.EDITOR_TITLE_HEIGHT));
}
private layoutEditor(position: Position): void {
const editorSize = this.silosSize[position];
const editor = this.visibleEditors[position];
@@ -2201,20 +2209,20 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
const editorContainer = editor.getContainer();
editorContainer.style('margin-left', this.centeredEditorActive ? `${editorPosition}px` : null);
editorContainer.style('width', this.centeredEditorActive ? `${editorWidth}px` : null);
editorContainer.style('border-color', this.centeredEditorActive ? this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder) : null);
editor.layout(new Dimension(editorWidth, editorHeight));
editorContainer.style.marginLeft = this.centeredEditorActive ? `${editorPosition}px` : null;
editorContainer.style.width = this.centeredEditorActive ? `${editorWidth}px` : null;
editorContainer.style.borderColor = this.centeredEditorActive ? this.getColor(EDITOR_GROUP_BORDER) || this.getColor(contrastBorder) : null;
editor.layout(new DOM.Dimension(editorWidth, editorHeight));
}
}
private resetCenteredEditor(layout: boolean = true) {
this.centeredEditorLeftMarginRatio = 0.5;
this.centeredEditorPreferedSize = Math.floor(this.dimension.width * EditorGroupsControl.GOLDEN_RATIO);
this.centeredEditorPreferredSize = Math.floor(this.dimension.width * EditorGroupsControl.GOLDEN_RATIO);
if (layout) {
this.layoutContainers();
}
this.storageServise.remove(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
this.storageService.remove(EditorGroupsControl.CENTERED_EDITOR_LAYOUT_DATA_STORAGE_KEY, StorageScope.GLOBAL);
}
public getInstantiationService(position: Position): IInstantiationService {
@@ -2269,13 +2277,13 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
switch (state) {
case ProgressState.INFINITE:
progressbar.infinite().getContainer().show();
progressbar.infinite().show();
break;
case ProgressState.DONE:
progressbar.done().getContainer().hide();
progressbar.done().hide();
break;
case ProgressState.STOP:
progressbar.stop().getContainer().hide();
progressbar.stop().hide();
break;
}
}

View File

@@ -9,12 +9,11 @@ import 'vs/css!./media/editorpart';
import 'vs/workbench/browser/parts/editor/editor.contribution';
import { TPromise } from 'vs/base/common/winjs.base';
import { Registry } from 'vs/platform/registry/common/platform';
import { Dimension, Builder, $ } from 'vs/base/browser/builder';
import nls = require('vs/nls');
import strings = require('vs/base/common/strings');
import arrays = require('vs/base/common/arrays');
import types = require('vs/base/common/types');
import errors = require('vs/base/common/errors');
import * as nls from 'vs/nls';
import * as strings from 'vs/base/common/strings';
import * as arrays from 'vs/base/common/arrays';
import * as types from 'vs/base/common/types';
import * as errors from 'vs/base/common/errors';
import * as objects from 'vs/base/common/objects';
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
import { toErrorMessage } from 'vs/base/common/errorMessage';
@@ -35,20 +34,19 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { EditorStacksModel, EditorGroup, EditorIdentifier, EditorCloseEvent } from 'vs/workbench/common/editor/editorStacksModel';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter, once } from 'vs/base/common/event';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { EDITOR_GROUP_BACKGROUND } from 'vs/workbench/common/theme';
import { createCSSRule } from 'vs/base/browser/dom';
import { createCSSRule, Dimension, addClass, removeClass } from 'vs/base/browser/dom';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { join } from 'vs/base/common/paths';
import { IEditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { ThrottledEmitter } from 'vs/base/common/async';
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { INotificationService, Severity, INotificationActions } from 'vs/platform/notification/common/notification';
import { isErrorWithActions } from 'vs/base/common/errors';
import { dispose } from 'vs/base/common/lifecycle';
// {{SQL CARBON EDIT}}
import { convertEditorInput } from 'sql/parts/common/customInputConverter';
@@ -107,16 +105,17 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
private forceHideTabs: boolean;
private doNotFireTabOptionsChanged: boolean;
private revealIfOpen: boolean;
private _onEditorsChanged: ThrottledEmitter<void>;
private _onEditorOpening: Emitter<IEditorOpeningEvent>;
private _onEditorGroupMoved: Emitter<void>;
private _onEditorOpenFail: Emitter<EditorInput>;
private _onGroupOrientationChanged: Emitter<void>;
private _onTabOptionsChanged: Emitter<IEditorTabOptions>;
private ignoreOpenEditorErrors: boolean;
private textCompareEditorVisible: IContextKey<boolean>;
private readonly _onEditorsChanged: ThrottledEmitter<void>;
private readonly _onEditorOpening: Emitter<IEditorOpeningEvent>;
private readonly _onEditorGroupMoved: Emitter<void>;
private readonly _onEditorOpenFail: Emitter<EditorInput>;
private readonly _onGroupOrientationChanged: Emitter<void>;
private readonly _onTabOptionsChanged: Emitter<IEditorTabOptions>;
private readonly _onLayout: Emitter<Dimension>;
// The following data structures are partitioned into array of Position as provided by Services.POSITION array
private visibleEditors: BaseEditor[];
private instantiatedEditors: BaseEditor[][];
@@ -124,9 +123,6 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
private pendingEditorInputsToClose: EditorIdentifier[];
private pendingEditorInputCloseTimeout: number;
private onLayoutEmitter = new Emitter<Dimension>();
public onLayout = this.onLayoutEmitter.event;
constructor(
id: string,
restoreFromStorage: boolean,
@@ -148,6 +144,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this._onEditorOpenFail = new Emitter<EditorInput>();
this._onGroupOrientationChanged = new Emitter<void>();
this._onTabOptionsChanged = new Emitter<IEditorTabOptions>();
this._onLayout = new Emitter<Dimension>();
this.visibleEditors = [];
@@ -298,6 +295,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this.editorGroupsControl.resizeGroup(position, groupSizeChange);
}
public get onLayout(): Event<Dimension> {
return this._onLayout.event;
}
public get onEditorsChanged(): Event<void> {
return this._onEditorsChanged.event;
}
@@ -479,11 +480,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Create editor as needed
if (!editor.getContainer()) {
editor.create($().div({
'class': 'editor-container',
'role': 'tabpanel',
id: descriptor.getId()
}));
const editorContainer = document.createElement('div');
editorContainer.id = descriptor.getId();
addClass(editorContainer, 'editor-container');
editorContainer.setAttribute('role', 'tabpanel');
editor.create(editorContainer);
}
return editor;
@@ -555,18 +557,21 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Stop loading promise if any
monitor.cancel();
// Report error only if this was not us restoring previous error state
if (this.partService.isCreated() && !errors.isPromiseCanceledError(error)) {
// Report error only if this was not us restoring previous error state or
// we are told to ignore errors that occur from opening an editor
if (this.partService.isCreated() && !errors.isPromiseCanceledError(error) && !this.ignoreOpenEditorErrors) {
const actions: INotificationActions = { primary: [] };
if (isErrorWithActions(error)) {
actions.primary = error.actions;
if (errors.isErrorWithActions(error)) {
actions.primary = (error as errors.IErrorWithActions).actions;
}
this.notificationService.notify({
const handle = this.notificationService.notify({
severity: Severity.Error,
message: nls.localize('editorOpenError', "Unable to open '{0}': {1}.", input.getName(), toErrorMessage(error)),
actions
});
once(handle.onDidClose)(() => dispose(actions.primary));
}
this.editorGroupsControl.updateProgress(position, ProgressState.DONE);
@@ -576,7 +581,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Recover by closing the active editor (if the input is still the active one)
if (group.activeEditor === input) {
this.doCloseActiveEditor(group, !(options && options.preserveFocus) /* still preserve focus as needed */);
this.doCloseActiveEditor(group, !(options && options.preserveFocus) /* still preserve focus as needed */, true /* from error */);
}
}
@@ -610,7 +615,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
}
}
private doCloseActiveEditor(group: EditorGroup, focusNext = true): void {
private doCloseActiveEditor(group: EditorGroup, focusNext = true, fromError?: boolean): void {
const position = this.stacks.positionOfGroup(group);
// Update stacks model
@@ -623,7 +628,22 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Otherwise open next active
else {
this.openEditor(group.activeEditor, !focusNext ? EditorOptions.create({ preserveFocus: true }) : null, position).done(null, errors.onUnexpectedError);
// When closing an editor due to an error we can end up in a loop where we continue closing
// editors that fail to open (e.g. when the file no longer exists). We do not want to show
// repeated errors in this case to the user. As such, if we open the next editor and we are
// in a scope of a previous editor failing, we silence the input errors until the editor is
// opened.
if (fromError) {
this.ignoreOpenEditorErrors = true;
}
this.openEditor(group.activeEditor, !focusNext ? EditorOptions.create({ preserveFocus: true }) : null, position).done(() => {
this.ignoreOpenEditorErrors = false;
}, error => {
errors.onUnexpectedError(error);
this.ignoreOpenEditorErrors = false;
});
}
}
@@ -710,7 +730,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
}
// Then check for array of positions to close
if (Array.isArray(positionsOrEditors) || isUndefinedOrNull(positionsOrEditors)) {
if (Array.isArray(positionsOrEditors) || types.isUndefinedOrNull(positionsOrEditors)) {
return this.doCloseAllEditorsAtPositions(positionsOrEditors as Position[]);
}
@@ -833,16 +853,18 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Close: By Filter or all
else {
editorsToClose = group.getEditors(true /* in MRU order */);
filter = filterOrEditors || Object.create(null);
const hasDirection = !types.isUndefinedOrNull(filter.direction);
editorsToClose = group.getEditors(!hasDirection /* in MRU order only if direction is not specified */);
// Filter: saved only
if (filter.savedOnly) {
editorsToClose = editorsToClose.filter(e => !e.isDirty());
}
// Filter: direction (left / right)
else if (!types.isUndefinedOrNull(filter.direction)) {
else if (hasDirection) {
editorsToClose = (filter.direction === Direction.LEFT) ? editorsToClose.slice(0, group.indexOf(filter.except)) : editorsToClose.slice(group.indexOf(filter.except) + 1);
}
@@ -1141,12 +1163,12 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
return this.editorGroupsControl.getGroupOrientation();
}
public createContentArea(parent: Builder): Builder {
public createContentArea(parent: HTMLElement): HTMLElement {
// Content Container
const contentArea = $(parent)
.div()
.addClass('content');
const contentArea = document.createElement('div');
addClass(contentArea, 'content');
parent.appendChild(contentArea);
// get settings
this.memento = this.getMemento(this.storageService, MementoScope.WORKSPACE);
@@ -1164,19 +1186,19 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Part container
const container = this.getContainer();
container.style('background-color', this.getColor(editorBackground));
container.style.backgroundColor = this.getColor(editorBackground);
// Content area
const content = this.getContentArea();
const groupCount = this.stacks.groups.length;
if (groupCount > 1) {
content.addClass('multiple-groups');
addClass(content, 'multiple-groups');
} else {
content.removeClass('multiple-groups');
removeClass(content, 'multiple-groups');
}
content.style('background-color', groupCount > 0 ? this.getColor(EDITOR_GROUP_BACKGROUND) : null);
content.style.backgroundColor = groupCount > 0 ? this.getColor(EDITOR_GROUP_BACKGROUND) : null;
}
private onGroupFocusChanged(): void {
@@ -1474,7 +1496,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this.dimension = sizes[1];
this.editorGroupsControl.layout(this.dimension);
this.onLayoutEmitter.fire(dimension);
this._onLayout.fire(dimension);
return sizes;
}
@@ -1512,6 +1534,9 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
this._onEditorOpening.dispose();
this._onEditorGroupMoved.dispose();
this._onEditorOpenFail.dispose();
this._onGroupOrientationChanged.dispose();
this._onTabOptionsChanged.dispose();
this._onLayout.dispose();
// Reset Tokens
this.editorOpenToken = [];

View File

@@ -6,9 +6,9 @@
import 'vs/css!./media/editorpicker';
import { TPromise } from 'vs/base/common/winjs.base';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import * as errors from 'vs/base/common/errors';
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, QuickOpenItemAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel';

View File

@@ -6,14 +6,14 @@
'use strict';
import 'vs/css!./media/editorstatus';
import nls = require('vs/nls');
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { $, append, runAtThisOrScheduleAtNextAnimationFrame, addDisposableListener } from 'vs/base/browser/dom';
import strings = require('vs/base/common/strings');
import paths = require('vs/base/common/paths');
import types = require('vs/base/common/types');
import { $, append, runAtThisOrScheduleAtNextAnimationFrame, addDisposableListener, getDomNodePagePosition } from 'vs/base/browser/dom';
import * as strings from 'vs/base/common/strings';
import * as paths from 'vs/base/common/paths';
import * as types from 'vs/base/common/types';
import uri from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import * as errors from 'vs/base/common/errors';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { Action } from 'vs/base/common/actions';
import { language, LANGUAGE_DEFAULT, AccessibilitySupport } from 'vs/base/common/platform';
@@ -50,35 +50,58 @@ import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/con
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { attachStylerCallback, attachButtonStyler } from 'vs/platform/theme/common/styler';
import { widgetShadow, editorWidgetBackground } from 'vs/platform/theme/common/colorRegistry';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { widgetShadow, editorWidgetBackground, foreground, darken, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { deepClone } from 'vs/base/common/objects';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Button } from 'vs/base/browser/ui/button/button';
import { Schemas } from 'vs/base/common/network';
import { IAnchor } from 'vs/base/browser/ui/contextview/contextview';
import { Themable } from 'vs/workbench/common/theme';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
// TODO@Sandeep layer breaker
// tslint:disable-next-line:import-patterns
import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences';
class SideBySideEditorEncodingSupport implements IEncodingSupport {
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
public getEncoding(): string {
return this.master.getEncoding(); // always report from modified (right hand) side
}
public setEncoding(encoding: string, mode: EncodingMode): void {
[this.master, this.details].forEach(s => s.setEncoding(encoding, mode));
}
}
// {{SQL CARBON EDIT}}
import { QueryEditorService } from 'sql/parts/query/services/queryEditorService';
function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport {
if (input instanceof SideBySideEditorInput) {
input = input.master;
}
// Untitled Editor
if (input instanceof UntitledEditorInput) {
return input;
}
// Side by Side (diff) Editor
if (input instanceof SideBySideEditorInput) {
const masterEncodingSupport = toEditorWithEncodingSupport(input.master);
const detailsEncodingSupport = toEditorWithEncodingSupport(input.details);
if (masterEncodingSupport && detailsEncodingSupport) {
return new SideBySideEditorEncodingSupport(masterEncodingSupport, detailsEncodingSupport);
}
return masterEncodingSupport;
}
// File or Resource Editor
let encodingSupport = input as IFileEditorInput;
if (types.areFunctions(encodingSupport.setEncoding, encodingSupport.getEncoding)) {
return encodingSupport;
}
// Unsupported for any other editor
return null;
}
@@ -246,16 +269,16 @@ const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus");
const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Optimized");
const nlsScreenReaderDetectedTitle = nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\".");
function _setDisplay(el: HTMLElement, desiredValue: string): void {
function setDisplay(el: HTMLElement, desiredValue: string): void {
if (el.style.display !== desiredValue) {
el.style.display = desiredValue;
}
}
function show(el: HTMLElement): void {
_setDisplay(el, '');
setDisplay(el, '');
}
function hide(el: HTMLElement): void {
_setDisplay(el, 'none');
setDisplay(el, 'none');
}
export class EditorStatus implements IStatusbarItem {
@@ -274,7 +297,7 @@ export class EditorStatus implements IStatusbarItem {
private activeEditorListeners: IDisposable[];
private delayedRender: IDisposable;
private toRender: StateChange;
private lastScreenReaderExplanation: ScreenReaderDetectedExplanation;
private screenReaderExplanation: ScreenReaderDetectedExplanation;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@@ -289,7 +312,6 @@ export class EditorStatus implements IStatusbarItem {
this.toDispose = [];
this.activeEditorListeners = [];
this.state = new State();
this.lastScreenReaderExplanation = null;
}
public render(container: HTMLElement): IDisposable {
@@ -313,7 +335,7 @@ export class EditorStatus implements IStatusbarItem {
hide(this.selectionElement);
this.indentationElement = append(this.element, $('a.editor-status-indentation'));
this.indentationElement.title = nls.localize('indentation', "Indentation");
this.indentationElement.title = nls.localize('selectIndentation', "Select Indentation");
this.indentationElement.onclick = () => this.onIndentationClick();
hide(this.indentationElement);
@@ -487,7 +509,18 @@ export class EditorStatus implements IStatusbarItem {
}
private onScreenReaderModeClick(): void {
this.lastScreenReaderExplanation = this.instantiationService.createInstance(ScreenReaderDetectedExplanation, this.screenRedearModeElement);
const showExplanation = !this.screenReaderExplanation || !this.screenReaderExplanation.visible;
if (!this.screenReaderExplanation) {
this.screenReaderExplanation = this.instantiationService.createInstance(ScreenReaderDetectedExplanation);
this.toDispose.push(this.screenReaderExplanation);
}
if (showExplanation) {
this.screenReaderExplanation.show(this.screenRedearModeElement);
} else {
this.screenReaderExplanation.hide();
}
}
private onSelectionClick(): void {
@@ -652,9 +685,8 @@ export class EditorStatus implements IStatusbarItem {
screenReaderMode = (editorWidget.getConfiguration().accessibilitySupport === AccessibilitySupport.Enabled);
}
if (screenReaderMode === false && this.lastScreenReaderExplanation) {
this.lastScreenReaderExplanation.hide();
this.lastScreenReaderExplanation = null;
if (screenReaderMode === false && this.screenReaderExplanation && this.screenReaderExplanation.visible) {
this.screenReaderExplanation.hide();
}
this.updateState({ screenReaderMode: screenReaderMode });
@@ -1229,113 +1261,148 @@ export class ChangeEncodingAction extends Action {
}
}
class ScreenReaderDetectedExplanation {
private _isDisposed: boolean;
private _toDispose: IDisposable[];
class ScreenReaderDetectedExplanation extends Themable {
private container: HTMLElement;
private hrElement: HTMLHRElement;
private _visible: boolean;
constructor(
anchorElement: HTMLElement,
@IThemeService private readonly themeService: IThemeService,
@IThemeService themeService: IThemeService,
@IContextViewService private readonly contextViewService: IContextViewService,
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
) {
this._isDisposed = false;
this._toDispose = [];
super(themeService);
}
public get visible(): boolean {
return this._visible;
}
protected updateStyles(): void {
if (this.container) {
const background = this.getColor(editorWidgetBackground);
this.container.style.backgroundColor = background ? background.toString() : null;
const widgetShadowColor = this.getColor(widgetShadow);
this.container.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : null;
const contrastBorderColor = this.getColor(contrastBorder);
this.container.style.border = contrastBorderColor ? `1px solid ${contrastBorderColor}` : null;
const foregroundColor = this.getColor(foreground);
this.hrElement.style.backgroundColor = foregroundColor ? foregroundColor.toString() : null;
}
}
public show(anchorElement: HTMLElement): void {
this._visible = true;
this.contextViewService.showContextView({
getAnchor: () => anchorElement,
getAnchor: () => {
const res = getDomNodePagePosition(anchorElement);
return {
x: res.left,
y: res.top - 9, /* above the status bar */
width: res.width,
height: res.height
} as IAnchor;
},
render: (container) => {
return this.renderContents(container);
},
onDOMEvent: (e, activeElement) => {
},
onDOMEvent: (e, activeElement) => { },
onHide: () => {
this.dispose();
this._visible = false;
}
});
}
public dispose(): void {
this._isDisposed = true;
this._toDispose = dispose(this._toDispose);
}
public hide(): void {
if (this._isDisposed) {
return;
}
this.contextViewService.hideContextView();
}
protected renderContents(container: HTMLElement): IDisposable {
const domNode = $('div.screen-reader-detected-explanation', {
protected renderContents(parent: HTMLElement): IDisposable {
const toDispose: IDisposable[] = [];
this.container = $('div.screen-reader-detected-explanation', {
'aria-hidden': 'true'
});
const title = $('h2.title', {}, nls.localize('screenReaderDetectedExplanation.title', "Screen Reader Optimized"));
domNode.appendChild(title);
this.container.appendChild(title);
const closeBtn = $('div.cancel');
this._toDispose.push(addDisposableListener(closeBtn, 'click', () => {
toDispose.push(addDisposableListener(closeBtn, 'click', () => {
this.contextViewService.hideContextView();
}));
domNode.appendChild(closeBtn);
toDispose.push(addDisposableListener(closeBtn, 'mouseover', () => {
const theme = this.themeService.getTheme();
let darkenFactor: number;
switch (theme.type) {
case 'light':
darkenFactor = 0.1;
break;
case 'dark':
darkenFactor = 0.2;
break;
}
if (darkenFactor) {
closeBtn.style.backgroundColor = this.getColor(editorWidgetBackground, (color, theme) => darken(color, darkenFactor)(theme));
}
}));
toDispose.push(addDisposableListener(closeBtn, 'mouseout', () => {
closeBtn.style.backgroundColor = null;
}));
this.container.appendChild(closeBtn);
// {{SQL CARBON EDIT}}
const question = $('p.question', {}, nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate SQL Operations Studio?"));
domNode.appendChild(question);
this.container.appendChild(question);
const buttonContainer = $('div.buttons');
domNode.appendChild(buttonContainer);
this.container.appendChild(buttonContainer);
const yesBtn = new Button(buttonContainer);
yesBtn.label = nls.localize('screenReaderDetectedExplanation.answerYes', "Yes");
this._toDispose.push(attachButtonStyler(yesBtn, this.themeService));
this._toDispose.push(yesBtn.onDidClick(e => {
toDispose.push(attachButtonStyler(yesBtn, this.themeService));
toDispose.push(yesBtn.onDidClick(e => {
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
this.contextViewService.hideContextView();
}));
const noBtn = new Button(buttonContainer);
noBtn.label = nls.localize('screenReaderDetectedExplanation.answerNo', "No");
this._toDispose.push(attachButtonStyler(noBtn, this.themeService));
this._toDispose.push(noBtn.onDidClick(e => {
toDispose.push(attachButtonStyler(noBtn, this.themeService));
toDispose.push(noBtn.onDidClick(e => {
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
this.contextViewService.hideContextView();
}));
const clear = $('div');
clear.style.clear = 'both';
domNode.appendChild(clear);
this.container.appendChild(clear);
const br = $('br');
domNode.appendChild(br);
this.container.appendChild(br);
const hr = $('hr');
domNode.appendChild(hr);
this.hrElement = $('hr');
this.container.appendChild(this.hrElement);
// {{SQL CARBON EDIT}}
const explanation1 = $('p.body1', {}, nls.localize('screenReaderDetectedExplanation.body1', "SQL Operations Studio is now optimized for usage with a screen reader."));
domNode.appendChild(explanation1);
this.container.appendChild(explanation1);
const explanation2 = $('p.body2', {}, nls.localize('screenReaderDetectedExplanation.body2', "Some editor features will have different behaviour: e.g. word wrapping, folding, etc."));
domNode.appendChild(explanation2);
this.container.appendChild(explanation2);
container.appendChild(domNode);
parent.appendChild(this.container);
this._toDispose.push(attachStylerCallback(this.themeService, { widgetShadow, editorWidgetBackground }, colors => {
domNode.style.backgroundColor = colors.editorWidgetBackground;
if (colors.widgetShadow) {
domNode.style.boxShadow = `0 5px 8px ${colors.widgetShadow}`;
}
}));
this.updateStyles();
return {
dispose: () => { this.dispose(); }
dispose: () => dispose(toDispose)
};
}
}

View File

@@ -3,6 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .quick-open-widget .quick-open-tree .quick-open-entry.editor-preview {
.monaco-workbench .monaco-quick-open-widget .quick-open-tree .quick-open-entry.editor-preview {
font-style: italic;
}

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