Vscode merge (#4582)

* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd

* fix issues with merges

* bump node version in azpipe

* replace license headers

* remove duplicate launch task

* fix build errors

* fix build errors

* fix tslint issues

* working through package and linux build issues

* more work

* wip

* fix packaged builds

* working through linux build errors

* wip

* wip

* wip

* fix mac and linux file limits

* iterate linux pipeline

* disable editor typing

* revert series to parallel

* remove optimize vscode from linux

* fix linting issues

* revert testing change

* add work round for new node

* readd packaging for extensions

* fix issue with angular not resolving decorator dependencies
This commit is contained in:
Anthony Dresser
2019-03-19 17:44:35 -07:00
committed by GitHub
parent 833d197412
commit 87765e8673
1879 changed files with 54505 additions and 38058 deletions

View File

@@ -8,7 +8,7 @@ import { forEach } from 'vs/base/common/collections';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import * as resources from 'vs/base/common/resources';
import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ViewContainer, ViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor } from 'vs/workbench/common/views';
import { ViewContainer, IViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor } from 'vs/workbench/common/views';
import { CustomTreeViewPanel, CustomTreeView } from 'vs/workbench/browser/parts/views/customView';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { coalesce, } from 'vs/base/common/arrays';
@@ -16,16 +16,16 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWo
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { VIEWLET_ID as EXPLORER } from 'vs/workbench/parts/files/common/files';
import { VIEWLET_ID as SCM } from 'vs/workbench/parts/scm/common/scm';
import { VIEWLET_ID as DEBUG } from 'vs/workbench/parts/debug/common/debug';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { VIEWLET_ID as EXPLORER } from 'vs/workbench/contrib/files/common/files';
import { VIEWLET_ID as SCM } from 'vs/workbench/contrib/scm/common/scm';
import { VIEWLET_ID as DEBUG } from 'vs/workbench/contrib/debug/common/debug';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet';
import { IExtensionDescription, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IStorageService } from 'vs/platform/storage/common/storage';
@@ -33,7 +33,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { createCSSRule } from 'vs/base/browser/dom';
@@ -145,27 +145,27 @@ type ViewContainerExtensionPointType = { [loc: string]: IUserFriendlyViewsContai
// {{SQL CARBON EDIT}} - Export viewsContainersExtensionPoint
export const viewsContainersExtensionPoint: IExtensionPoint<ViewContainerExtensionPointType> = ExtensionsRegistry.registerExtensionPoint<ViewContainerExtensionPointType>({
extensionPoint: 'viewsContainers',
jsonSchema: viewsContainersContribution,
isDynamic: true
jsonSchema: viewsContainersContribution
});
type ViewExtensionPointType = { [loc: string]: IUserFriendlyViewDescriptor[] };
const viewsExtensionPoint: IExtensionPoint<ViewExtensionPointType> = ExtensionsRegistry.registerExtensionPoint<ViewExtensionPointType>({
extensionPoint: 'views',
deps: [viewsContainersExtensionPoint],
jsonSchema: viewsContribution,
isDynamic: true
jsonSchema: viewsContribution
});
const TEST_VIEW_CONTAINER_ORDER = 6;
class ViewsExtensionHandler implements IWorkbenchContribution {
private viewContainersRegistry: IViewContainersRegistry;
private viewsRegistry: IViewsRegistry;
constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
this.viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
this.viewsRegistry = Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry);
this.handleAndRegisterCustomViewContainers();
this.handleAndRegisterCustomViews();
}
@@ -205,9 +205,9 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
for (const viewContainer of viewContainersRegistry.all) {
if (viewContainer.extensionId && removedExtensions.has(ExtensionIdentifier.toKey(viewContainer.extensionId))) {
// move only those views that do not belong to the removed extension
const views = ViewsRegistry.getViews(viewContainer).filter((view: ICustomViewDescriptor) => !removedExtensions.has(ExtensionIdentifier.toKey(view.extensionId)));
const views = this.viewsRegistry.getViews(viewContainer).filter((view: ICustomViewDescriptor) => !removedExtensions.has(ExtensionIdentifier.toKey(view.extensionId)));
if (views.length) {
ViewsRegistry.moveViews(views, this.getDefaultViewContainer());
this.viewsRegistry.moveViews(views, this.getDefaultViewContainer());
}
this.deregisterCustomViewContainer(viewContainer);
}
@@ -262,11 +262,11 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
const viewsToMove: IViewDescriptor[] = [];
for (const existingViewContainer of existingViewContainers) {
if (viewContainer !== existingViewContainer) {
viewsToMove.push(...ViewsRegistry.getViews(existingViewContainer).filter((view: ICustomViewDescriptor) => view.originalContainerId === descriptor.id));
viewsToMove.push(...this.viewsRegistry.getViews(existingViewContainer).filter((view: ICustomViewDescriptor) => view.originalContainerId === descriptor.id));
}
}
if (viewsToMove.length) {
ViewsRegistry.moveViews(viewsToMove, viewContainer);
this.viewsRegistry.moveViews(viewsToMove, viewContainer);
}
}
});
@@ -278,13 +278,13 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
if (!viewContainer) {
viewContainer = this.viewContainersRegistry.registerViewContainer(id, extensionId);
viewContainer = this.viewContainersRegistry.registerViewContainer(id, true, extensionId);
// Register as viewlet
class CustomViewlet extends ViewContainerViewlet {
constructor(
@IConfigurationService configurationService: IConfigurationService,
@IPartService partService: IPartService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@ITelemetryService telemetryService: ITelemetryService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
@@ -294,7 +294,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService
) {
super(id, `${id}.state`, true, configurationService, partService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
super(id, `${id}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
}
}
const viewletDescriptor = new ViewletDescriptor(
@@ -314,9 +314,9 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
id: string, label: string,
@IViewletService viewletService: IViewletService,
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IPartService partService: IPartService
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
) {
super(id, label, id, viewletService, editorGroupService, partService);
super(id, label, id, viewletService, editorGroupService, layoutService);
}
}
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
@@ -359,12 +359,12 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
return;
}
let container = this.getViewContainer(entry.key);
if (!container) {
const viewContainer = this.getViewContainer(entry.key);
if (!viewContainer) {
collector.warn(localize('ViewContainerDoesnotExist', "View container '{0}' does not exist and all views registered to it will be added to 'Explorer'.", entry.key));
container = this.getDefaultViewContainer();
}
const registeredViews = ViewsRegistry.getViews(container);
const container = viewContainer || this.getDefaultViewContainer();
const registeredViews = this.viewsRegistry.getViews(container);
const viewIds: string[] = [];
const viewDescriptors = coalesce(entry.value.map((item, index) => {
// validate
@@ -380,7 +380,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
const viewDescriptor = <ICustomViewDescriptor>{
id: item.id,
name: item.name,
ctor: CustomTreeViewPanel,
ctorDescriptor: { ctor: CustomTreeViewPanel },
when: ContextKeyExpr.deserialize(item.when),
canToggleVisibility: true,
collapsed: this.showCollapsed(container),
@@ -393,21 +393,21 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
viewIds.push(viewDescriptor.id);
return viewDescriptor;
}));
ViewsRegistry.registerViews(viewDescriptors, container);
this.viewsRegistry.registerViews(viewDescriptors, container);
});
}
}
private getDefaultViewContainer(): ViewContainer {
return this.viewContainersRegistry.get(EXPLORER);
return this.viewContainersRegistry.get(EXPLORER)!;
}
private removeViews(extensions: IExtensionPointUser<ViewExtensionPointType>[]): void {
const removedExtensions: Set<string> = extensions.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set<string>());
for (const viewContainer of this.viewContainersRegistry.all) {
const removedViews = ViewsRegistry.getViews(viewContainer).filter((v: ICustomViewDescriptor) => v.extensionId && removedExtensions.has(ExtensionIdentifier.toKey(v.extensionId)));
const removedViews = this.viewsRegistry.getViews(viewContainer).filter((v: ICustomViewDescriptor) => v.extensionId && removedExtensions.has(ExtensionIdentifier.toKey(v.extensionId)));
if (removedViews.length) {
ViewsRegistry.deregisterViews(removedViews, viewContainer);
this.viewsRegistry.deregisterViews(removedViews, viewContainer);
}
}
}
@@ -436,7 +436,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
return true;
}
private getViewContainer(value: string): ViewContainer {
private getViewContainer(value: string): ViewContainer | undefined {
switch (value) {
case 'explorer': return this.viewContainersRegistry.get(EXPLORER);
case 'debug': return this.viewContainersRegistry.get(DEBUG);

View File

@@ -91,8 +91,7 @@ const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint<I
$ref: editorConfigurationSchemaId,
}
}
},
isDynamic: true
}
});
defaultConfigurationExtPoint.setHandler((extensions, { added, removed }) => {
if (removed.length) {
@@ -134,8 +133,7 @@ const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IConfigu
items: configurationEntrySchema
}
]
},
isDynamic: true
}
});
const extensionConfigurations: Map<string, IConfigurationNode[]> = new Map<string, IConfigurationNode[]>();

View File

@@ -0,0 +1,81 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import * as strings from 'vs/base/common/strings';
import * as resources from 'vs/base/common/resources';
interface IJSONValidationExtensionPoint {
fileMatch: string;
url: string;
}
const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint<IJSONValidationExtensionPoint[]>({
extensionPoint: 'jsonValidation',
defaultExtensionKind: 'workspace',
jsonSchema: {
description: nls.localize('contributes.jsonValidation', 'Contributes json schema configuration.'),
type: 'array',
defaultSnippets: [{ body: [{ fileMatch: '${1:file.json}', url: '${2:url}' }] }],
items: {
type: 'object',
defaultSnippets: [{ body: { fileMatch: '${1:file.json}', url: '${2:url}' } }],
properties: {
fileMatch: {
type: 'string',
description: nls.localize('contributes.jsonValidation.fileMatch', 'The file pattern to match, for example "package.json" or "*.launch".'),
},
url: {
description: nls.localize('contributes.jsonValidation.url', 'A schema URL (\'http:\', \'https:\') or relative path to the extension folder (\'./\').'),
type: 'string'
}
}
}
}
});
export class JSONValidationExtensionPoint {
constructor() {
configurationExtPoint.setHandler((extensions) => {
for (const extension of extensions) {
const extensionValue = <IJSONValidationExtensionPoint[]>extension.value;
const collector = extension.collector;
const extensionLocation = extension.description.extensionLocation;
if (!extensionValue || !Array.isArray(extensionValue)) {
collector.error(nls.localize('invalid.jsonValidation', "'configuration.jsonValidation' must be a array"));
return;
}
extensionValue.forEach(extension => {
if (typeof extension.fileMatch !== 'string') {
collector.error(nls.localize('invalid.fileMatch', "'configuration.jsonValidation.fileMatch' must be defined"));
return;
}
let uri = extension.url;
if (typeof extension.url !== 'string') {
collector.error(nls.localize('invalid.url', "'configuration.jsonValidation.url' must be a URL or relative path"));
return;
}
if (strings.startsWith(uri, './')) {
try {
const colorThemeLocation = resources.joinPath(extensionLocation, uri);
if (!resources.isEqualOrParent(colorThemeLocation, extensionLocation)) {
collector.warn(nls.localize('invalid.path.1', "Expected `contributes.{0}.url` ({1}) to be included inside extension's folder ({2}). This might make the extension non-portable.", configurationExtPoint.name, colorThemeLocation.toString(), extensionLocation.path));
}
} catch (e) {
collector.error(nls.localize('invalid.url.fileschema', "'configuration.jsonValidation.url' is an invalid relative URL: {0}", e.message));
}
} else if (!strings.startsWith(uri, 'https:/') && strings.startsWith(uri, 'https:/')) {
collector.error(nls.localize('invalid.url.schema', "'configuration.jsonValidation.url' must start with 'http:', 'https:' or './' to reference schemas located in the extension"));
return;
}
});
}
});
}
}

View File

@@ -34,6 +34,7 @@ namespace schema {
case 'explorer/context': return MenuId.ExplorerContext;
case 'editor/title/context': return MenuId.EditorTitleContext;
case 'debug/callstack/context': return MenuId.DebugCallStackContext;
case 'debug/toolbar': return MenuId.DebugToolbar;
case 'scm/title': return MenuId.SCMTitle;
case 'scm/sourceControl': return MenuId.SCMSourceControl;
case 'scm/resourceGroup/context': return MenuId.SCMResourceGroupContext;
@@ -139,6 +140,11 @@ namespace schema {
type: 'array',
items: menuItem
},
'debug/toolbar': {
description: localize('menus.debugToolbar', "The debug toolbar menu"),
type: 'array',
items: menuItem
},
'scm/title': {
description: localize('menus.scmTitle', "The Source Control title menu"),
type: 'array',
@@ -286,8 +292,7 @@ let _commandRegistrations: IDisposable[] = [];
ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlyCommand | schema.IUserFriendlyCommand[]>({
extensionPoint: 'commands',
jsonSchema: schema.commandsContribution,
isDynamic: true
jsonSchema: schema.commandsContribution
}).setHandler(extensions => {
function handleCommand(userFriendlyCommand: schema.IUserFriendlyCommand, extension: IExtensionPointUser<any>, disposables: IDisposable[]) {
@@ -336,8 +341,7 @@ let _menuRegistrations: IDisposable[] = [];
ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyMenuItem[] }>({
extensionPoint: 'menus',
jsonSchema: schema.menusContribtion,
isDynamic: true
jsonSchema: schema.menusContribtion
}).setHandler(extensions => {
// remove all previous menu registrations

View File

@@ -9,9 +9,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
// --- other interested parties
import { JSONValidationExtensionPoint } from 'vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint';
import { JSONValidationExtensionPoint } from 'vs/workbench/api/common/jsonValidationExtensionPoint';
import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorExtensionPoint';
import { LanguageConfigurationFileHandler } from 'vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint';
import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint';
// --- mainThread participants
import 'vs/workbench/api/node/apiCommands';

View File

@@ -20,6 +20,6 @@ export class MainThreadCommands implements MainThreadClipboardShape {
$writeText(value: string): Promise<void> {
clipboard.writeText(value);
return undefined;
return Promise.resolve();
}
}

View File

@@ -64,13 +64,14 @@ export class MainThreadCommands implements MainThreadCommandsShape {
}
$unregisterCommand(id: string): void {
if (this._disposables.has(id)) {
this._disposables.get(id).dispose();
const command = this._disposables.get(id);
if (command) {
command.dispose();
this._disposables.delete(id);
}
}
$executeCommand<T>(id: string, args: any[]): Promise<T> {
$executeCommand<T>(id: string, args: any[]): Promise<T | undefined> {
for (let i = 0; i < args.length; i++) {
args[i] = revive(args[i], 0);
}
@@ -88,7 +89,7 @@ function _generateMarkdown(description: string | ICommandHandlerDescription): st
if (typeof description === 'string') {
return description;
} else {
let parts = [description.description];
const parts = [description.description];
parts.push('\n\n');
if (description.args) {
for (let arg of description.args) {

View File

@@ -11,26 +11,28 @@ import { keys } from 'vs/base/common/map';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape, CommentProviderFeatures } from '../node/extHost.protocol';
import { ICommentService } from 'vs/workbench/parts/comments/electron-browser/commentService';
import { COMMENTS_PANEL_ID, CommentsPanel, COMMENTS_PANEL_TITLE } from 'vs/workbench/parts/comments/electron-browser/commentsPanel';
import { ICommentService, ICommentInfo } from 'vs/workbench/contrib/comments/electron-browser/commentService';
import { COMMENTS_PANEL_ID, CommentsPanel, COMMENTS_PANEL_TITLE } from 'vs/workbench/contrib/comments/electron-browser/commentsPanel';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { generateUuid } from 'vs/base/common/uuid';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICommentsConfiguration } from 'vs/workbench/parts/comments/electron-browser/comments.contribution';
import { ICommentsConfiguration } from 'vs/workbench/contrib/comments/electron-browser/comments.contribution';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
import { IRange } from 'vs/editor/common/core/range';
import { Emitter, Event } from 'vs/base/common/event';
export class MainThreadDocumentCommentProvider implements modes.DocumentCommentProvider {
private _proxy: ExtHostCommentsShape;
private _handle: number;
private _features: CommentProviderFeatures;
get startDraftLabel(): string { return this._features.startDraftLabel; }
get deleteDraftLabel(): string { return this._features.deleteDraftLabel; }
get finishDraftLabel(): string { return this._features.finishDraftLabel; }
get reactionGroup(): modes.CommentReaction[] { return this._features.reactionGroup; }
private readonly _proxy: ExtHostCommentsShape;
private readonly _handle: number;
private readonly _features: CommentProviderFeatures;
get startDraftLabel(): string | undefined { return this._features.startDraftLabel; }
get deleteDraftLabel(): string | undefined { return this._features.deleteDraftLabel; }
get finishDraftLabel(): string | undefined { return this._features.finishDraftLabel; }
get reactionGroup(): modes.CommentReaction[] | undefined { return this._features.reactionGroup; }
constructor(proxy: ExtHostCommentsShape, handle: number, features: CommentProviderFeatures) {
this._proxy = proxy;
@@ -75,16 +77,330 @@ export class MainThreadDocumentCommentProvider implements modes.DocumentCommentP
}
onDidChangeCommentThreads = null;
// onDidChangeCommentThreads = null;
}
export class MainThreadCommentThread implements modes.CommentThread2 {
private _input?: modes.CommentInput;
get input(): modes.CommentInput | undefined {
return this._input;
}
set input(value: modes.CommentInput | undefined) {
this._input = value;
this._onDidChangeInput.fire(value);
}
private _onDidChangeInput = new Emitter<modes.CommentInput | undefined>();
get onDidChangeInput(): Event<modes.CommentInput | undefined> { return this._onDidChangeInput.event; }
private _label: string;
get label(): string {
return this._label;
}
set label(label: string) {
this._label = label;
this._onDidChangeLabel.fire(this._label);
}
private _onDidChangeLabel = new Emitter<string>();
get onDidChangeLabel(): Event<string> { return this._onDidChangeLabel.event; }
public get comments(): modes.Comment[] {
return this._comments;
}
public set comments(newComments: modes.Comment[]) {
this._comments = newComments;
this._onDidChangeComments.fire(this._comments);
}
private _onDidChangeComments = new Emitter<modes.Comment[]>();
get onDidChangeComments(): Event<modes.Comment[]> { return this._onDidChangeComments.event; }
set acceptInputCommand(newCommand: modes.Command) {
this._acceptInputCommand = newCommand;
this._onDidChangeAcceptInputCommand.fire(this._acceptInputCommand);
}
get acceptInputCommand(): modes.Command {
return this._acceptInputCommand!;
}
private _onDidChangeAcceptInputCommand = new Emitter<modes.Command>();
get onDidChangeAcceptInputCommand(): Event<modes.Command> { return this._onDidChangeAcceptInputCommand.event; }
set additionalCommands(newCommands: modes.Command[]) {
this._additionalCommands = newCommands;
this._onDidChangeAdditionalCommands.fire(this._additionalCommands);
}
get additionalCommands(): modes.Command[] {
return this._additionalCommands;
}
private _onDidChangeAdditionalCommands = new Emitter<modes.Command[]>();
get onDidChangeAdditionalCommands(): Event<modes.Command[]> { return this._onDidChangeAdditionalCommands.event; }
set range(range: IRange) {
this._range = range;
this._onDidChangeRange.fire(this._range);
}
get range(): IRange {
return this._range;
}
private _onDidChangeRange = new Emitter<IRange>();
public onDidChangeRange = this._onDidChangeRange.event;
get collapsibleState() {
return this._collapsibleState;
}
set collapsibleState(newState: modes.CommentThreadCollapsibleState) {
this._collapsibleState = newState;
this._onDidChangeCollasibleState.fire(this._collapsibleState);
}
private _onDidChangeCollasibleState = new Emitter<modes.CommentThreadCollapsibleState>();
public onDidChangeCollasibleState = this._onDidChangeCollasibleState.event;
constructor(
public commentThreadHandle: number,
public controller: MainThreadCommentController,
public extensionId: string,
public threadId: string,
public resource: string,
private _range: IRange,
private _comments: modes.Comment[],
private _acceptInputCommand: modes.Command | undefined,
private _additionalCommands: modes.Command[],
private _collapsibleState: modes.CommentThreadCollapsibleState
) {
}
dispose() { }
toJSON(): any {
return {
$mid: 7,
commentControlHandle: this.controller.handle,
commentThreadHandle: this.commentThreadHandle,
};
}
}
export class MainThreadCommentController {
get handle(): number {
return this._handle;
}
get id(): string {
return this._id;
}
get proxy(): ExtHostCommentsShape {
return this._proxy;
}
get label(): string {
return this._label;
}
private _reactions: modes.CommentReaction[] | undefined;
get reactions() {
return this._reactions;
}
set reactions(reactions: modes.CommentReaction[] | undefined) {
this._reactions = reactions;
}
private readonly _threads: Map<number, MainThreadCommentThread> = new Map<number, MainThreadCommentThread>();
public activeCommentThread?: MainThreadCommentThread;
constructor(
private readonly _proxy: ExtHostCommentsShape,
private readonly _commentService: ICommentService,
private readonly _handle: number,
private readonly _uniqueId: string,
private readonly _id: string,
private readonly _label: string,
private _features: CommentProviderFeatures
) { }
updateFeatures(features: CommentProviderFeatures) {
this._features = features;
}
createCommentThread(commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, comments: modes.Comment[], acceptInputCommand: modes.Command | undefined, additionalCommands: modes.Command[], collapseState: modes.CommentThreadCollapsibleState): modes.CommentThread2 {
let thread = new MainThreadCommentThread(
commentThreadHandle,
this,
'',
threadId,
URI.revive(resource).toString(),
range,
comments,
acceptInputCommand,
additionalCommands,
collapseState
);
this._threads.set(commentThreadHandle, thread);
this._commentService.updateComments(this._uniqueId, {
added: [thread],
removed: [],
changed: [],
draftMode: modes.DraftMode.NotSupported
});
return thread;
}
deleteCommentThread(commentThreadHandle: number) {
let thread = this.getKnownThread(commentThreadHandle);
this._threads.delete(commentThreadHandle);
this._commentService.updateComments(this._uniqueId, {
added: [],
removed: [thread],
changed: [],
draftMode: modes.DraftMode.NotSupported
});
thread.dispose();
}
updateComments(commentThreadHandle: number, comments: modes.Comment[]) {
let thread = this.getKnownThread(commentThreadHandle);
thread.comments = comments;
this._commentService.updateComments(this._uniqueId, {
added: [],
removed: [],
changed: [thread],
draftMode: modes.DraftMode.NotSupported
});
}
updateAcceptInputCommand(commentThreadHandle: number, acceptInputCommand: modes.Command) {
let thread = this.getKnownThread(commentThreadHandle);
thread.acceptInputCommand = acceptInputCommand;
}
updateAdditionalCommands(commentThreadHandle: number, additionalCommands: modes.Command[]) {
let thread = this.getKnownThread(commentThreadHandle);
thread.additionalCommands = additionalCommands;
}
updateCollapsibleState(commentThreadHandle: number, collapseState: modes.CommentThreadCollapsibleState) {
let thread = this.getKnownThread(commentThreadHandle);
thread.collapsibleState = collapseState;
}
updateCommentThreadRange(commentThreadHandle: number, range: IRange) {
let thread = this.getKnownThread(commentThreadHandle);
thread.range = range;
}
updateCommentThreadLabel(commentThreadHandle: number, label: string) {
let thread = this.getKnownThread(commentThreadHandle);
thread.label = label;
}
updateInput(input: string) {
let thread = this.activeCommentThread;
if (thread && thread.input) {
let commentInput = thread.input;
commentInput.value = input;
thread.input = commentInput;
}
}
private getKnownThread(commentThreadHandle: number) {
const thread = this._threads.get(commentThreadHandle);
if (!thread) {
throw new Error('unknown thread');
}
return thread;
}
async getDocumentComments(resource: URI, token) {
let ret: modes.CommentThread2[] = [];
for (let thread of keys(this._threads)) {
const commentThread = this._threads.get(thread)!;
if (commentThread.resource === resource.toString()) {
ret.push(commentThread);
}
}
let commentingRanges = await this._proxy.$provideCommentingRanges(this.handle, resource, token);
return <ICommentInfo>{
owner: this._uniqueId,
threads: ret,
commentingRanges: commentingRanges ?
{
resource: resource, ranges: commentingRanges, newCommentThreadCallback: (uri: UriComponents, range: IRange) => {
this._proxy.$createNewCommentWidgetCallback(this.handle, uri, range, token);
}
} : [],
draftMode: modes.DraftMode.NotSupported
};
}
async getCommentingRanges(resource: URI, token): Promise<IRange[]> {
let commentingRanges = await this._proxy.$provideCommentingRanges(this.handle, resource, token);
return commentingRanges || [];
}
getReactionGroup(): modes.CommentReaction[] | undefined {
return this._features.reactionGroup;
}
async toggleReaction(uri, thread: modes.CommentThread2, comment: modes.Comment, reaction: modes.CommentReaction, token): Promise<void> {
return this._proxy.$toggleReaction(this._handle, thread.commentThreadHandle, uri, comment, reaction);
}
getAllComments(): MainThreadCommentThread[] {
let ret: MainThreadCommentThread[] = [];
for (let thread of keys(this._threads)) {
ret.push(this._threads.get(thread)!);
}
return ret;
}
toJSON(): any {
return {
$mid: 6,
handle: this.handle
};
}
}
@extHostNamedCustomer(MainContext.MainThreadComments)
export class MainThreadComments extends Disposable implements MainThreadCommentsShape {
private _disposables: IDisposable[];
private _proxy: ExtHostCommentsShape;
private _activeCommentThreadDisposables: IDisposable[];
private readonly _proxy: ExtHostCommentsShape;
private _documentProviders = new Map<number, IDisposable>();
private _workspaceProviders = new Map<number, IDisposable>();
private _handlers = new Map<number, string>();
private _commentControllers = new Map<number, MainThreadCommentController>();
private _activeCommentThread?: MainThreadCommentThread;
private _input?: modes.CommentInput;
private _openPanelListener: IDisposable | null;
constructor(
@@ -97,7 +413,149 @@ export class MainThreadComments extends Disposable implements MainThreadComments
) {
super();
this._disposables = [];
this._activeCommentThreadDisposables = [];
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments);
this._disposables.push(this._commentService.onDidChangeActiveCommentThread(async thread => {
let controller = (thread as MainThreadCommentThread).controller;
if (!controller) {
return;
}
this._activeCommentThreadDisposables = dispose(this._activeCommentThreadDisposables);
this._activeCommentThread = thread as MainThreadCommentThread;
controller.activeCommentThread = this._activeCommentThread;
this._activeCommentThreadDisposables.push(this._activeCommentThread.onDidChangeInput(input => { // todo, dispose
this._input = input;
this._proxy.$onCommentWidgetInputChange(controller.handle, this._input ? this._input.value : undefined);
}));
await this._proxy.$onCommentWidgetInputChange(controller.handle, this._input ? this._input.value : undefined);
}));
}
$registerCommentController(handle: number, id: string, label: string): void {
const providerId = generateUuid();
this._handlers.set(handle, providerId);
const provider = new MainThreadCommentController(this._proxy, this._commentService, handle, providerId, id, label, {});
this._commentService.registerCommentController(providerId, provider);
this._commentControllers.set(handle, provider);
const commentsPanelAlreadyConstructed = this._panelService.getPanels().some(panel => panel.id === COMMENTS_PANEL_ID);
if (!commentsPanelAlreadyConstructed) {
this.registerPanel(commentsPanelAlreadyConstructed);
this.registerOpenPanelListener(commentsPanelAlreadyConstructed);
}
this._commentService.setWorkspaceComments(String(handle), []);
}
$unregisterCommentController(handle: number): void {
const providerId = this._handlers.get(handle);
this._commentService.unregisterCommentController(providerId);
this._handlers.delete(handle);
this._commentControllers.delete(handle);
}
$updateCommentControllerFeatures(handle: number, features: CommentProviderFeatures): void {
let provider = this._commentControllers.get(handle);
if (!provider) {
return undefined;
}
provider.updateFeatures(features);
}
$createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, comments: modes.Comment[], acceptInputCommand: modes.Command | undefined, additionalCommands: modes.Command[], collapseState: modes.CommentThreadCollapsibleState): modes.CommentThread2 | undefined {
let provider = this._commentControllers.get(handle);
if (!provider) {
return undefined;
}
return provider.createCommentThread(commentThreadHandle, threadId, resource, range, comments, acceptInputCommand, additionalCommands, collapseState);
}
$deleteCommentThread(handle: number, commentThreadHandle: number) {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
return provider.deleteCommentThread(commentThreadHandle);
}
$updateComments(handle: number, commentThreadHandle: number, comments: modes.Comment[]) {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateComments(commentThreadHandle, comments);
}
$setInputValue(handle: number, input: string) {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateInput(input);
}
$updateCommentThreadAcceptInputCommand(handle: number, commentThreadHandle: number, acceptInputCommand: modes.Command) {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateAcceptInputCommand(commentThreadHandle, acceptInputCommand);
}
$updateCommentThreadAdditionalCommands(handle: number, commentThreadHandle: number, additionalCommands: modes.Command[]) {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateAdditionalCommands(commentThreadHandle, additionalCommands);
}
$updateCommentThreadCollapsibleState(handle: number, commentThreadHandle: number, collapseState: modes.CommentThreadCollapsibleState): void {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateCollapsibleState(commentThreadHandle, collapseState);
}
$updateCommentThreadRange(handle: number, commentThreadHandle: number, range: any): void {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateCommentThreadRange(commentThreadHandle, range);
}
$updateCommentThreadLabel(handle: number, commentThreadHandle: number, label: string): void {
let provider = this._commentControllers.get(handle);
if (!provider) {
return;
}
provider.updateCommentThreadLabel(commentThreadHandle, label);
}
$registerDocumentCommentProvider(handle: number, features: CommentProviderFeatures): void {
@@ -110,6 +568,18 @@ export class MainThreadComments extends Disposable implements MainThreadComments
this._commentService.registerDataProvider(providerId, handler);
}
private registerPanel(commentsPanelAlreadyConstructed: boolean) {
if (!commentsPanelAlreadyConstructed) {
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescriptor(
CommentsPanel,
COMMENTS_PANEL_ID,
COMMENTS_PANEL_TITLE,
'commentsPanel',
10
));
}
}
/**
* If the comments panel has never been opened, the constructor for it has not yet run so it has
* no listeners for comment threads being set or updated. Listen for the panel opening for the
@@ -122,20 +592,37 @@ export class MainThreadComments extends Disposable implements MainThreadComments
keys(this._workspaceProviders).forEach(handle => {
this._proxy.$provideWorkspaceComments(handle).then(commentThreads => {
if (commentThreads) {
const providerId = this._handlers.get(handle);
const providerId = this.getHandler(handle);
this._commentService.setWorkspaceComments(providerId, commentThreads);
}
});
});
this._openPanelListener.dispose();
this._openPanelListener = null;
keys(this._commentControllers).forEach(handle => {
let threads = this._commentControllers.get(handle)!.getAllComments();
if (threads.length) {
const providerId = this.getHandler(handle);
this._commentService.setWorkspaceComments(providerId, threads);
}
});
if (this._openPanelListener) {
this._openPanelListener.dispose();
this._openPanelListener = null;
}
}
});
}
}
private getHandler(handle: number) {
if (!this._handlers.has(handle)) {
throw new Error('Unknown handler');
}
return this._handlers.get(handle)!;
}
$registerWorkspaceCommentProvider(handle: number, extensionId: ExtensionIdentifier): void {
this._workspaceProviders.set(handle, undefined);
@@ -143,13 +630,9 @@ export class MainThreadComments extends Disposable implements MainThreadComments
this._handlers.set(handle, providerId);
const commentsPanelAlreadyConstructed = this._panelService.getPanels().some(panel => panel.id === COMMENTS_PANEL_ID);
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescriptor(
CommentsPanel,
COMMENTS_PANEL_ID,
COMMENTS_PANEL_TITLE,
'commentsPanel',
10
));
if (!commentsPanelAlreadyConstructed) {
this.registerPanel(commentsPanelAlreadyConstructed);
}
const openPanel = this._configurationService.getValue<ICommentsConfiguration>('comments').openPanel;
@@ -187,7 +670,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
$unregisterDocumentCommentProvider(handle: number): void {
this._documentProviders.delete(handle);
const handlerId = this._handlers.get(handle);
const handlerId = this.getHandler(handle);
this._commentService.unregisterDataProvider(handlerId);
this._handlers.delete(handle);
}
@@ -203,14 +686,14 @@ export class MainThreadComments extends Disposable implements MainThreadComments
}
}
const handlerId = this._handlers.get(handle);
const handlerId = this.getHandler(handle);
this._commentService.removeWorkspaceComments(handlerId);
this._handlers.delete(handle);
}
$onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent) {
// notify comment service
const providerId = this._handlers.get(handle);
const providerId = this.getHandler(handle);
this._commentService.updateComments(providerId, event);
}
@@ -234,13 +717,16 @@ export class MainThreadComments extends Disposable implements MainThreadComments
async provideWorkspaceComments(): Promise<modes.CommentThread[]> {
const result: modes.CommentThread[] = [];
for (const handle of keys(this._workspaceProviders)) {
result.push(...await this._proxy.$provideWorkspaceComments(handle));
const result = await this._proxy.$provideWorkspaceComments(handle);
if (Array.isArray(result)) {
result.push(...result);
}
}
return result;
}
async provideDocumentComments(resource: URI): Promise<modes.CommentInfo[]> {
const result: modes.CommentInfo[] = [];
async provideDocumentComments(resource: URI): Promise<Array<modes.CommentInfo | null>> {
const result: Array<modes.CommentInfo | null> = [];
for (const handle of keys(this._documentProviders)) {
result.push(await this._proxy.$provideDocumentComments(handle, resource));
}
@@ -249,6 +735,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
dispose(): void {
this._disposables = dispose(this._disposables);
this._activeCommentThreadDisposables = dispose(this._activeCommentThreadDisposables);
this._workspaceProviders.forEach(value => dispose(value));
this._workspaceProviders.clear();
this._documentProviders.forEach(value => dispose(value));

View File

@@ -8,10 +8,9 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, getScopes } from 'vs/platform/configuration/common/configurationRegistry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext, IWorkspaceConfigurationChangeEventData, IConfigurationInitData } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationModel, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@extHostNamedCustomer(MainContext.MainThreadConfiguration)
@@ -22,7 +21,7 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape {
constructor(
extHostContext: IExtHostContext,
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
) {
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostConfiguration);
@@ -34,7 +33,7 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape {
}
private _getConfigurationData(): IConfigurationInitData {
const configurationData: IConfigurationInitData = { ...this.configurationService.getConfigurationData(), configurationScopes: {} };
const configurationData: IConfigurationInitData = { ...(this.configurationService.getConfigurationData()!), configurationScopes: {} };
// Send configurations scopes only in development mode.
if (!this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment) {
configurationData.configurationScopes = getScopes();
@@ -46,22 +45,22 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape {
this._configurationListener.dispose();
}
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resourceUriComponenets: UriComponents): Promise<void> {
$updateConfigurationOption(target: ConfigurationTarget | null, key: string, value: any, resourceUriComponenets: UriComponents | undefined): Promise<void> {
const resource = resourceUriComponenets ? URI.revive(resourceUriComponenets) : null;
return this.writeConfiguration(target, key, value, resource);
}
$removeConfigurationOption(target: ConfigurationTarget, key: string, resourceUriComponenets: UriComponents): Promise<void> {
$removeConfigurationOption(target: ConfigurationTarget | null, key: string, resourceUriComponenets: UriComponents | undefined): Promise<void> {
const resource = resourceUriComponenets ? URI.revive(resourceUriComponenets) : null;
return this.writeConfiguration(target, key, undefined, resource);
}
private writeConfiguration(target: ConfigurationTarget, key: string, value: any, resource: URI): Promise<void> {
private writeConfiguration(target: ConfigurationTarget | null, key: string, value: any, resource: URI | null): Promise<void> {
target = target !== null && target !== undefined ? target : this.deriveConfigurationTarget(key, resource);
return this.configurationService.updateValue(key, value, { resource }, target, true);
}
private deriveConfigurationTarget(key: string, resource: URI): ConfigurationTarget {
private deriveConfigurationTarget(key: string, resource: URI | null): ConfigurationTarget {
if (resource && this._workspaceContextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
const configurationProperties = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
if (configurationProperties[key] && configurationProperties[key].scope === ConfigurationScope.RESOURCE) {

View File

@@ -9,7 +9,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console';
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/electron-browser/extensionHost';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService';
import { IBroadcastService } from 'vs/workbench/services/broadcast/electron-browser/broadcastService';
import { EXTENSION_LOG_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
@extHostNamedCustomer(MainContext.MainThreadConsole)

View File

@@ -8,29 +8,29 @@
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { URI as uri } from 'vs/base/common/uri';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, ITerminalSettings, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDebugAdapterTrackerFactory } from 'vs/workbench/parts/debug/common/debug';
import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, ITerminalSettings, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDebugAdapterTrackerFactory } from 'vs/workbench/contrib/debug/common/debug';
import {
ExtHostContext, ExtHostDebugServiceShape, MainThreadDebugServiceShape, DebugSessionUUID, MainContext,
IExtHostContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, ISourceBreakpointDto, IFunctionBreakpointDto, IDebugSessionDto
} from 'vs/workbench/api/node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import severity from 'vs/base/common/severity';
import { AbstractDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/parts/debug/common/debugUtils';
import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/contrib/debug/common/debugUtils';
@extHostNamedCustomer(MainContext.MainThreadDebugService)
export class MainThreadDebugService implements MainThreadDebugServiceShape, IDebugAdapterFactory {
private _proxy: ExtHostDebugServiceShape;
private readonly _proxy: ExtHostDebugServiceShape;
private _toDispose: IDisposable[];
private _breakpointEventsActive: boolean;
private _debugAdapters: Map<number, ExtensionHostDebugAdapter>;
private readonly _debugAdapters: Map<number, ExtensionHostDebugAdapter>;
private _debugAdaptersHandleCounter = 1;
private _debugConfigurationProviders: Map<number, IDebugConfigurationProvider>;
private _debugAdapterDescriptorFactories: Map<number, IDebugAdapterDescriptorFactory>;
private _debugAdapterTrackerFactories: Map<number, IDebugAdapterTrackerFactory>;
private _sessions: Set<DebugSessionUUID>;
private readonly _debugConfigurationProviders: Map<number, IDebugConfigurationProvider>;
private readonly _debugAdapterDescriptorFactories: Map<number, IDebugAdapterDescriptorFactory>;
private readonly _debugAdapterTrackerFactories: Map<number, IDebugAdapterTrackerFactory>;
private readonly _sessions: Set<DebugSessionUUID>;
constructor(
extHostContext: IExtHostContext,
@@ -73,7 +73,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
return da;
}
substituteVariables(folder: IWorkspaceFolder, config: IConfig): Promise<IConfig> {
substituteVariables(folder: IWorkspaceFolder | undefined, config: IConfig): Promise<IConfig> {
return Promise.resolve(this._proxy.$substituteVariables(folder ? folder.uri : undefined, config));
}
@@ -144,13 +144,13 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
this.debugService.addFunctionBreakpoint(dto.functionName, dto.id);
}
}
return undefined;
return Promise.resolve();
}
public $unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Promise<void> {
breakpointIds.forEach(id => this.debugService.removeBreakpoints(id));
functionBreakpointIds.forEach(id => this.debugService.removeFunctionBreakpoints(id));
return undefined;
return Promise.resolve();
}
@@ -161,18 +161,18 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
};
if (hasProvide) {
provider.provideDebugConfigurations = (folder) => {
return Promise.resolve(this._proxy.$provideDebugConfigurations(handle, folder));
return this._proxy.$provideDebugConfigurations(handle, folder);
};
}
if (hasResolve) {
provider.resolveDebugConfiguration = (folder, config) => {
return Promise.resolve(this._proxy.$resolveDebugConfiguration(handle, folder, config));
return this._proxy.$resolveDebugConfiguration(handle, folder, config);
};
}
if (hasProvideDebugAdapter) {
console.info('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.');
provider.debugAdapterExecutable = (folder) => {
return Promise.resolve(this._proxy.$legacyDebugAdapterExecutable(handle, folder));
return this._proxy.$legacyDebugAdapterExecutable(handle, folder);
};
}
this._debugConfigurationProviders.set(handle, provider);
@@ -229,10 +229,17 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
}
}
public $startDebugging(_folderUri: uri | undefined, nameOrConfiguration: string | IConfig): Promise<boolean> {
private getSession(sessionId: DebugSessionUUID | undefined): IDebugSession | undefined {
if (sessionId) {
return this.debugService.getModel().getSessions(true).filter(s => s.getId() === sessionId).pop();
}
return undefined;
}
public $startDebugging(_folderUri: uri | undefined, nameOrConfiguration: string | IConfig, parentSessionID: DebugSessionUUID | undefined): Promise<boolean> {
const folderUri = _folderUri ? uri.revive(_folderUri) : undefined;
const launch = this.debugService.getConfigurationManager().getLaunch(folderUri);
return this.debugService.startDebugging(launch, nameOrConfiguration).then(success => {
return this.debugService.startDebugging(launch, nameOrConfiguration, false, this.getSession(parentSessionID)).then(success => {
return success;
}, err => {
return Promise.reject(new Error(err && err.message ? err.message : 'cannot start debugging'));
@@ -262,26 +269,44 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
}
public $acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage) {
this._debugAdapters.get(handle).acceptMessage(convertToVSCPaths(message, false));
this.getDebugAdapter(handle).acceptMessage(convertToVSCPaths(message, false));
}
public $acceptDAError(handle: number, name: string, message: string, stack: string) {
this._debugAdapters.get(handle).fireError(handle, new Error(`${name}: ${message}\n${stack}`));
this.getDebugAdapter(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);
this.getDebugAdapter(handle).fireExit(handle, code, signal);
}
private getDebugAdapter(handle: number): ExtensionHostDebugAdapter {
const adapter = this._debugAdapters.get(handle);
if (!adapter) {
throw new Error('Invalid debug adapter');
}
return adapter;
}
// dto helpers
getSessionDto(session: IDebugSession): IDebugSessionDto {
public $sessionCached(sessionID: string) {
// remember that the EH has cached the session and we do not have to send it again
this._sessions.add(sessionID);
}
getSessionDto(session: undefined): undefined;
getSessionDto(session: IDebugSession): IDebugSessionDto;
getSessionDto(session: IDebugSession | undefined): IDebugSessionDto | undefined;
getSessionDto(session: IDebugSession | undefined): IDebugSessionDto | undefined {
if (session) {
const sessionID = <DebugSessionUUID>session.getId();
if (this._sessions.has(sessionID)) {
return sessionID;
} else {
this._sessions.add(sessionID);
// this._sessions.add(sessionID); // #69534: see $sessionCached above
return {
id: sessionID,
type: session.configuration.type,
@@ -333,7 +358,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
/*
class ExtensionHostDebugAdapter extends AbstractDebugAdapter {
constructor(private _ds: MainThreadDebugService, private _handle: number, private _proxy: ExtHostDebugServiceShape, private _session: IDebugSession) {
constructor(private readonly _ds: MainThreadDebugService, private _handle: number, private _proxy: ExtHostDebugServiceShape, private _session: IDebugSession) {
super();
}

View File

@@ -21,7 +21,7 @@ class DecorationRequestsQueue {
private _timer: any;
constructor(
private _proxy: ExtHostDecorationsShape
private readonly _proxy: ExtHostDecorationsShape
) {
//
}
@@ -109,13 +109,17 @@ export class MainThreadDecorations implements MainThreadDecorationsShape {
}
$onDidChange(handle: number, resources: UriComponents[]): void {
const [emitter] = this._provider.get(handle);
emitter.fire(resources && resources.map(URI.revive));
const provider = this._provider.get(handle);
if (provider) {
const [emitter] = provider;
emitter.fire(resources && resources.map(URI.revive));
}
}
$unregisterDecorationProvider(handle: number): void {
if (this._provider.has(handle)) {
dispose(this._provider.get(handle));
const provider = this._provider.get(handle);
if (provider) {
dispose(provider);
this._provider.delete(handle);
}
}

View File

@@ -22,7 +22,7 @@ export class MainThreadDiagnostics implements MainThreadDiagnosticsShape {
}
dispose(): void {
this._activeOwners.forEach(owner => this._markerService.changeAll(owner, undefined));
this._activeOwners.forEach(owner => this._markerService.changeAll(owner, []));
}
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void {
@@ -43,7 +43,7 @@ export class MainThreadDiagnostics implements MainThreadDiagnosticsShape {
}
$clear(owner: string): void {
this._markerService.changeAll(owner, undefined);
this._markerService.changeAll(owner, []);
this._activeOwners.delete(owner);
}
}

View File

@@ -23,11 +23,11 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
//
}
$showOpenDialog(options: MainThreadDialogOpenOptions): Promise<URI[]> {
$showOpenDialog(options: MainThreadDialogOpenOptions): Promise<URI[] | undefined> {
return Promise.resolve(this._fileDialogService.showOpenDialog(MainThreadDialogs._convertOpenOptions(options)));
}
$showSaveDialog(options: MainThreadDialogSaveOptions): Promise<URI> {
$showSaveDialog(options: MainThreadDialogSaveOptions): Promise<URI | undefined> {
return Promise.resolve(this._fileDialogService.showSaveDialog(MainThreadDialogs._convertSaveOptions(options)));
}
@@ -41,7 +41,7 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
};
if (options.filters) {
result.filters = [];
forEach(options.filters, entry => result.filters.push({ name: entry.key, extensions: entry.value }));
forEach(options.filters, entry => result.filters!.push({ name: entry.key, extensions: entry.value }));
}
return result;
}
@@ -53,7 +53,7 @@ export class MainThreadDialogs implements MainThreadDiaglogsShape {
};
if (options.filters) {
result.filters = [];
forEach(options.filters, entry => result.filters.push({ name: entry.key, extensions: entry.value }));
forEach(options.filters, entry => result.filters!.push({ name: entry.key, extensions: entry.value }));
}
return result;
}

View File

@@ -41,7 +41,7 @@ export class MainThreadDocumentContentProviders implements MainThreadDocumentCon
$registerTextContentProvider(handle: number, scheme: string): void {
const registration = this._textModelResolverService.registerTextModelContentProvider(scheme, {
provideTextContent: (uri: URI): Promise<ITextModel> => {
provideTextContent: (uri: URI): Promise<ITextModel | undefined> => {
return this._proxy.$provideTextDocumentContent(handle, uri).then(value => {
if (typeof value === 'string') {
const firstLineText = value.substr(0, 1 + value.search(/\r?\n/));
@@ -70,8 +70,9 @@ export class MainThreadDocumentContentProviders implements MainThreadDocumentCon
}
// cancel and dispose an existing update
if (this._pendingUpdate.has(model.id)) {
this._pendingUpdate.get(model.id).cancel();
const pending = this._pendingUpdate.get(model.id);
if (pending) {
pending.cancel();
}
// create and keep update token
@@ -86,7 +87,7 @@ export class MainThreadDocumentContentProviders implements MainThreadDocumentCon
// ignore this
return;
}
if (edits.length > 0) {
if (edits && edits.length > 0) {
// use the evil-edit as these models show in readonly-editor only
model.applyEdits(edits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
}

View File

@@ -24,8 +24,8 @@ export class BoundModelReferenceCollection {
private _length = 0;
constructor(
private _maxAge: number = 1000 * 60 * 3,
private _maxLength: number = 1024 * 1024 * 80
private readonly _maxAge: number = 1000 * 60 * 3,
private readonly _maxLength: number = 1024 * 1024 * 80
) {
//
}
@@ -35,11 +35,11 @@ export class BoundModelReferenceCollection {
}
add(ref: IReference<ITextEditorModel>): void {
let length = ref.object.textEditorModel.getValueLength();
const length = ref.object.textEditorModel.getValueLength();
let handle: any;
let entry: { length: number, dispose(): void };
const dispose = () => {
let idx = this._data.indexOf(entry);
const idx = this._data.indexOf(entry);
if (idx >= 0) {
this._length -= length;
ref.dispose();
@@ -64,16 +64,16 @@ export class BoundModelReferenceCollection {
export class MainThreadDocuments implements MainThreadDocumentsShape {
private _modelService: IModelService;
private _textModelResolverService: ITextModelService;
private _textFileService: ITextFileService;
private _fileService: IFileService;
private _untitledEditorService: IUntitledEditorService;
private readonly _modelService: IModelService;
private readonly _textModelResolverService: ITextModelService;
private readonly _textFileService: ITextFileService;
private readonly _fileService: IFileService;
private readonly _untitledEditorService: IUntitledEditorService;
private _toDispose: IDisposable[];
private _modelToDisposeMap: { [modelUrl: string]: IDisposable; };
private _proxy: ExtHostDocumentsShape;
private _modelIsSynced: { [modelId: string]: boolean; };
private readonly _proxy: ExtHostDocumentsShape;
private readonly _modelIsSynced: { [modelId: string]: boolean; };
private _modelReferenceCollection = new BoundModelReferenceCollection();
constructor(
@@ -130,16 +130,16 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
private _shouldHandleFileEvent(e: TextFileModelChangeEvent): boolean {
const model = this._modelService.getModel(e.resource);
return model && shouldSynchronizeModel(model);
return !!model && shouldSynchronizeModel(model);
}
private _onModelAdded(model: ITextModel): void {
// Same filter as in mainThreadEditorsTracker
if (!shouldSynchronizeModel(model)) {
// don't synchronize too large models
return null;
return;
}
let modelUrl = model.uri;
const modelUrl = model.uri;
this._modelIsSynced[modelUrl.toString()] = true;
this._modelToDisposeMap[modelUrl.toString()] = model.onDidChangeContent((e) => {
this._proxy.$acceptModelChanged(modelUrl, e, this._textFileService.isDirty(modelUrl));
@@ -148,7 +148,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
private _onModelModeChanged(event: { model: ITextModel; oldModeId: string; }): void {
let { model, oldModeId } = event;
let modelUrl = model.uri;
const modelUrl = model.uri;
if (!this._modelIsSynced[modelUrl.toString()]) {
return;
}
@@ -156,7 +156,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
}
private _onModelRemoved(modelUrl: URI): void {
let strModelUrl = modelUrl.toString();
const strModelUrl = modelUrl.toString();
if (!this._modelIsSynced[strModelUrl]) {
return;
}
@@ -214,7 +214,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
}
private _handleUntitledScheme(uri: URI): Promise<boolean> {
let asFileUri = uri.with({ scheme: Schemas.file });
const asFileUri = uri.with({ scheme: Schemas.file });
return this._fileService.resolveFile(asFileUri).then(stats => {
// don't create a new file ontop of an existing file
return Promise.reject(new Error('file already exists on disk'));

View File

@@ -7,7 +7,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
import { values } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser';
import { ICodeEditor, isCodeEditor, isDiffEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IEditor } from 'vs/editor/common/editorCommon';
@@ -25,7 +25,7 @@ import { EditorViewColumn, editorGroupToViewColumn } from 'vs/workbench/api/shar
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { IEditor as IWorkbenchEditor } from 'vs/workbench/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
@@ -70,7 +70,7 @@ class TextEditorSnapshot {
readonly id: string;
constructor(
readonly editor: ICodeEditor,
readonly editor: IActiveCodeEditor,
) {
this.id = `${editor.getId()},${editor.getModel().id}`;
}
@@ -85,8 +85,8 @@ class DocumentAndEditorStateDelta {
readonly addedDocuments: ITextModel[],
readonly removedEditors: TextEditorSnapshot[],
readonly addedEditors: TextEditorSnapshot[],
readonly oldActiveEditor: string,
readonly newActiveEditor: string,
readonly oldActiveEditor: string | null | undefined,
readonly newActiveEditor: string | null | undefined,
) {
this.isEmpty = this.removedDocuments.length === 0
&& this.addedDocuments.length === 0
@@ -131,7 +131,7 @@ class DocumentAndEditorState {
constructor(
readonly documents: Set<ITextModel>,
readonly textEditors: Map<string, TextEditorSnapshot>,
readonly activeEditor: string,
readonly activeEditor: string | null | undefined,
) {
//
}
@@ -230,14 +230,14 @@ class MainThreadDocumentAndEditorStateComputer {
// editor: only take those that have a not too large model
const editors = new Map<string, TextEditorSnapshot>();
let activeEditor: string | null = null;
let activeEditor: string | null = null; // Strict null work. This doesn't like being undefined!
for (const editor of this._codeEditorService.listCodeEditors()) {
if (editor.isSimpleWidget) {
continue;
}
const model = editor.getModel();
if (model && shouldSynchronizeModel(model)
if (editor.hasModel() && 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
) {
@@ -257,7 +257,7 @@ class MainThreadDocumentAndEditorStateComputer {
// to match output panels or the active workbench editor with
// one of editor we have just computed
if (!activeEditor) {
let candidate: IEditor;
let candidate: IEditor | undefined;
if (this._activeEditorOrder === ActiveEditorOrder.Editor) {
candidate = this._getActiveEditorFromEditorPart() || this._getActiveEditorFromPanel();
} else {
@@ -282,8 +282,8 @@ class MainThreadDocumentAndEditorStateComputer {
}
}
private _getActiveEditorFromPanel(): IEditor {
let panel = this._panelService.getActivePanel();
private _getActiveEditorFromPanel(): IEditor | undefined {
const panel = this._panelService.getActivePanel();
if (panel instanceof BaseTextEditor && isCodeEditor(panel.getControl())) {
return panel.getControl();
} else {
@@ -291,7 +291,7 @@ class MainThreadDocumentAndEditorStateComputer {
}
}
private _getActiveEditorFromEditorPart(): IEditor {
private _getActiveEditorFromEditorPart(): IEditor | undefined {
let result = this._editorService.activeTextEditorWidget;
if (isDiffEditor(result)) {
result = result.getModifiedEditor();
@@ -304,8 +304,8 @@ class MainThreadDocumentAndEditorStateComputer {
export class MainThreadDocumentsAndEditors {
private _toDispose: IDisposable[];
private _proxy: ExtHostDocumentsAndEditorsShape;
private _stateComputer: MainThreadDocumentAndEditorStateComputer;
private readonly _proxy: ExtHostDocumentsAndEditorsShape;
private readonly _stateComputer: MainThreadDocumentAndEditorStateComputer;
private _textEditors = <{ [id: string]: MainThreadTextEditor }>Object.create(null);
private _onTextEditorAdd = new Emitter<MainThreadTextEditor[]>();
@@ -362,8 +362,8 @@ export class MainThreadDocumentsAndEditors {
private _onDelta(delta: DocumentAndEditorStateDelta): void {
let removedDocuments: URI[];
let removedEditors: string[] = [];
let addedEditors: MainThreadTextEditor[] = [];
const removedEditors: string[] = [];
const addedEditors: MainThreadTextEditor[] = [];
// removed models
removedDocuments = delta.removedDocuments.map(m => m.uri);
@@ -387,7 +387,7 @@ export class MainThreadDocumentsAndEditors {
}
}
let extHostDelta: IDocumentsAndEditorsDelta = Object.create(null);
const extHostDelta: IDocumentsAndEditorsDelta = Object.create(null);
let empty = true;
if (delta.newActiveEditor !== undefined) {
empty = false;
@@ -444,8 +444,8 @@ export class MainThreadDocumentsAndEditors {
};
}
private _findEditorPosition(editor: MainThreadTextEditor): EditorViewColumn {
for (let workbenchEditor of this._editorService.visibleControls) {
private _findEditorPosition(editor: MainThreadTextEditor): EditorViewColumn | undefined {
for (const workbenchEditor of this._editorService.visibleControls) {
if (editor.matches(workbenchEditor)) {
return editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group);
}
@@ -453,8 +453,8 @@ export class MainThreadDocumentsAndEditors {
return undefined;
}
findTextEditorIdFor(editor: IWorkbenchEditor): string {
for (let id in this._textEditors) {
findTextEditorIdFor(editor: IWorkbenchEditor): string | undefined {
for (const id in this._textEditors) {
if (this._textEditors[id].matches(editor)) {
return id;
}

View File

@@ -16,6 +16,7 @@ import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2
import { IApplyEditsOptions, IEditorPropertiesChangeData, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, IUndoStopOptions, TextEditorRevealType } from 'vs/workbench/api/node/extHost.protocol';
import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
import { IEditor } from 'vs/workbench/common/editor';
import { withNullAsUndefined } from 'vs/base/common/types';
export interface IFocusTracker {
onGainedFocus(): void;
@@ -24,14 +25,14 @@ export interface IFocusTracker {
export class MainThreadTextEditorProperties {
public static readFromEditor(previousProperties: MainThreadTextEditorProperties, model: ITextModel, codeEditor: ICodeEditor): MainThreadTextEditorProperties {
public static readFromEditor(previousProperties: MainThreadTextEditorProperties | null, model: ITextModel, codeEditor: ICodeEditor | null): MainThreadTextEditorProperties {
const selections = MainThreadTextEditorProperties._readSelectionsFromCodeEditor(previousProperties, codeEditor);
const options = MainThreadTextEditorProperties._readOptionsFromCodeEditor(previousProperties, model, codeEditor);
const visibleRanges = MainThreadTextEditorProperties._readVisibleRangesFromCodeEditor(previousProperties, codeEditor);
return new MainThreadTextEditorProperties(selections, options, visibleRanges);
}
private static _readSelectionsFromCodeEditor(previousProperties: MainThreadTextEditorProperties, codeEditor: ICodeEditor): Selection[] {
private static _readSelectionsFromCodeEditor(previousProperties: MainThreadTextEditorProperties | null, codeEditor: ICodeEditor | null): Selection[] {
let result: Selection[] | null = null;
if (codeEditor) {
result = codeEditor.getSelections();
@@ -45,10 +46,14 @@ export class MainThreadTextEditorProperties {
return result;
}
private static _readOptionsFromCodeEditor(previousProperties: MainThreadTextEditorProperties, model: ITextModel, codeEditor: ICodeEditor): IResolvedTextEditorConfiguration {
private static _readOptionsFromCodeEditor(previousProperties: MainThreadTextEditorProperties | null, model: ITextModel, codeEditor: ICodeEditor | null): IResolvedTextEditorConfiguration {
if (model.isDisposed()) {
// shutdown time
return previousProperties.options;
if (previousProperties) {
// shutdown time
return previousProperties.options;
} else {
throw new Error('No valid properties');
}
}
let cursorStyle: TextEditorCursorStyle;
@@ -80,12 +85,13 @@ export class MainThreadTextEditorProperties {
return {
insertSpaces: modelOptions.insertSpaces,
tabSize: modelOptions.tabSize,
indentSize: modelOptions.indentSize,
cursorStyle: cursorStyle,
lineNumbers: lineNumbers
};
}
private static _readVisibleRangesFromCodeEditor(previousProperties: MainThreadTextEditorProperties, codeEditor: ICodeEditor): Range[] {
private static _readVisibleRangesFromCodeEditor(previousProperties: MainThreadTextEditorProperties | null, codeEditor: ICodeEditor | null): Range[] {
if (codeEditor) {
return codeEditor.getVisibleRanges();
}
@@ -99,8 +105,8 @@ export class MainThreadTextEditorProperties {
) {
}
public generateDelta(oldProps: MainThreadTextEditorProperties, selectionChangeSource: string): IEditorPropertiesChangeData {
let delta: IEditorPropertiesChangeData = {
public generateDelta(oldProps: MainThreadTextEditorProperties | null, selectionChangeSource: string | null): IEditorPropertiesChangeData | null {
const delta: IEditorPropertiesChangeData = {
options: null,
selections: null,
visibleRanges: null
@@ -109,7 +115,7 @@ export class MainThreadTextEditorProperties {
if (!oldProps || !MainThreadTextEditorProperties._selectionsEqual(oldProps.selections, this.selections)) {
delta.selections = {
selections: this.selections,
source: selectionChangeSource
source: withNullAsUndefined(selectionChangeSource)
};
}
@@ -162,6 +168,7 @@ export class MainThreadTextEditorProperties {
}
return (
a.tabSize === b.tabSize
&& a.indentSize === b.indentSize
&& a.insertSpaces === b.insertSpaces
&& a.cursorStyle === b.cursorStyle
&& a.lineNumbers === b.lineNumbers
@@ -175,12 +182,12 @@ export class MainThreadTextEditorProperties {
*/
export class MainThreadTextEditor {
private _id: string;
private readonly _id: string;
private _model: ITextModel;
private _modelService: IModelService;
private readonly _modelService: IModelService;
private _modelListeners: IDisposable[];
private _codeEditor: ICodeEditor;
private _focusTracker: IFocusTracker;
private _codeEditor: ICodeEditor | null;
private readonly _focusTracker: IFocusTracker;
private _codeEditorListeners: IDisposable[];
private _properties: MainThreadTextEditorProperties;
@@ -200,7 +207,6 @@ export class MainThreadTextEditor {
this._modelService = modelService;
this._codeEditorListeners = [];
this._properties = null;
this._onPropertiesChanged = new Emitter<IEditorPropertiesChangeData>();
this._modelListeners = [];
@@ -213,20 +219,20 @@ export class MainThreadTextEditor {
}
public dispose(): void {
this._model = null;
this._model = null!;
this._modelListeners = dispose(this._modelListeners);
this._codeEditor = null;
this._codeEditorListeners = dispose(this._codeEditorListeners);
}
private _updatePropertiesNow(selectionChangeSource: string): void {
private _updatePropertiesNow(selectionChangeSource: string | null): void {
this._setProperties(
MainThreadTextEditorProperties.readFromEditor(this._properties, this._model, this._codeEditor),
selectionChangeSource
);
}
private _setProperties(newProperties: MainThreadTextEditorProperties, selectionChangeSource: string): void {
private _setProperties(newProperties: MainThreadTextEditorProperties, selectionChangeSource: string | null): void {
const delta = newProperties.generateDelta(this._properties, selectionChangeSource);
this._properties = newProperties;
if (delta) {
@@ -242,15 +248,15 @@ export class MainThreadTextEditor {
return this._model;
}
public getCodeEditor(): ICodeEditor {
public getCodeEditor(): ICodeEditor | null {
return this._codeEditor;
}
public hasCodeEditor(codeEditor: ICodeEditor): boolean {
public hasCodeEditor(codeEditor: ICodeEditor | null): boolean {
return (this._codeEditor === codeEditor);
}
public setCodeEditor(codeEditor: ICodeEditor): void {
public setCodeEditor(codeEditor: ICodeEditor | null): void {
if (this.hasCodeEditor(codeEditor)) {
// Nothing to do...
return;
@@ -318,10 +324,10 @@ export class MainThreadTextEditor {
}
private _setIndentConfiguration(newConfiguration: ITextEditorConfigurationUpdate): void {
const creationOpts = this._modelService.getCreationOptions(this._model.getLanguageIdentifier().language, this._model.uri, this._model.isForSimpleWidget);
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, this._model.isForSimpleWidget);
let insertSpaces = creationOpts.insertSpaces;
let tabSize = creationOpts.tabSize;
@@ -337,13 +343,20 @@ export class MainThreadTextEditor {
return;
}
let newOpts: ITextModelUpdateOptions = {};
const newOpts: ITextModelUpdateOptions = {};
if (typeof newConfiguration.insertSpaces !== 'undefined') {
newOpts.insertSpaces = newConfiguration.insertSpaces;
}
if (typeof newConfiguration.tabSize !== 'undefined') {
newOpts.tabSize = newConfiguration.tabSize;
}
if (typeof newConfiguration.indentSize !== 'undefined') {
if (newConfiguration.indentSize === 'tabSize') {
newOpts.indentSize = newOpts.tabSize || creationOpts.tabSize;
} else {
newOpts.indentSize = newConfiguration.indentSize;
}
}
this._model.updateOptions(newOpts);
}
@@ -355,7 +368,7 @@ export class MainThreadTextEditor {
}
if (newConfiguration.cursorStyle) {
let newCursorStyle = cursorStyleToString(newConfiguration.cursorStyle);
const newCursorStyle = cursorStyleToString(newConfiguration.cursorStyle);
this._codeEditor.updateOptions({
cursorStyle: newCursorStyle
});
@@ -390,7 +403,7 @@ export class MainThreadTextEditor {
if (!this._codeEditor) {
return;
}
let ranges: Range[] = [];
const ranges: Range[] = [];
for (let i = 0, len = Math.floor(_ranges.length / 4); i < len; i++) {
ranges[i] = new Range(_ranges[4 * i], _ranges[4 * i + 1], _ranges[4 * i + 2], _ranges[4 * i + 3]);
}
@@ -452,7 +465,7 @@ export class MainThreadTextEditor {
this._model.pushEOL(EndOfLineSequence.LF);
}
let transformedEdits = edits.map((edit): IIdentifiedSingleEditOperation => {
const transformedEdits = edits.map((edit): IIdentifiedSingleEditOperation => {
return {
range: Range.lift(edit.range),
text: edit.text,

View File

@@ -23,20 +23,20 @@ import { MainThreadTextEditor } from 'vs/workbench/api/electron-browser/mainThre
import { ExtHostContext, ExtHostEditorsShape, IApplyEditsOptions, IExtHostContext, ITextDocumentShowOptions, ITextEditorConfigurationUpdate, ITextEditorPositionData, IUndoStopOptions, MainThreadTextEditorsShape, TextEditorRevealType, WorkspaceEditDto, reviveWorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/shared/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IURLService } from 'vs/platform/url/common/url';
import product from 'vs/platform/node/product';
import product from 'vs/platform/product/node/product';
export class MainThreadTextEditors implements MainThreadTextEditorsShape {
private static INSTANCE_COUNT: number = 0;
private _instanceId: string;
private _proxy: ExtHostEditorsShape;
private _documentsAndEditors: MainThreadDocumentsAndEditors;
private readonly _instanceId: string;
private readonly _proxy: ExtHostEditorsShape;
private readonly _documentsAndEditors: MainThreadDocumentsAndEditors;
private _toDispose: IDisposable[];
private _textEditorsListenersMap: { [editorId: string]: IDisposable[]; };
private _editorPositionData: ITextEditorPositionData;
private _editorPositionData: ITextEditorPositionData | null;
private _registeredDecorationTypes: { [decorationType: string]: boolean; };
constructor(
@@ -77,8 +77,8 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
}
private _onTextEditorAdd(textEditor: MainThreadTextEditor): void {
let id = textEditor.getId();
let toDispose: IDisposable[] = [];
const id = textEditor.getId();
const toDispose: IDisposable[] = [];
toDispose.push(textEditor.onPropertiesChanged((data) => {
this._proxy.$acceptEditorPropertiesChanged(id, data);
}));
@@ -94,7 +94,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
private _updateActiveAndVisibleTextEditors(): void {
// editor columns
let editorPositionData = this._getTextEditorPositionData();
const editorPositionData = this._getTextEditorPositionData();
if (!objectEquals(this._editorPositionData, editorPositionData)) {
this._editorPositionData = editorPositionData;
this._proxy.$acceptEditorPositionData(this._editorPositionData);
@@ -102,7 +102,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
}
private _getTextEditorPositionData(): ITextEditorPositionData {
let result: ITextEditorPositionData = Object.create(null);
const result: ITextEditorPositionData = Object.create(null);
for (let workbenchEditor of this._editorService.visibleControls) {
const id = this._documentsAndEditors.findTextEditorIdFor(workbenchEditor);
if (id) {
@@ -114,7 +114,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
// --- from extension host process
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise<string> {
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise<string | undefined> {
const uri = URI.revive(resource);
const editorOptions: ITextEditorOptions = {
@@ -137,28 +137,28 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
}
$tryShowEditor(id: string, position?: EditorViewColumn): Promise<void> {
let mainThreadEditor = this._documentsAndEditors.getEditor(id);
const mainThreadEditor = this._documentsAndEditors.getEditor(id);
if (mainThreadEditor) {
let model = mainThreadEditor.getModel();
const model = mainThreadEditor.getModel();
return this._editorService.openEditor({
resource: model.uri,
options: { preserveFocus: false }
}, viewColumnToEditorGroup(this._editorGroupService, position)).then(() => { return; });
}
return undefined;
return Promise.resolve();
}
$tryHideEditor(id: string): Promise<void> {
let mainThreadEditor = this._documentsAndEditors.getEditor(id);
const mainThreadEditor = this._documentsAndEditors.getEditor(id);
if (mainThreadEditor) {
let editors = this._editorService.visibleControls;
const editors = this._editorService.visibleControls;
for (let editor of editors) {
if (mainThreadEditor.matches(editor)) {
return editor.group.closeEditor(editor.input).then(() => { return; });
}
}
}
return undefined;
return Promise.resolve();
}
$trySetSelections(id: string, selections: ISelection[]): Promise<void> {
@@ -192,7 +192,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
return Promise.reject(disposed(`TextEditor(${id})`));
}
this._documentsAndEditors.getEditor(id).revealRange(range, revealType);
return undefined;
return Promise.resolve();
}
$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): Promise<void> {
@@ -242,12 +242,16 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
}
const codeEditor = editor.getCodeEditor();
if (!codeEditor) {
return Promise.reject(new Error('No such CodeEditor'));
}
const codeEditorId = codeEditor.getId();
const diffEditors = this._codeEditorService.listDiffEditors();
const [diffEditor] = diffEditors.filter(d => d.getOriginalEditor().getId() === codeEditorId || d.getModifiedEditor().getId() === codeEditorId);
if (diffEditor) {
return Promise.resolve(diffEditor.getLineChanges());
return Promise.resolve(diffEditor.getLineChanges() || []);
}
const dirtyDiffContribution = codeEditor.getContribution('editor.contrib.dirtydiff');

View File

@@ -7,29 +7,45 @@ import { SerializedError } from 'vs/base/common/errors';
import Severity from 'vs/base/common/severity';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IExtHostContext, MainContext, MainThreadExtensionServiceShape } from 'vs/workbench/api/node/extHost.protocol';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionService, ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { localize } from 'vs/nls';
import { Action } from 'vs/base/common/actions';
import { EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IExtensionsWorkbenchService, IExtension } from 'vs/workbench/contrib/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadExtensionService)
export class MainThreadExtensionService implements MainThreadExtensionServiceShape {
private readonly _extensionService: ExtensionService;
private readonly _notificationService: INotificationService;
private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService;
private readonly _windowService: IWindowService;
constructor(
extHostContext: IExtHostContext,
@IExtensionService extensionService: IExtensionService
@IExtensionService extensionService: IExtensionService,
@INotificationService notificationService: INotificationService,
@IExtensionsWorkbenchService extensionsWorkbenchService: IExtensionsWorkbenchService,
@IWindowService windowService: IWindowService
) {
if (extensionService instanceof ExtensionService) {
this._extensionService = extensionService;
}
this._notificationService = notificationService;
this._extensionsWorkbenchService = extensionsWorkbenchService;
this._windowService = windowService;
}
public dispose(): void {
}
$localShowMessage(severity: Severity, msg: string): void {
this._extensionService._logOrShowMessage(severity, msg);
$activateExtension(extensionId: ExtensionIdentifier, activationEvent: string): Promise<void> {
return this._extensionService._activateById(extensionId, activationEvent);
}
$onWillActivateExtension(extensionId: ExtensionIdentifier): void {
this._extensionService._onWillActivateExtension(extensionId);
@@ -46,9 +62,66 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
console.error(`[${extensionId}]${error.message}`);
console.error(error.stack);
}
$onExtensionActivationFailed(extensionId: ExtensionIdentifier): void {
async $onExtensionActivationError(extensionId: ExtensionIdentifier, activationError: ExtensionActivationError): Promise<void> {
if (typeof activationError === 'string') {
this._extensionService._logOrShowMessage(Severity.Error, activationError);
} else {
this._handleMissingDependency(extensionId, activationError.dependency);
}
}
$addMessage(extensionId: ExtensionIdentifier, severity: Severity, message: string): void {
this._extensionService._addMessage(extensionId, severity, message);
private async _handleMissingDependency(extensionId: ExtensionIdentifier, missingDependency: string): Promise<void> {
const extension = await this._extensionService.getExtension(extensionId.value);
if (extension) {
const local = await this._extensionsWorkbenchService.queryLocal();
const installedDependency = local.filter(i => areSameExtensions(i.identifier, { id: missingDependency }))[0];
if (installedDependency) {
await this._handleMissingInstalledDependency(extension, installedDependency);
} else {
await this._handleMissingNotInstalledDependency(extension, missingDependency);
}
}
}
private async _handleMissingInstalledDependency(extension: IExtensionDescription, missingInstalledDependency: IExtension): Promise<void> {
const extName = extension.displayName || extension.name;
if (missingInstalledDependency.enablementState === EnablementState.Enabled || missingInstalledDependency.enablementState === EnablementState.WorkspaceEnabled) {
this._notificationService.notify({
severity: Severity.Error,
message: localize('reload window', "Cannot activate extension '{0}' because it depends on extension '{1}', which is not loaded. Would you like to reload the window to load the extension?", extName, missingInstalledDependency.displayName),
actions: {
primary: [new Action('reload', localize('reload', "Reload Window"), '', true, () => this._windowService.reloadWindow())]
}
});
} else {
this._notificationService.notify({
severity: Severity.Error,
message: localize('disabledDep', "Cannot activate extension '{0}' because it depends on extension '{1}', which is disabled. Would you like to enable the extension and reload the window?", extName, missingInstalledDependency.displayName),
actions: {
primary: [new Action('enable', localize('enable dep', "Enable and Reload"), '', true,
() => this._extensionsWorkbenchService.setEnablement([missingInstalledDependency], missingInstalledDependency.enablementState === EnablementState.Disabled ? EnablementState.Enabled : EnablementState.WorkspaceEnabled)
.then(() => this._windowService.reloadWindow(), e => this._notificationService.error(e)))]
}
});
}
}
private async _handleMissingNotInstalledDependency(extension: IExtensionDescription, missingDependency: string): Promise<void> {
const extName = extension.displayName || extension.name;
const dependencyExtension = (await this._extensionsWorkbenchService.queryGallery({ names: [missingDependency] })).firstPage[0];
if (dependencyExtension) {
this._notificationService.notify({
severity: Severity.Error,
message: localize('uninstalledDep', "Cannot activate extension '{0}' because it depends on extension '{1}', which is not installed. Would you like to install the extension and reload the window?", extName, dependencyExtension.displayName),
actions: {
primary: [new Action('install', localize('install missing dep', "Install and Reload"), '', true,
() => this._extensionsWorkbenchService.install(dependencyExtension)
.then(() => this._windowService.reloadWindow(), e => this._notificationService.error(e)))]
}
});
} else {
this._notificationService.error(localize('unknownDep', "Cannot activate extension '{0}' because it depends on an unknown extension '{1}'.", extName, missingDependency));
}
}
}

View File

@@ -16,6 +16,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
private readonly _proxy: ExtHostFileSystemShape;
private readonly _fileProvider = new Map<number, RemoteFileSystemProvider>();
private readonly _resourceLabelFormatters = new Map<number, IDisposable>();
constructor(
extHostContext: IExtHostContext,
@@ -39,12 +40,24 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
this._fileProvider.delete(handle);
}
$setUriFormatter(formatter: ResourceLabelFormatter): void {
this._labelService.registerFormatter(formatter);
$registerResourceLabelFormatter(handle: number, formatter: ResourceLabelFormatter): void {
// Dynamicily registered formatters should have priority over those contributed via package.json
formatter.priority = true;
const disposable = this._labelService.registerFormatter(formatter);
this._resourceLabelFormatters.set(handle, disposable);
}
$unregisterResourceLabelFormatter(handle: number): void {
dispose(this._resourceLabelFormatters.get(handle));
this._resourceLabelFormatters.delete(handle);
}
$onFileSystemChange(handle: number, changes: IFileChangeDto[]): void {
this._fileProvider.get(handle).$onFileSystemChange(changes);
const fileProvider = this._fileProvider.get(handle);
if (!fileProvider) {
throw new Error('Unknown file provider');
}
fileProvider.$onFileSystemChange(changes);
}
}

View File

@@ -52,12 +52,12 @@ export class MainThreadFileSystemEventService {
// file operation events - (changes the editor makes)
fileService.onAfterOperation(e => {
if (e.operation === FileOperation.MOVE) {
proxy.$onFileRename(e.resource, e.target.resource);
proxy.$onFileRename(e.resource, e.target!.resource);
}
}, undefined, this._listener);
textfileService.onWillMove(e => {
let promise = proxy.$onWillRename(e.oldResource, e.newResource);
const promise = proxy.$onWillRename(e.oldResource, e.newResource);
e.waitUntil(promise);
}, undefined, this._listener);
}

View File

@@ -9,8 +9,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { isThenable } from 'vs/base/common/async';
import { isNullOrUndefined } from 'util';
import { GCSignal } from 'gc-signals';
export const IHeapService = createDecorator<IHeapService>('heapService');
@@ -20,10 +19,9 @@ export interface IHeapService {
readonly onGarbageCollection: Event<number[]>;
/**
* Track gc-collection for all new objects that
* have the $ident-value set.
* Track gc-collection for the given object
*/
trackRecursive<T>(obj: T | Promise<T>): Promise<T>;
trackObject(obj: ObjectIdentifier | undefined): void;
}
export class HeapService implements IHeapService {
@@ -35,7 +33,10 @@ export class HeapService implements IHeapService {
private _activeSignals = new WeakMap<any, object>();
private _activeIds = new Set<number>();
private _consumeHandle: any;
private _ctor: { new(id: number): GCSignal };
private _ctorInit: Promise<void>;
constructor() {
//
@@ -45,80 +46,57 @@ export class HeapService implements IHeapService {
clearInterval(this._consumeHandle);
}
trackRecursive<T>(obj: T | Promise<T>): Promise<T> {
if (isThenable(obj)) {
return obj.then(result => this.trackRecursive(result));
trackObject(obj: ObjectIdentifier | undefined | null): void {
if (!obj) {
return;
}
const ident = obj.$ident;
if (typeof ident !== 'number') {
return;
}
if (this._activeIds.has(ident)) {
return;
}
if (this._ctor) {
// track and leave
this._activeIds.add(ident);
this._activeSignals.set(obj, new this._ctor(ident));
} else {
return this._doTrackRecursive(obj);
}
}
// make sure to load gc-signals, then track and leave
if (!this._ctorInit) {
this._ctorInit = import('gc-signals').then(({ GCSignal, consumeSignals }) => {
this._ctor = GCSignal;
this._consumeHandle = setInterval(() => {
const ids = consumeSignals();
private _doTrackRecursive(obj: any): Promise<any> {
if (isNullOrUndefined(obj)) {
return Promise.resolve(obj);
}
return import('gc-signals').then(({ GCSignal, consumeSignals }) => {
if (this._consumeHandle === undefined) {
// ensure that there is one consumer of signals
this._consumeHandle = setInterval(() => {
const ids = consumeSignals();
if (ids.length > 0) {
// local book-keeping
for (const id of ids) {
this._activeIds.delete(id);
if (ids.length > 0) {
// local book-keeping
for (const id of ids) {
this._activeIds.delete(id);
}
// fire event
this._onGarbageCollection.fire(ids);
}
// fire event
this._onGarbageCollection.fire(ids);
}
}, 15 * 1000);
}, 15 * 1000);
});
}
const stack = [obj];
while (stack.length > 0) {
// remove first element
let obj = stack.shift();
if (!obj || typeof obj !== 'object') {
continue;
}
for (let key in obj) {
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
continue;
}
const value = obj[key];
// recurse -> object/array
if (typeof value === 'object') {
stack.push(value);
} else if (key === ObjectIdentifier.name) {
// track new $ident-objects
if (typeof value === 'number' && !this._activeIds.has(value)) {
this._activeIds.add(value);
this._activeSignals.set(obj, new GCSignal(value));
}
}
}
}
return obj;
});
this._ctorInit.then(() => {
this._activeIds.add(ident);
this._activeSignals.set(obj, new this._ctor(ident));
});
}
}
}
@extHostCustomer
export class MainThreadHeapService {
private _toDispose: IDisposable;
private readonly _toDispose: IDisposable;
constructor(
extHostContext: IExtHostContext,

View File

@@ -7,11 +7,11 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { ITextModel, ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import * as search from 'vs/workbench/parts/search/common/search';
import * as search from 'vs/workbench/contrib/search/common/search';
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, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata } from '../node/extHost.protocol';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, CodeInsetDto, LinkDto } 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';
@@ -20,14 +20,16 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { URI } from 'vs/base/common/uri';
import { Selection } from 'vs/editor/common/core/selection';
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesShape {
private _proxy: ExtHostLanguageFeaturesShape;
private _heapService: IHeapService;
private _modeService: IModeService;
private _registrations: { [handle: number]: IDisposable; } = Object.create(null);
private readonly _proxy: ExtHostLanguageFeaturesShape;
private readonly _heapService: IHeapService;
private readonly _modeService: IModeService;
private readonly _registrations: { [handle: number]: IDisposable; } = Object.create(null);
constructor(
extHostContext: IExtHostContext,
@@ -46,7 +48,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
$unregister(handle: number): void {
let registration = this._registrations[handle];
const registration = this._registrations[handle];
if (registration) {
registration.dispose();
delete this._registrations[handle];
@@ -85,9 +87,10 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
private static _reviveWorkspaceSymbolDto(data: WorkspaceSymbolDto): search.IWorkspaceSymbol;
private static _reviveWorkspaceSymbolDto(data: WorkspaceSymbolDto[]): search.IWorkspaceSymbol[];
private static _reviveWorkspaceSymbolDto(data: WorkspaceSymbolDto | WorkspaceSymbolDto[]): search.IWorkspaceSymbol | search.IWorkspaceSymbol[] {
private static _reviveWorkspaceSymbolDto(data: undefined): undefined;
private static _reviveWorkspaceSymbolDto(data: WorkspaceSymbolDto | WorkspaceSymbolDto[] | undefined): search.IWorkspaceSymbol | search.IWorkspaceSymbol[] | undefined {
if (!data) {
return <search.IWorkspaceSymbol>data;
return <undefined>data;
} else if (Array.isArray(data)) {
data.forEach(MainThreadLanguageFeatures._reviveWorkspaceSymbolDto);
return <search.IWorkspaceSymbol[]>data;
@@ -97,13 +100,20 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
private static _reviveCodeActionDto(data: CodeActionDto[]): modes.CodeAction[] {
private static _reviveCodeActionDto(data: CodeActionDto[] | undefined): modes.CodeAction[] {
if (data) {
data.forEach(code => reviveWorkspaceEditDto(code.edit));
}
return <modes.CodeAction[]>data;
}
private static _reviveLinkDTO(data: LinkDto): modes.ILink {
if (data.url && typeof data.url !== 'string') {
data.url = URI.revive(data.url);
}
return <modes.ILink>data;
}
//#endregion
// --- outline
@@ -111,7 +121,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerDocumentSymbolProvider(handle: number, selector: ISerializedDocumentFilter[], displayName: string): void {
this._registrations[handle] = modes.DocumentSymbolProviderRegistry.register(typeConverters.LanguageSelector.from(selector), <modes.DocumentSymbolProvider>{
displayName,
provideDocumentSymbols: (model: ITextModel, token: CancellationToken): Promise<modes.DocumentSymbol[]> => {
provideDocumentSymbols: (model: ITextModel, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined> => {
return this._proxy.$provideDocumentSymbols(handle, model.uri, token);
}
});
@@ -119,14 +129,28 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- code lens
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void {
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void {
const provider = <modes.CodeLensProvider>{
provideCodeLenses: (model: ITextModel, token: CancellationToken): modes.ICodeLensSymbol[] | Promise<modes.ICodeLensSymbol[]> => {
return this._heapService.trackRecursive(this._proxy.$provideCodeLenses(handle, model.uri, token));
return this._proxy.$provideCodeLenses(handle, model.uri, token).then(dto => {
if (dto) {
dto.forEach(obj => {
this._heapService.trackObject(obj);
this._heapService.trackObject(obj.command);
});
}
return dto;
});
},
resolveCodeLens: (model: ITextModel, codeLens: modes.ICodeLensSymbol, token: CancellationToken): modes.ICodeLensSymbol | Promise<modes.ICodeLensSymbol> => {
return this._heapService.trackRecursive(this._proxy.$resolveCodeLens(handle, model.uri, codeLens, token));
resolveCodeLens: (model: ITextModel, codeLens: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol | undefined> => {
return this._proxy.$resolveCodeLens(handle, model.uri, codeLens, token).then(obj => {
if (obj) {
this._heapService.trackObject(obj);
this._heapService.trackObject(obj.command);
}
return obj;
});
}
};
@@ -146,6 +170,35 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
// -- code inset
$registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void {
const provider = <codeInset.CodeInsetProvider>{
provideCodeInsets: (model: ITextModel, token: CancellationToken): CodeInsetDto[] | Thenable<CodeInsetDto[]> => {
return this._proxy.$provideCodeInsets(handle, model.uri, token).then(dto => {
if (dto) { dto.forEach(obj => this._heapService.trackObject(obj)); }
return dto;
});
},
resolveCodeInset: (model: ITextModel, codeInset: CodeInsetDto, token: CancellationToken): CodeInsetDto | Thenable<CodeInsetDto> => {
return this._proxy.$resolveCodeInset(handle, model.uri, codeInset, token).then(obj => {
this._heapService.trackObject(obj);
return obj;
});
}
};
if (typeof eventHandle === 'number') {
const emitter = new Emitter<codeInset.CodeInsetProvider>();
this._registrations[eventHandle] = emitter;
provider.onDidChange = emitter.event;
}
const langSelector = typeConverters.LanguageSelector.from(selector);
this._registrations[handle] = codeInset.CodeInsetProviderRegistry.register(langSelector, provider);
}
// --- declaration
$registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void {
@@ -184,7 +237,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerHoverProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.HoverProviderRegistry.register(typeConverters.LanguageSelector.from(selector), <modes.HoverProvider>{
provideHover: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.Hover> => {
provideHover: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.Hover | undefined> => {
return this._proxy.$provideHover(handle, model.uri, position, token);
}
});
@@ -194,7 +247,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerDocumentHighlightProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.DocumentHighlightProviderRegistry.register(typeConverters.LanguageSelector.from(selector), <modes.DocumentHighlightProvider>{
provideDocumentHighlights: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.DocumentHighlight[]> => {
provideDocumentHighlights: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.DocumentHighlight[] | undefined> => {
return this._proxy.$provideDocumentHighlights(handle, model.uri, position, token);
}
});
@@ -215,7 +268,12 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerQuickFixSupport(handle: number, selector: ISerializedDocumentFilter[], providedCodeActionKinds?: string[]): void {
this._registrations[handle] = modes.CodeActionProviderRegistry.register(typeConverters.LanguageSelector.from(selector), <modes.CodeActionProvider>{
provideCodeActions: (model: ITextModel, rangeOrSelection: EditorRange | Selection, context: modes.CodeActionContext, token: CancellationToken): Promise<modes.CodeAction[]> => {
return this._heapService.trackRecursive(this._proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, context, token)).then(MainThreadLanguageFeatures._reviveCodeActionDto);
return this._proxy.$provideCodeActions(handle, model.uri, rangeOrSelection, context, token).then(dto => {
if (dto) {
dto.forEach(obj => { this._heapService.trackObject(obj.command); });
}
return MainThreadLanguageFeatures._reviveCodeActionDto(dto);
});
},
providedCodeActionKinds
});
@@ -223,30 +281,29 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- formatting
$registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], displayName: string): void {
$registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier): void {
this._registrations[handle] = modes.DocumentFormattingEditProviderRegistry.register(typeConverters.LanguageSelector.from(selector), <modes.DocumentFormattingEditProvider>{
displayName,
provideDocumentFormattingEdits: (model: ITextModel, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]> => {
extensionId,
provideDocumentFormattingEdits: (model: ITextModel, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined> => {
return this._proxy.$provideDocumentFormattingEdits(handle, model.uri, options, token);
}
});
}
$registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], displayName: string): void {
$registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier): void {
this._registrations[handle] = modes.DocumentRangeFormattingEditProviderRegistry.register(typeConverters.LanguageSelector.from(selector), <modes.DocumentRangeFormattingEditProvider>{
displayName,
provideDocumentRangeFormattingEdits: (model: ITextModel, range: EditorRange, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]> => {
extensionId,
provideDocumentRangeFormattingEdits: (model: ITextModel, range: EditorRange, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined> => {
return this._proxy.$provideDocumentRangeFormattingEdits(handle, model.uri, range, options, token);
}
});
}
$registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[]): void {
$registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void {
this._registrations[handle] = modes.OnTypeFormattingEditProviderRegistry.register(typeConverters.LanguageSelector.from(selector), <modes.OnTypeFormattingEditProvider>{
extensionId,
autoFormatTriggerCharacters,
provideOnTypeFormattingEdits: (model: ITextModel, position: EditorPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]> => {
provideOnTypeFormattingEdits: (model: ITextModel, position: EditorPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined> => {
return this._proxy.$provideOnTypeFormattingEdits(handle, model.uri, position, ch, options, token);
}
});
@@ -255,7 +312,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- navigate type
$registerNavigateTypeSupport(handle: number): void {
let lastResultId: number;
let lastResultId: number | undefined;
this._registrations[handle] = search.WorkspaceSymbolProviderRegistry.register(<search.IWorkspaceSymbolProvider>{
provideWorkspaceSymbols: (search: string, token: CancellationToken): Promise<search.IWorkspaceSymbol[]> => {
return this._proxy.$provideWorkspaceSymbols(handle, search, token).then(result => {
@@ -266,8 +323,13 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return MainThreadLanguageFeatures._reviveWorkspaceSymbolDto(result.symbols);
});
},
resolveWorkspaceSymbol: (item: search.IWorkspaceSymbol, token: CancellationToken): Promise<search.IWorkspaceSymbol> => {
return this._proxy.$resolveWorkspaceSymbol(handle, item, token).then(i => MainThreadLanguageFeatures._reviveWorkspaceSymbolDto(i));
resolveWorkspaceSymbol: (item: search.IWorkspaceSymbol, token: CancellationToken): Promise<search.IWorkspaceSymbol | undefined> => {
return this._proxy.$resolveWorkspaceSymbol(handle, item, token).then(i => {
if (i) {
return MainThreadLanguageFeatures._reviveWorkspaceSymbolDto(i);
}
return undefined;
});
}
});
}
@@ -281,7 +343,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return this._proxy.$provideRenameEdits(handle, model.uri, position, newName, token).then(reviveWorkspaceEditDto);
},
resolveRenameLocation: supportResolveLocation
? (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.RenameLocation> => this._proxy.$resolveRenameLocation(handle, model.uri, position, token)
? (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.RenameLocation | undefined> => this._proxy.$resolveRenameLocation(handle, model.uri, position, token)
: undefined
});
}
@@ -291,7 +353,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean): void {
this._registrations[handle] = modes.CompletionProviderRegistry.register(typeConverters.LanguageSelector.from(selector), <modes.CompletionItemProvider>{
triggerCharacters,
provideCompletionItems: (model: ITextModel, position: EditorPosition, context: modes.CompletionContext, token: CancellationToken): Promise<modes.CompletionList> => {
provideCompletionItems: (model: ITextModel, position: EditorPosition, context: modes.CompletionContext, token: CancellationToken): Promise<modes.CompletionList | undefined> => {
return this._proxy.$provideCompletionItems(handle, model.uri, position, context, token).then(result => {
if (!result) {
return result;
@@ -299,7 +361,11 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return {
suggestions: result.suggestions,
incomplete: result.incomplete,
dispose: () => this._proxy.$releaseCompletionItems(handle, result._id)
dispose: () => {
if (typeof result._id === 'number') {
this._proxy.$releaseCompletionItems(handle, result._id);
}
}
};
});
},
@@ -317,7 +383,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
signatureHelpTriggerCharacters: metadata.triggerCharacters,
signatureHelpRetriggerCharacters: metadata.retriggerCharacters,
provideSignatureHelp: (model: ITextModel, position: EditorPosition, token: CancellationToken, context: modes.SignatureHelpContext): Promise<modes.SignatureHelp> => {
provideSignatureHelp: (model: ITextModel, position: EditorPosition, token: CancellationToken, context: modes.SignatureHelpContext): Promise<modes.SignatureHelp | undefined> => {
return this._proxy.$provideSignatureHelp(handle, model.uri, position, context, token);
}
});
@@ -328,10 +394,24 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.LinkProviderRegistry.register(typeConverters.LanguageSelector.from(selector), <modes.LinkProvider>{
provideLinks: (model, token) => {
return this._heapService.trackRecursive(this._proxy.$provideDocumentLinks(handle, model.uri, token));
return this._proxy.$provideDocumentLinks(handle, model.uri, token).then(dto => {
if (dto) {
dto.forEach(obj => {
MainThreadLanguageFeatures._reviveLinkDTO(obj);
this._heapService.trackObject(obj);
});
}
return dto;
});
},
resolveLink: (link, token) => {
return this._proxy.$resolveDocumentLink(handle, link, token);
return this._proxy.$resolveDocumentLink(handle, link, token).then(obj => {
if (obj) {
MainThreadLanguageFeatures._reviveLinkDTO(obj);
this._heapService.trackObject(obj);
}
return obj;
});
}
});
}
@@ -385,8 +465,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerSelectionRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void {
this._registrations[handle] = modes.SelectionRangeRegistry.register(typeConverters.LanguageSelector.from(selector), {
provideSelectionRanges: (model, position, token) => {
return this._proxy.$provideSelectionRanges(handle, model.uri, position, token);
provideSelectionRanges: (model, positions, token) => {
return this._proxy.$provideSelectionRanges(handle, model.uri, positions, token);
}
});
}
@@ -394,61 +474,43 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
// --- configuration
private static _reviveRegExp(regExp: ISerializedRegExp): RegExp {
if (typeof regExp === 'undefined') {
return undefined;
}
if (regExp === null) {
return null;
}
return new RegExp(regExp.pattern, regExp.flags);
}
private static _reviveIndentationRule(indentationRule: ISerializedIndentationRule): IndentationRule {
if (typeof indentationRule === 'undefined') {
return undefined;
}
if (indentationRule === null) {
return null;
}
return {
decreaseIndentPattern: MainThreadLanguageFeatures._reviveRegExp(indentationRule.decreaseIndentPattern),
increaseIndentPattern: MainThreadLanguageFeatures._reviveRegExp(indentationRule.increaseIndentPattern),
indentNextLinePattern: MainThreadLanguageFeatures._reviveRegExp(indentationRule.indentNextLinePattern),
unIndentedLinePattern: MainThreadLanguageFeatures._reviveRegExp(indentationRule.unIndentedLinePattern),
indentNextLinePattern: indentationRule.indentNextLinePattern ? MainThreadLanguageFeatures._reviveRegExp(indentationRule.indentNextLinePattern) : undefined,
unIndentedLinePattern: indentationRule.unIndentedLinePattern ? MainThreadLanguageFeatures._reviveRegExp(indentationRule.unIndentedLinePattern) : undefined,
};
}
private static _reviveOnEnterRule(onEnterRule: ISerializedOnEnterRule): OnEnterRule {
return {
beforeText: MainThreadLanguageFeatures._reviveRegExp(onEnterRule.beforeText),
afterText: MainThreadLanguageFeatures._reviveRegExp(onEnterRule.afterText),
oneLineAboveText: MainThreadLanguageFeatures._reviveRegExp(onEnterRule.oneLineAboveText),
afterText: onEnterRule.afterText ? MainThreadLanguageFeatures._reviveRegExp(onEnterRule.afterText) : undefined,
oneLineAboveText: onEnterRule.oneLineAboveText ? MainThreadLanguageFeatures._reviveRegExp(onEnterRule.oneLineAboveText) : undefined,
action: onEnterRule.action
};
}
private static _reviveOnEnterRules(onEnterRules: ISerializedOnEnterRule[]): OnEnterRule[] {
if (typeof onEnterRules === 'undefined') {
return undefined;
}
if (onEnterRules === null) {
return null;
}
return onEnterRules.map(MainThreadLanguageFeatures._reviveOnEnterRule);
}
$setLanguageConfiguration(handle: number, languageId: string, _configuration: ISerializedLanguageConfiguration): void {
let configuration: LanguageConfiguration = {
const configuration: LanguageConfiguration = {
comments: _configuration.comments,
brackets: _configuration.brackets,
wordPattern: MainThreadLanguageFeatures._reviveRegExp(_configuration.wordPattern),
indentationRules: MainThreadLanguageFeatures._reviveIndentationRule(_configuration.indentationRules),
onEnterRules: MainThreadLanguageFeatures._reviveOnEnterRules(_configuration.onEnterRules),
wordPattern: _configuration.wordPattern ? MainThreadLanguageFeatures._reviveRegExp(_configuration.wordPattern) : undefined,
indentationRules: _configuration.indentationRules ? MainThreadLanguageFeatures._reviveIndentationRule(_configuration.indentationRules) : undefined,
onEnterRules: _configuration.onEnterRules ? MainThreadLanguageFeatures._reviveOnEnterRules(_configuration.onEnterRules) : undefined,
autoClosingPairs: null,
surroundingPairs: null,
__electricCharacterSupport: null
autoClosingPairs: undefined,
surroundingPairs: undefined,
__electricCharacterSupport: undefined
};
if (_configuration.__characterPairSupport) {
@@ -465,7 +527,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
};
}
let languageIdentifier = this._modeService.getLanguageIdentifier(languageId);
const languageIdentifier = this._modeService.getLanguageIdentifier(languageId);
if (languageIdentifier) {
this._registrations[handle] = LanguageConfigurationRegistry.register(languageIdentifier, configuration);
}

View File

@@ -29,7 +29,7 @@ export class MainThreadLanguages implements MainThreadLanguagesShape {
$changeLanguage(resource: UriComponents, languageId: string): Promise<void> {
const uri = URI.revive(resource);
let model = this._modelService.getModel(uri);
const model = this._modelService.getModel(uri);
if (!model) {
return Promise.reject(new Error('Invalid uri'));
}

View File

@@ -8,13 +8,12 @@ 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 { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Event } from 'vs/base/common/event';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { dispose } from 'vs/base/common/lifecycle';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadMessageService)
export class MainThreadMessageService implements MainThreadMessageServiceShape {
@@ -32,7 +31,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
//
}
$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Promise<number> {
$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Promise<number | undefined> {
if (options.modal) {
return this._showModalMessage(severity, message, commands);
} else {
@@ -40,17 +39,17 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
}
}
private _showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[], extension: IExtensionDescription): Promise<number> {
private _showMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[], extension: IExtensionDescription | undefined): Promise<number> {
return new Promise<number>(resolve => {
let primaryActions: MessageItemAction[] = [];
const primaryActions: MessageItemAction[] = [];
class MessageItemAction extends Action {
constructor(id: string, label: string, handle: number) {
super(id, label, undefined, true, () => {
resolve(handle);
return undefined;
return Promise.resolve();
});
}
}
@@ -67,7 +66,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
primaryActions.push(new MessageItemAction('_extension_message_handle_' + command.handle, command.title, command.handle));
});
let source: string;
let source: string | undefined;
if (extension) {
source = nls.localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
}
@@ -97,7 +96,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
});
}
private _showModalMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Promise<number> {
private _showModalMessage(severity: Severity, message: string, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Promise<number | undefined> {
let cancelId: number | undefined = undefined;
const buttons = commands.map((command, index) => {

View File

@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { Registry } from 'vs/platform/registry/common/platform';
import { IOutputService, IOutputChannel, OUTPUT_PANEL_ID, Extensions, IOutputChannelRegistry } from 'vs/workbench/parts/output/common/output';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IOutputService, IOutputChannel, OUTPUT_PANEL_ID, Extensions, IOutputChannelRegistry } from 'vs/workbench/contrib/output/common/output';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { MainThreadOutputServiceShape, MainContext, IExtHostContext, ExtHostOutputServiceShape, ExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@@ -18,27 +18,27 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut
private static _idPool = 1;
private _proxy: ExtHostOutputServiceShape;
private readonly _proxy: ExtHostOutputServiceShape;
private readonly _outputService: IOutputService;
private readonly _partService: IPartService;
private readonly _layoutService: IWorkbenchLayoutService;
private readonly _panelService: IPanelService;
constructor(
extHostContext: IExtHostContext,
@IOutputService outputService: IOutputService,
@IPartService partService: IPartService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IPanelService panelService: IPanelService
) {
super();
this._outputService = outputService;
this._partService = partService;
this._layoutService = layoutService;
this._panelService = panelService;
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostOutputService);
const setVisibleChannel = () => {
const panel = this._panelService.getActivePanel();
const visibleChannel: IOutputChannel = panel && panel.getId() === OUTPUT_PANEL_ID ? this._outputService.getActiveChannel() : null;
const visibleChannel: IOutputChannel | null = panel && panel.getId() === OUTPUT_PANEL_ID ? this._outputService.getActiveChannel() : null;
this._proxy.$setVisibleChannel(visibleChannel ? visibleChannel.id : null);
};
this._register(Event.any<any>(this._outputService.onActiveOutputChannel, this._panelService.onDidPanelOpen, this._panelService.onDidPanelClose)(() => setVisibleChannel()));
@@ -47,12 +47,12 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut
public $register(label: string, log: boolean, file?: UriComponents): Promise<string> {
const id = 'extension-output-#' + (MainThreadOutputService._idPool++);
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id, label, file: file ? URI.revive(file) : null, log });
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id, label, file: file ? URI.revive(file) : undefined, log });
this._register(toDisposable(() => this.$dispose(id)));
return Promise.resolve(id);
}
public $append(channelId: string, value: string): Promise<void> {
public $append(channelId: string, value: string): Promise<void> | undefined {
const channel = this._getChannel(channelId);
if (channel) {
channel.append(value);
@@ -60,7 +60,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut
return undefined;
}
public $update(channelId: string): Promise<void> {
public $update(channelId: string): Promise<void> | undefined {
const channel = this._getChannel(channelId);
if (channel) {
channel.update();
@@ -68,7 +68,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut
return undefined;
}
public $clear(channelId: string, till: number): Promise<void> {
public $clear(channelId: string, till: number): Promise<void> | undefined {
const channel = this._getChannel(channelId);
if (channel) {
channel.clear(till);
@@ -76,7 +76,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut
return undefined;
}
public $reveal(channelId: string, preserveFocus: boolean): Promise<void> {
public $reveal(channelId: string, preserveFocus: boolean): Promise<void> | undefined {
const channel = this._getChannel(channelId);
if (channel) {
this._outputService.showChannel(channel.id, preserveFocus);
@@ -84,16 +84,19 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut
return undefined;
}
public $close(channelId: string): Promise<void> {
public $close(channelId: string): Promise<void> | undefined {
const panel = this._panelService.getActivePanel();
if (panel && panel.getId() === OUTPUT_PANEL_ID && channelId === this._outputService.getActiveChannel().id) {
this._partService.setPanelHidden(true);
if (panel && panel.getId() === OUTPUT_PANEL_ID) {
const activeChannel = this._outputService.getActiveChannel();
if (activeChannel && channelId === activeChannel.id) {
this._layoutService.setPanelHidden(true);
}
}
return undefined;
}
public $dispose(channelId: string): Promise<void> {
public $dispose(channelId: string): Promise<void> | undefined {
const channel = this._getChannel(channelId);
if (channel) {
channel.dispose();
@@ -101,7 +104,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut
return undefined;
}
private _getChannel(channelId: string): IOutputChannel {
private _getChannel(channelId: string): IOutputChannel | null {
return this._outputService.getChannel(channelId);
}
}

View File

@@ -10,9 +10,9 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC
@extHostNamedCustomer(MainContext.MainThreadProgress)
export class MainThreadProgress implements MainThreadProgressShape {
private _progressService: IProgressService2;
private readonly _progressService: IProgressService2;
private _progress = new Map<number, { resolve: () => void, progress: IProgress<IProgressStep> }>();
private _proxy: ExtHostProgressShape;
private readonly _proxy: ExtHostProgressShape;
constructor(
extHostContext: IExtHostContext,
@@ -34,14 +34,16 @@ export class MainThreadProgress implements MainThreadProgressShape {
}
$progressReport(handle: number, message: IProgressStep): void {
if (this._progress.has(handle)) {
this._progress.get(handle).progress.report(message);
const entry = this._progress.get(handle);
if (entry) {
entry.progress.report(message);
}
}
$progressEnd(handle: number): void {
if (this._progress.has(handle)) {
this._progress.get(handle).resolve();
const entry = this._progress.get(handle);
if (entry) {
entry.resolve();
this._progress.delete(handle);
}
}

View File

@@ -18,9 +18,9 @@ interface QuickInputSession {
@extHostNamedCustomer(MainContext.MainThreadQuickOpen)
export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
private _proxy: ExtHostQuickOpenShape;
private _quickInputService: IQuickInputService;
private _items: Record<number, {
private readonly _proxy: ExtHostQuickOpenShape;
private readonly _quickInputService: IQuickInputService;
private readonly _items: Record<number, {
resolve(items: TransferQuickPickItems[]): void;
reject(error: Error): void;
}> = {};
@@ -36,7 +36,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
public dispose(): void {
}
$show(instance: number, options: IPickOptions<TransferQuickPickItems>, token: CancellationToken): Promise<number | number[]> {
$show(instance: number, options: IPickOptions<TransferQuickPickItems>, token: CancellationToken): Promise<number | number[] | undefined> {
const contents = new Promise<TransferQuickPickItems[]>((resolve, reject) => {
this._items[instance] = { resolve, reject };
});
@@ -72,7 +72,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
this._items[instance].resolve(items);
delete this._items[instance];
}
return undefined;
return Promise.resolve();
}
$setError(instance: number, error: Error): Promise<void> {
@@ -80,12 +80,12 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
this._items[instance].reject(error);
delete this._items[instance];
}
return undefined;
return Promise.resolve();
}
// ---- input
$input(options: InputBoxOptions, validateInput: boolean, token: CancellationToken): Promise<string> {
$input(options: InputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise<string> {
const inputOptions: IInputOptions = Object.create(null);
if (options) {
@@ -181,13 +181,13 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
.filter(handle => handlesToItems.has(handle))
.map(handle => handlesToItems.get(handle));
} else if (param === 'buttons') {
input[param] = params.buttons.map(button => {
input[param] = params.buttons!.map(button => {
if (button.handle === -1) {
return this._quickInputService.backButton;
}
const { iconPath, tooltip, handle } = button;
return {
iconPath: {
iconPath: iconPath && {
dark: URI.revive(iconPath.dark),
light: iconPath.light && URI.revive(iconPath.light)
},

View File

@@ -7,7 +7,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { assign } from 'vs/base/common/objects';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation } from 'vs/workbench/services/scm/common/scm';
import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation } from 'vs/workbench/contrib/scm/common/scm';
import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { Command } from 'vs/editor/common/modes';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
@@ -21,14 +21,14 @@ class MainThreadSCMResourceGroup implements ISCMResourceGroup {
private _onDidSplice = new Emitter<ISplice<ISCMResource>>();
readonly onDidSplice = this._onDidSplice.event;
get hideWhenEmpty(): boolean { return this.features.hideWhenEmpty; }
get hideWhenEmpty(): boolean { return !!this.features.hideWhenEmpty; }
private _onDidChange = new Emitter<void>();
get onDidChange(): Event<void> { return this._onDidChange.event; }
constructor(
private sourceControlHandle: number,
private handle: number,
private readonly sourceControlHandle: number,
private readonly handle: number,
public provider: ISCMProvider,
public features: SCMGroupFeatures,
public label: string,
@@ -62,10 +62,10 @@ class MainThreadSCMResourceGroup implements ISCMResourceGroup {
class MainThreadSCMResource implements ISCMResource {
constructor(
private proxy: ExtHostSCMShape,
private sourceControlHandle: number,
private groupHandle: number,
private handle: number,
private readonly proxy: ExtHostSCMShape,
private readonly sourceControlHandle: number,
private readonly groupHandle: number,
private readonly handle: number,
public sourceUri: URI,
public resourceGroup: ISCMResourceGroup,
public decorations: ISCMResourceDecorations
@@ -92,7 +92,7 @@ class MainThreadSCMProvider implements ISCMProvider {
get id(): string { return this._id; }
readonly groups = new Sequence<MainThreadSCMResourceGroup>();
private _groupsByHandle: { [handle: number]: MainThreadSCMResourceGroup; } = Object.create(null);
private readonly _groupsByHandle: { [handle: number]: MainThreadSCMResourceGroup; } = Object.create(null);
// get groups(): ISequence<ISCMResourceGroup> {
// return {
@@ -129,11 +129,11 @@ class MainThreadSCMProvider implements ISCMProvider {
get onDidChange(): Event<void> { return this._onDidChange.event; }
constructor(
private proxy: ExtHostSCMShape,
private _handle: number,
private _contextValue: string,
private _label: string,
private _rootUri: URI | undefined,
private readonly proxy: ExtHostSCMShape,
private readonly _handle: number,
private readonly _contextValue: string,
private readonly _label: string,
private readonly _rootUri: URI | undefined,
@ISCMService scmService: ISCMService
) { }
@@ -142,11 +142,11 @@ class MainThreadSCMProvider implements ISCMProvider {
this._onDidChange.fire();
if (typeof features.commitTemplate !== 'undefined') {
this._onDidChangeCommitTemplate.fire(this.commitTemplate);
this._onDidChangeCommitTemplate.fire(this.commitTemplate!);
}
if (typeof features.statusBarCommands !== 'undefined') {
this._onDidChangeStatusBarCommands.fire(this.statusBarCommands);
this._onDidChangeStatusBarCommands.fire(this.statusBarCommands!);
}
}
@@ -202,14 +202,14 @@ class MainThreadSCMProvider implements ISCMProvider {
const icon = icons[0];
const iconDark = icons[1] || icon;
const decorations = {
icon: icon && URI.parse(icon),
iconDark: iconDark && URI.parse(iconDark),
icon: icon ? URI.parse(icon) : undefined,
iconDark: iconDark ? URI.parse(iconDark) : undefined,
tooltip,
strikeThrough,
faded,
source,
letter,
color: color && color.id
color: color ? color.id : undefined
};
return new MainThreadSCMResource(
@@ -241,7 +241,7 @@ class MainThreadSCMProvider implements ISCMProvider {
this.groups.splice(this.groups.elements.indexOf(group), 1);
}
async getOriginalResource(uri: URI): Promise<URI> {
async getOriginalResource(uri: URI): Promise<URI | null> {
if (!this.features.hasQuickDiffProvider) {
return null;
}
@@ -265,7 +265,7 @@ class MainThreadSCMProvider implements ISCMProvider {
@extHostNamedCustomer(MainContext.MainThreadSCM)
export class MainThreadSCM implements MainThreadSCMShape {
private _proxy: ExtHostSCMShape;
private readonly _proxy: ExtHostSCMShape;
private _repositories: { [handle: number]: ISCMRepository; } = Object.create(null);
private _inputDisposables: { [handle: number]: IDisposable; } = Object.create(null);
private _disposables: IDisposable[] = [];

View File

@@ -7,7 +7,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays';
import { IdleValue, sequence } from 'vs/base/common/async';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import * as strings from 'vs/base/common/strings';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
@@ -16,14 +16,14 @@ import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { IIdentifiedSingleEditOperation, ISingleEditOperation, ITextModel } from 'vs/editor/common/model';
import { CodeAction } from 'vs/editor/common/modes';
import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
import { CodeAction, TextEdit } from 'vs/editor/common/modes';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
import { getDocumentFormattingEdits, NoProviderError } from 'vs/editor/contrib/format/format';
import { getDocumentFormattingEdits, FormatMode } from 'vs/editor/contrib/format/format';
import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { localize } from 'vs/nls';
@@ -34,8 +34,10 @@ import { ILogService } from 'vs/platform/log/common/log';
import { IProgressService2, ProgressLocation } from 'vs/platform/progress/common/progress';
import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
import { ISaveParticipant, ITextFileEditorModel, SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
// {{SQL CARBON EDIT}}
import { ISaveParticipant, SaveReason, IResolvedTextFileEditorModel, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../node/extHost.protocol';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
// {{SQL CARBON EDIT}}
import { INotebookService } from 'sql/workbench/services/notebook/common/notebookService';
@@ -78,7 +80,7 @@ class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
// Nothing
}
async participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) {
this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO);
}
@@ -88,7 +90,7 @@ class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
let prevSelection: Selection[] = [];
let cursors: Position[] = [];
let editor = findEditor(model, this.codeEditorService);
const editor = findEditor(model, this.codeEditorService);
if (editor) {
// Find `prevSelection` in any case do ensure a good undo stack when pushing the edit
// Collect active cursors in `cursors` only if `isAutoSaved` to avoid having the cursors jump
@@ -113,12 +115,12 @@ class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
}
}
function findEditor(model: ITextModel, codeEditorService: ICodeEditorService): ICodeEditor {
let candidate: ICodeEditor | null = null;
function findEditor(model: ITextModel, codeEditorService: ICodeEditorService): IActiveCodeEditor | null {
let candidate: IActiveCodeEditor | null = null;
if (model.isAttachedToEditor()) {
for (const editor of codeEditorService.listCodeEditors()) {
if (editor.getModel() === model) {
if (editor.hasModel() && editor.getModel() === model) {
if (editor.hasTextFocus()) {
return editor; // favour focused editor if there are multiple
}
@@ -140,7 +142,7 @@ export class FinalNewLineParticipant implements ISaveParticipantParticipant {
// Nothing
}
async participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) {
this.doInsertFinalNewLine(model.textEditorModel);
}
@@ -178,7 +180,7 @@ export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant
// Nothing
}
async participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.getResource() })) {
this.doTrimFinalNewLines(model.textEditorModel, env.reason === SaveReason.AUTO);
}
@@ -243,12 +245,13 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
constructor(
@ICodeEditorService private readonly _editorService: ICodeEditorService,
@IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService,
@IConfigurationService private readonly _configurationService: IConfigurationService
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
) {
// Nothing
}
async participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
const model = editorModel.textEditorModel;
if (env.reason === SaveReason.AUTO
@@ -257,26 +260,19 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
}
const versionNow = model.getVersionId();
const { tabSize, insertSpaces } = model.getOptions();
const timeout = this._configurationService.getValue<number>('editor.formatOnSaveTimeout', { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.getResource() });
return new Promise<ISingleEditOperation[]>((resolve, reject) => {
let source = new CancellationTokenSource();
let request = getDocumentFormattingEdits(model, { tabSize, insertSpaces }, source.token);
return new Promise<TextEdit[] | null | undefined>((resolve, reject) => {
const source = new CancellationTokenSource();
const request = getDocumentFormattingEdits(this._telemetryService, this._editorWorkerService, model, model.getFormattingOptions(), FormatMode.Auto, source.token);
setTimeout(() => {
reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout));
source.cancel();
}, timeout);
request.then(edits => this._editorWorkerService.computeMoreMinimalEdits(model.uri, edits)).then(resolve, err => {
if (!NoProviderError.is(err)) {
reject(err);
} else {
resolve();
}
});
request.then(resolve, reject);
}).then(edits => {
if (isNonEmptyArray(edits) && versionNow === model.getVersionId()) {
@@ -290,11 +286,11 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
});
}
private _editsWithEditor(editor: ICodeEditor, edits: ISingleEditOperation[]): void {
private _editsWithEditor(editor: ICodeEditor, edits: TextEdit[]): void {
FormattingEdit.execute(editor, edits);
}
private _editWithModel(model: ITextModel, edits: ISingleEditOperation[]): void {
private _editWithModel(model: ITextModel, edits: TextEdit[]): void {
const [{ range }] = edits;
const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
@@ -305,11 +301,11 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant {
return [new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)];
}
}
return undefined;
return null;
});
}
private static _asIdentEdit({ text, range }: ISingleEditOperation): IIdentifiedSingleEditOperation {
private static _asIdentEdit({ text, range }: TextEdit): IIdentifiedSingleEditOperation {
return {
text,
range: Range.lift(range),
@@ -326,7 +322,7 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
@IConfigurationService private readonly _configurationService: IConfigurationService
) { }
async participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
if (env.reason === SaveReason.AUTO) {
return undefined;
}
@@ -377,14 +373,14 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
for (const codeActionKind of codeActionsOnSave) {
const actionsToRun = await this.getActionsToRun(model, codeActionKind, token);
try {
await this.applyCodeActions(actionsToRun);
await this.applyCodeActions(actionsToRun.actions);
} catch {
// Failure to apply a code action should not block other on save actions
}
}
}
private async applyCodeActions(actionsToRun: CodeAction[]) {
private async applyCodeActions(actionsToRun: ReadonlyArray<CodeAction>) {
for (const action of actionsToRun) {
await applyCodeAction(action, this._bulkEditService, this._commandService);
}
@@ -400,13 +396,13 @@ class CodeActionOnSaveParticipant implements ISaveParticipant {
class ExtHostSaveParticipant implements ISaveParticipantParticipant {
private _proxy: ExtHostDocumentSaveParticipantShape;
private readonly _proxy: ExtHostDocumentSaveParticipantShape;
constructor(extHostContext: IExtHostContext) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentSaveParticipant);
}
async participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
if (!shouldSynchronizeModel(editorModel.textEditorModel)) {
// the model never made it to the extension
@@ -455,11 +451,11 @@ export class SaveParticipant implements ISaveParticipant {
}
dispose(): void {
TextFileEditorModel.setSaveParticipant(undefined);
TextFileEditorModel.setSaveParticipant(null);
this._saveParticipants.dispose();
}
async participate(model: ITextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }): Promise<void> {
return this._progressService.withProgress({ location: ProgressLocation.Window }, progress => {
progress.report({ message: localize('saveParticipants', "Running Save Participants...") });
const promiseFactory = this._saveParticipants.getValue().map(p => () => {

View File

@@ -7,7 +7,7 @@ import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { values } from 'vs/base/common/map';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IFileMatch, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISearchProgressItem, ISearchResultProvider, ISearchService, QueryType, SearchProviderType, ITextQuery, IFileQuery } from 'vs/platform/search/common/search';
import { IFileMatch, IRawFileMatch2, ISearchComplete, ISearchCompleteStats, ISearchProgressItem, ISearchResultProvider, ISearchService, QueryType, SearchProviderType, ITextQuery, IFileQuery } from 'vs/workbench/services/search/common/search';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { ExtHostContext, ExtHostSearchShape, IExtHostContext, MainContext, MainThreadSearchShape } from '../node/extHost.protocol';
@@ -40,21 +40,27 @@ export class MainThreadSearch implements MainThreadSearchShape {
this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, SearchProviderType.file, scheme, handle, this._proxy));
}
$registerFileIndexProvider(handle: number, scheme: string): void {
this._searchProvider.set(handle, new RemoteSearchProvider(this._searchService, SearchProviderType.fileIndex, scheme, handle, this._proxy));
}
$unregisterProvider(handle: number): void {
dispose(this._searchProvider.get(handle));
this._searchProvider.delete(handle);
}
$handleFileMatch(handle: number, session, data: UriComponents[]): void {
this._searchProvider.get(handle).handleFindMatch(session, data);
const provider = this._searchProvider.get(handle);
if (!provider) {
throw new Error('Got result for unknown provider');
}
provider.handleFindMatch(session, data);
}
$handleTextMatch(handle: number, session, data: IRawFileMatch2[]): void {
this._searchProvider.get(handle).handleFindMatch(session, data);
const provider = this._searchProvider.get(handle);
if (!provider) {
throw new Error('Got result for unknown provider');
}
provider.handleFindMatch(session, data);
}
$handleTelemetry(eventName: string, data: any): void {
@@ -77,7 +83,8 @@ class SearchOperation {
addMatch(match: IFileMatch): void {
if (this.matches.has(match.resource.toString())) {
// Merge with previous IFileMatches
this.matches.get(match.resource.toString()).results.push(...match.results);
// TODO@rob clean up text/file result types
this.matches.get(match.resource.toString())!.results!.push(...match.results!);
} else {
this.matches.set(match.resource.toString(), match);
}
@@ -107,15 +114,15 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable {
dispose(this._registrations);
}
fileSearch(query: IFileQuery, token: CancellationToken = CancellationToken.None): Promise<ISearchComplete> {
return this.doSearch(query, null, token);
fileSearch(query: IFileQuery, token: CancellationToken = CancellationToken.None): Promise<ISearchComplete | undefined> {
return this.doSearch(query, undefined, token);
}
textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): Promise<ISearchComplete> {
textSearch(query: ITextQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): Promise<ISearchComplete | undefined> {
return this.doSearch(query, onProgress, token);
}
doSearch(query: ITextQuery | IFileQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): Promise<ISearchComplete> {
doSearch(query: ITextQuery | IFileQuery, onProgress?: (p: ISearchProgressItem) => void, token: CancellationToken = CancellationToken.None): Promise<ISearchComplete | undefined> {
if (isFalsyOrEmpty(query.folderQueries)) {
return Promise.resolve(undefined);
}
@@ -141,12 +148,13 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable {
}
handleFindMatch(session: number, dataOrUri: Array<UriComponents | IRawFileMatch2>): void {
if (!this._searches.has(session)) {
const searchOp = this._searches.get(session);
if (!searchOp) {
// ignore...
return;
}
const searchOp = this._searches.get(session);
dataOrUri.forEach(result => {
if ((<IRawFileMatch2>result).results) {
searchOp.addMatch({

View File

@@ -34,12 +34,12 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
this.$dispose(id);
// Add new
let entry = this._statusbarService.addEntry({ text, tooltip, command, color, extensionId }, alignment, priority);
const entry = this._statusbarService.addEntry({ text, tooltip, command, color, extensionId }, alignment, priority);
this._entries[id] = entry;
}
$dispose(id: number) {
let disposeable = this._entries[id];
const disposeable = this._entries[id];
if (disposeable) {
disposeable.dispose();
}

View File

@@ -11,10 +11,10 @@ import { IDisposable } from 'vs/base/common/lifecycle';
@extHostNamedCustomer(MainContext.MainThreadStorage)
export class MainThreadStorage implements MainThreadStorageShape {
private _storageService: IStorageService;
private _proxy: ExtHostStorageShape;
private _storageListener: IDisposable;
private _sharedStorageKeysToWatch: Map<string, boolean> = new Map<string, boolean>();
private readonly _storageService: IStorageService;
private readonly _proxy: ExtHostStorageShape;
private readonly _storageListener: IDisposable;
private readonly _sharedStorageKeysToWatch: Map<string, boolean> = new Map<string, boolean>();
constructor(
extHostContext: IExtHostContext,
@@ -24,7 +24,7 @@ export class MainThreadStorage implements MainThreadStorageShape {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostStorage);
this._storageListener = this._storageService.onDidChangeStorage(e => {
let shared = e.scope === StorageScope.GLOBAL;
const shared = e.scope === StorageScope.GLOBAL;
if (shared && this._sharedStorageKeysToWatch.has(e.key)) {
try {
this._proxy.$acceptValue(shared, e.key, this._getValue(shared, e.key));
@@ -39,7 +39,7 @@ export class MainThreadStorage implements MainThreadStorageShape {
this._storageListener.dispose();
}
$getValue<T>(shared: boolean, key: string): Promise<T> {
$getValue<T>(shared: boolean, key: string): Promise<T | undefined> {
if (shared) {
this._sharedStorageKeysToWatch.set(key, true);
}
@@ -50,8 +50,8 @@ export class MainThreadStorage implements MainThreadStorageShape {
}
}
private _getValue<T>(shared: boolean, key: string): T {
let jsonValue = this._storageService.get(key, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
private _getValue<T>(shared: boolean, key: string): T | undefined {
const jsonValue = this._storageService.get(key, shared ? StorageScope.GLOBAL : StorageScope.WORKSPACE);
if (!jsonValue) {
return undefined;
}
@@ -66,6 +66,6 @@ export class MainThreadStorage implements MainThreadStorageShape {
} catch (err) {
return Promise.reject(err);
}
return undefined;
return Promise.resolve(undefined);
}
}

View File

@@ -18,19 +18,19 @@ import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspac
import {
ContributedTask, KeyedTaskIdentifier, TaskExecution, Task, TaskEvent, TaskEventKind,
PresentationOptions, CommandOptions, CommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, TaskSourceKind, ExtensionTaskSource, RunOptions, TaskSet
} from 'vs/workbench/parts/tasks/common/tasks';
} from 'vs/workbench/contrib/tasks/common/tasks';
import { ResolveSet, ResolvedVariables } from 'vs/workbench/parts/tasks/common/taskSystem';
import { ITaskService, TaskFilter, ITaskProvider } from 'vs/workbench/parts/tasks/common/taskService';
import { ResolveSet, ResolvedVariables } from 'vs/workbench/contrib/tasks/common/taskSystem';
import { ITaskService, TaskFilter, ITaskProvider } from 'vs/workbench/contrib/tasks/common/taskService';
import { TaskDefinition } from 'vs/workbench/parts/tasks/node/tasks';
import { TaskDefinition } from 'vs/workbench/contrib/tasks/node/tasks';
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, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
ProcessExecutionDTO, ShellExecutionDTO, ShellExecutionOptionsDTO, CustomExecutionDTO, TaskDTO, TaskSourceDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO,
RunOptionsDTO
} from 'vs/workbench/api/shared/tasks';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
@@ -42,12 +42,6 @@ namespace TaskExecutionDTO {
task: TaskDTO.from(value.task)
};
}
export function to(value: TaskExecutionDTO, workspace: IWorkspaceContextService, executeOnly: boolean): TaskExecution {
return {
id: value.id,
task: TaskDTO.to(value.task, workspace, executeOnly)
};
}
}
namespace TaskProcessStartedDTO {
@@ -70,11 +64,11 @@ namespace TaskProcessEndedDTO {
namespace TaskDefinitionDTO {
export function from(value: KeyedTaskIdentifier): TaskDefinitionDTO {
let result = Objects.assign(Object.create(null), value);
const result = Objects.assign(Object.create(null), value);
delete result._key;
return result;
}
export function to(value: TaskDefinitionDTO, executeOnly: boolean): KeyedTaskIdentifier {
export function to(value: TaskDefinitionDTO, executeOnly: boolean): KeyedTaskIdentifier | undefined {
let result = TaskDefinition.createTaskIdentifier(value, console);
if (result === undefined && executeOnly) {
result = {
@@ -87,13 +81,13 @@ namespace TaskDefinitionDTO {
}
namespace TaskPresentationOptionsDTO {
export function from(value: PresentationOptions): TaskPresentationOptionsDTO {
export function from(value: PresentationOptions | undefined): TaskPresentationOptionsDTO | undefined {
if (value === undefined || value === null) {
return undefined;
}
return Objects.assign(Object.create(null), value);
}
export function to(value: TaskPresentationOptionsDTO): PresentationOptions {
export function to(value: TaskPresentationOptionsDTO | undefined): PresentationOptions {
if (value === undefined || value === null) {
return PresentationOptions.defaults;
}
@@ -102,13 +96,13 @@ namespace TaskPresentationOptionsDTO {
}
namespace RunOptionsDTO {
export function from(value: RunOptions): RunOptionsDTO {
export function from(value: RunOptions): RunOptionsDTO | undefined {
if (value === undefined || value === null) {
return undefined;
}
return Objects.assign(Object.create(null), value);
}
export function to(value: RunOptionsDTO): RunOptions {
export function to(value: RunOptionsDTO | undefined): RunOptions {
if (value === undefined || value === null) {
return RunOptions.defaults;
}
@@ -117,7 +111,7 @@ namespace RunOptionsDTO {
}
namespace ProcessExecutionOptionsDTO {
export function from(value: CommandOptions): ProcessExecutionOptionsDTO {
export function from(value: CommandOptions): ProcessExecutionOptionsDTO | undefined {
if (value === undefined || value === null) {
return undefined;
}
@@ -126,7 +120,7 @@ namespace ProcessExecutionOptionsDTO {
env: value.env
};
}
export function to(value: ProcessExecutionOptionsDTO): CommandOptions {
export function to(value: ProcessExecutionOptionsDTO | undefined): CommandOptions {
if (value === undefined || value === null) {
return CommandOptions.defaults;
}
@@ -138,14 +132,14 @@ namespace ProcessExecutionOptionsDTO {
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ProcessExecutionDTO {
let candidate = value as ProcessExecutionDTO;
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ProcessExecutionDTO {
const 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 = {
const process: string = Types.isString(value.name) ? value.name : value.name!.value;
const args: string[] = value.args ? value.args.map(value => Types.isString(value) ? value : value.value) : [];
const result: ProcessExecutionDTO = {
process: process,
args: args
};
@@ -155,7 +149,7 @@ namespace ProcessExecutionDTO {
return result;
}
export function to(value: ProcessExecutionDTO): CommandConfiguration {
let result: CommandConfiguration = {
const result: CommandConfiguration = {
runtime: RuntimeType.Process,
name: value.process,
args: value.args,
@@ -167,11 +161,11 @@ namespace ProcessExecutionDTO {
}
namespace ShellExecutionOptionsDTO {
export function from(value: CommandOptions): ShellExecutionOptionsDTO {
export function from(value: CommandOptions): ShellExecutionOptionsDTO | undefined {
if (value === undefined || value === null) {
return undefined;
}
let result: ShellExecutionOptionsDTO = {
const result: ShellExecutionOptionsDTO = {
cwd: value.cwd || CommandOptions.defaults.cwd,
env: value.env
};
@@ -182,11 +176,11 @@ namespace ShellExecutionOptionsDTO {
}
return result;
}
export function to(value: ShellExecutionOptionsDTO): CommandOptions {
export function to(value: ShellExecutionOptionsDTO): CommandOptions | undefined {
if (value === undefined || value === null) {
return undefined;
}
let result: CommandOptions = {
const result: CommandOptions = {
cwd: value.cwd,
env: value.env
};
@@ -206,12 +200,12 @@ namespace ShellExecutionOptionsDTO {
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ShellExecutionDTO {
let candidate = value as ShellExecutionDTO;
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is ShellExecutionDTO {
const candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
}
export function from(value: CommandConfiguration): ShellExecutionDTO {
let result: ShellExecutionDTO = {};
const result: ShellExecutionDTO = {};
if (value.name && Types.isString(value.name) && (value.args === undefined || value.args === null || value.args.length === 0)) {
result.commandLine = value.name;
} else {
@@ -224,7 +218,7 @@ namespace ShellExecutionDTO {
return result;
}
export function to(value: ShellExecutionDTO): CommandConfiguration {
let result: CommandConfiguration = {
const result: CommandConfiguration = {
runtime: RuntimeType.Shell,
name: value.commandLine ? value.commandLine : value.command,
args: value.args,
@@ -237,9 +231,29 @@ namespace ShellExecutionDTO {
}
}
namespace CustomExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO): value is CustomExecutionDTO {
const candidate = value as CustomExecutionDTO;
return candidate && candidate.customExecution === 'customExecution';
}
export function from(value: CommandConfiguration): CustomExecutionDTO {
return {
customExecution: 'customExecution'
};
}
export function to(value: CustomExecutionDTO): CommandConfiguration {
return {
runtime: RuntimeType.CustomExecution,
presentation: undefined
};
}
}
namespace TaskSourceDTO {
export function from(value: TaskSource): TaskSourceDTO {
let result: TaskSourceDTO = {
const result: TaskSourceDTO = {
label: value.label
};
if (value.kind === TaskSourceKind.Extension) {
@@ -257,7 +271,7 @@ namespace TaskSourceDTO {
}
export function to(value: TaskSourceDTO, workspace: IWorkspaceContextService): ExtensionTaskSource {
let scope: TaskScope;
let workspaceFolder: IWorkspaceFolder;
let workspaceFolder: IWorkspaceFolder | undefined;
if ((value.scope === undefined) || ((typeof value.scope === 'number') && (value.scope !== TaskScope.Global))) {
if (workspace.getWorkspace().folders.length === 0) {
scope = TaskScope.Global;
@@ -270,9 +284,9 @@ namespace TaskSourceDTO {
scope = value.scope;
} else {
scope = TaskScope.Folder;
workspaceFolder = workspace.getWorkspaceFolder(URI.revive(value.scope));
workspaceFolder = Types.withNullAsUndefined(workspace.getWorkspaceFolder(URI.revive(value.scope)));
}
let result: ExtensionTaskSource = {
const result: ExtensionTaskSource = {
kind: TaskSourceKind.Extension,
label: value.label,
extension: value.extensionId,
@@ -285,17 +299,17 @@ namespace TaskSourceDTO {
namespace TaskHandleDTO {
export function is(value: any): value is TaskHandleDTO {
let candidate: TaskHandleDTO = value;
const candidate: TaskHandleDTO = value;
return candidate && Types.isString(candidate.id) && !!candidate.workspaceFolder;
}
}
namespace TaskDTO {
export function from(task: Task): TaskDTO {
export function from(task: Task): TaskDTO | undefined {
if (task === undefined || task === null || (!CustomTask.is(task) && !ContributedTask.is(task))) {
return undefined;
}
let result: TaskDTO = {
const result: TaskDTO = {
_id: task._id,
name: task.configurationProperties.name,
definition: TaskDefinitionDTO.from(task.getDefinition()),
@@ -327,26 +341,32 @@ namespace TaskDTO {
return result;
}
export function to(task: TaskDTO, workspace: IWorkspaceContextService, executeOnly: boolean): ContributedTask {
if (typeof task.name !== 'string') {
export function to(task: TaskDTO | undefined, workspace: IWorkspaceContextService, executeOnly: boolean): ContributedTask | undefined {
if (!task || (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);
let command: CommandConfiguration | undefined;
if (task.execution) {
if (ShellExecutionDTO.is(task.execution)) {
command = ShellExecutionDTO.to(task.execution);
} else if (ProcessExecutionDTO.is(task.execution)) {
command = ProcessExecutionDTO.to(task.execution);
} else if (CustomExecutionDTO.is(task.execution)) {
command = CustomExecutionDTO.to(task.execution);
}
}
if (!command) {
return undefined;
}
command.presentation = TaskPresentationOptionsDTO.to(task.presentationOptions);
let source = TaskSourceDTO.to(task.source, workspace);
const source = TaskSourceDTO.to(task.source, workspace);
let label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
let definition = TaskDefinitionDTO.to(task.definition, executeOnly);
let id = `${task.source.extensionId}.${definition._key}`;
let result: ContributedTask = new ContributedTask(
const label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
const definition = TaskDefinitionDTO.to(task.definition, executeOnly)!;
const id = `${task.source.extensionId}.${definition._key}`;
const result: ContributedTask = new ContributedTask(
id, // uuidMap.getUUID(identifier)
source,
label,
@@ -371,7 +391,7 @@ namespace TaskFilterDTO {
export function from(value: TaskFilter): TaskFilterDTO {
return value;
}
export function to(value: TaskFilterDTO): TaskFilter {
export function to(value: TaskFilterDTO | undefined): TaskFilter | undefined {
return value;
}
}
@@ -379,9 +399,9 @@ namespace TaskFilterDTO {
@extHostNamedCustomer(MainContext.MainThreadTask)
export class MainThreadTask implements MainThreadTaskShape {
private _extHostContext: IExtHostContext;
private _proxy: ExtHostTaskShape;
private _providers: Map<number, { disposable: IDisposable, provider: ITaskProvider }>;
private readonly _extHostContext: IExtHostContext;
private readonly _proxy: ExtHostTaskShape;
private readonly _providers: Map<number, { disposable: IDisposable, provider: ITaskProvider }>;
constructor(
extHostContext: IExtHostContext,
@@ -392,13 +412,13 @@ export class MainThreadTask implements MainThreadTaskShape {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTask);
this._providers = new Map();
this._taskService.onDidStateChange((event: TaskEvent) => {
let task = event.__task;
const task = event.__task!;
if (event.kind === TaskEventKind.Start) {
this._proxy.$onDidStartTask(TaskExecutionDTO.from(task.getTaskExecution()));
this._proxy.$onDidStartTask(TaskExecutionDTO.from(task.getTaskExecution()), event.terminalId!);
} else if (event.kind === TaskEventKind.ProcessStarted) {
this._proxy.$onDidStartTaskProcess(TaskProcessStartedDTO.from(task.getTaskExecution(), event.processId));
this._proxy.$onDidStartTaskProcess(TaskProcessStartedDTO.from(task.getTaskExecution(), event.processId!));
} else if (event.kind === TaskEventKind.ProcessEnded) {
this._proxy.$onDidEndTaskProcess(TaskProcessEndedDTO.from(task.getTaskExecution(), event.exitCode));
this._proxy.$onDidEndTaskProcess(TaskProcessEndedDTO.from(task.getTaskExecution(), event.exitCode!));
} else if (event.kind === TaskEventKind.End) {
this._proxy.$OnDidEndTask(TaskExecutionDTO.from(task.getTaskExecution()));
}
@@ -412,13 +432,24 @@ export class MainThreadTask implements MainThreadTaskShape {
this._providers.clear();
}
$createTaskId(taskDTO: TaskDTO): Promise<string> {
return new Promise((resolve, reject) => {
let task = TaskDTO.to(taskDTO, this._workspaceContextServer, true);
if (task) {
resolve(task._id);
} else {
reject(new Error('Task could not be created from DTO'));
}
});
}
public $registerTaskProvider(handle: number): Promise<void> {
let provider: ITaskProvider = {
const provider: ITaskProvider = {
provideTasks: (validTypes: IStringDictionary<boolean>) => {
return Promise.resolve(this._proxy.$provideTasks(handle, validTypes)).then((value) => {
let tasks: Task[] = [];
const tasks: Task[] = [];
for (let dto of value.tasks) {
let task = TaskDTO.to(dto, this._workspaceContextServer, true);
const task = TaskDTO.to(dto, this._workspaceContextServer, true);
if (task) {
tasks.push(task);
} else {
@@ -432,21 +463,25 @@ export class MainThreadTask implements MainThreadTaskShape {
});
}
};
let disposable = this._taskService.registerTaskProvider(provider);
const disposable = this._taskService.registerTaskProvider(provider);
this._providers.set(handle, { disposable, provider });
return Promise.resolve(undefined);
}
public $unregisterTaskProvider(handle: number): Promise<void> {
this._providers.delete(handle);
const provider = this._providers.get(handle);
if (provider) {
provider.disposable.dispose();
this._providers.delete(handle);
}
return Promise.resolve(undefined);
}
public $fetchTasks(filter?: TaskFilterDTO): Promise<TaskDTO[]> {
return this._taskService.tasks(TaskFilterDTO.to(filter)).then((tasks) => {
let result: TaskDTO[] = [];
const result: TaskDTO[] = [];
for (let task of tasks) {
let item = TaskDTO.from(task);
const item = TaskDTO.from(task);
if (item) {
result.push(item);
}
@@ -458,25 +493,29 @@ export class MainThreadTask implements MainThreadTaskShape {
public $executeTask(value: TaskHandleDTO | TaskDTO): Promise<TaskExecutionDTO> {
return new Promise<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).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
const workspaceFolder = this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
if (workspaceFolder) {
this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task) => {
this._taskService.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
const result: TaskExecutionDTO = {
id: value.id,
task: TaskDTO.from(task)
};
resolve(result);
}, (_error) => {
reject(new Error('Task not found'));
});
let result: TaskExecutionDTO = {
id: value.id,
task: TaskDTO.from(task)
};
resolve(result);
}, (_error) => {
reject(new Error('Task not found'));
});
} else {
reject(new Error('No workspace folder'));
}
} else {
let task = TaskDTO.to(value, this._workspaceContextServer, true);
const task = TaskDTO.to(value, this._workspaceContextServer, true)!;
this._taskService.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
let result: TaskExecutionDTO = {
const result: TaskExecutionDTO = {
id: task._id,
task: TaskDTO.from(task)
};
@@ -485,6 +524,24 @@ export class MainThreadTask implements MainThreadTaskShape {
});
}
public $customExecutionComplete(id: string, result?: number): Promise<void> {
return new Promise<void>((resolve, reject) => {
this._taskService.getActiveTasks().then((tasks) => {
for (let task of tasks) {
if (id === task._id) {
this._taskService.extensionCallbackTaskComplete(task, result).then((value) => {
resolve(undefined);
}, (error) => {
reject(error);
});
return;
}
}
reject(new Error('Task to mark as complete not found'));
});
});
}
public $terminateTask(id: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
this._taskService.getActiveTasks().then((tasks) => {
@@ -525,17 +582,17 @@ export class MainThreadTask implements MainThreadTaskShape {
},
context: this._extHostContext,
resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet): Promise<ResolvedVariables> => {
let vars: string[] = [];
const vars: string[] = [];
toResolve.variables.forEach(item => vars.push(item));
return Promise.resolve(this._proxy.$resolveVariables(workspaceFolder.uri, { process: toResolve.process, variables: vars })).then(values => {
const partiallyResolvedVars = new Array<string>();
forEach(values.variables, (entry) => {
partiallyResolvedVars.push(entry.value);
});
return new Promise((resolve, reject) => {
return new Promise<ResolvedVariables>((resolve, reject) => {
this._configurationResolverService.resolveWithInteraction(workspaceFolder, partiallyResolvedVars, 'tasks').then(resolvedVars => {
let result = {
process: undefined as string,
const result: ResolvedVariables = {
process: undefined,
variables: new Map<string, string>()
};
for (let i = 0; i < partiallyResolvedVars.length; i++) {

View File

@@ -4,9 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/parts/terminal/common/terminal';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig, ITerminalProcessExtHostProxy, ITerminalProcessExtHostRequest, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/contrib/terminal/common/terminal';
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { UriComponents, URI } from 'vs/base/common/uri';
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
@@ -28,13 +29,16 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
// 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(instance), EXT_HOST_CREATION_DELAY);
setTimeout(() => {
this._onTerminalOpened(instance);
this._onInstanceDimensionsChanged(instance);
}, EXT_HOST_CREATION_DELAY);
}));
this._toDispose.push(terminalService.onInstanceDisposed(instance => this._onTerminalDisposed(instance)));
this._toDispose.push(terminalService.onInstanceProcessIdReady(instance => this._onTerminalProcessIdReady(instance)));
this._toDispose.push(terminalService.onInstanceDimensionsChanged(instance => this._onInstanceDimensionsChanged(instance)));
this._toDispose.push(terminalService.onInstanceRequestExtHostProcess(request => this._onTerminalRequestExtHostProcess(request)));
this._toDispose.push(terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : undefined)));
this._toDispose.push(terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : null)));
this._toDispose.push(terminalService.onInstanceTitleChanged(instance => this._onTitleChanged(instance.id, instance.title)));
// Set initial ext host state
@@ -55,12 +59,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
// when the extension host process goes down ?
}
public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }> {
public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string | UriComponents, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }> {
const shellLaunchConfig: IShellLaunchConfig = {
name,
executable: shellPath,
args: shellArgs,
cwd,
cwd: typeof cwd === 'string' ? cwd : URI.revive(cwd),
waitOnExit,
ignoreConfigurationCwd: true,
env,
@@ -87,7 +91,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
public $hide(terminalId: number): void {
if (this.terminalService.getActiveInstance().id === terminalId) {
const instance = this.terminalService.getActiveInstance();
if (instance && instance.id === terminalId) {
this.terminalService.hidePanel();
}
}
@@ -161,7 +166,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
terminalInstance.addDisposable(this._terminalOnDidWriteDataListeners[terminalId]);
}
private _onActiveTerminalChanged(terminalId: number | undefined): void {
private _onActiveTerminalChanged(terminalId: number | null): void {
this._proxy.$acceptActiveTerminalChanged(terminalId);
}
@@ -192,15 +197,14 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
private _onTerminalProcessIdReady(terminalInstance: ITerminalInstance): void {
if (terminalInstance.processId === undefined) {
return;
}
this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId);
}
private _onInstanceDimensionsChanged(instance: ITerminalInstance): void {
// Only send the dimensions if the terminal is a renderer only as there is no API to access
// dimensions on a plain Terminal.
if (instance.shellLaunchConfig.isRendererOnly) {
this._proxy.$acceptTerminalRendererDimensions(instance.id, instance.cols, instance.rows);
}
this._proxy.$acceptTerminalDimensions(instance.id, instance.cols, instance.rows);
}
private _onTerminalRequestExtHostProcess(request: ITerminalProcessExtHostRequest): void {

View File

@@ -5,18 +5,19 @@
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, ViewsRegistry, ITreeViewDescriptor, IRevealOptions } from 'vs/workbench/common/views';
import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, IViewsRegistry, ITreeViewDescriptor, IRevealOptions, Extensions } 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';
import { isUndefinedOrNull, isNumber } from 'vs/base/common/types';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { Registry } from 'vs/platform/registry/common/platform';
@extHostNamedCustomer(MainContext.MainThreadTreeViews)
export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape {
private _proxy: ExtHostTreeViewsShape;
private _dataProviders: Map<string, TreeViewDataProvider> = new Map<string, TreeViewDataProvider>();
private readonly _proxy: ExtHostTreeViewsShape;
private readonly _dataProviders: Map<string, TreeViewDataProvider> = new Map<string, TreeViewDataProvider>();
constructor(
extHostContext: IExtHostContext,
@@ -45,7 +46,10 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
return this.viewsService.openView(treeViewId, options.focus)
.then(() => {
const viewer = this.getTreeView(treeViewId);
return this.reveal(viewer, this._dataProviders.get(treeViewId), item, parentChain, options);
if (viewer) {
return this.reveal(viewer, this._dataProviders.get(treeViewId)!, item, parentChain, options);
}
return undefined;
});
}
@@ -56,7 +60,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
const itemsToRefresh = dataProvider.getItemsToRefresh(itemsToRefreshByHandle);
return viewer.refresh(itemsToRefresh.length ? itemsToRefresh : undefined);
}
return null;
return Promise.resolve();
}
$setMessage(treeViewId: string, message: string | IMarkdownString): void {
@@ -66,7 +70,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
}
}
private async reveal(treeView: ITreeView, dataProvider: TreeViewDataProvider, item: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise<void> {
private async reveal(treeView: ITreeView, dataProvider: TreeViewDataProvider, itemIn: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise<void> {
options = options ? options : { select: false, focus: false };
const select = isUndefinedOrNull(options.select) ? false : options.select;
const focus = isUndefinedOrNull(options.focus) ? false : options.focus;
@@ -79,7 +83,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
for (const parent of parentChain) {
await treeView.expand(parent);
}
item = dataProvider.getItem(item.handle);
const item = dataProvider.getItem(itemIn.handle);
if (item) {
await treeView.reveal(item);
if (select) {
@@ -91,13 +95,13 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
let itemsToExpand = [item];
for (; itemsToExpand.length > 0 && expand > 0; expand--) {
await treeView.expand(itemsToExpand);
itemsToExpand = itemsToExpand.reduce((result, item) => {
item = dataProvider.getItem(item.handle);
itemsToExpand = itemsToExpand.reduce((result, itemValue) => {
const item = dataProvider.getItem(itemValue.handle);
if (item && item.children && item.children.length) {
result.push(...item.children);
}
return result;
}, []);
}, [] as ITreeItem[]);
}
}
}
@@ -109,8 +113,8 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
this._register(treeView.onDidChangeVisibility(isVisible => this._proxy.$setVisible(treeViewId, isVisible)));
}
private getTreeView(treeViewId: string): ITreeView {
const viewDescriptor: ITreeViewDescriptor = <ITreeViewDescriptor>ViewsRegistry.getView(treeViewId);
private getTreeView(treeViewId: string): ITreeView | null {
const viewDescriptor: ITreeViewDescriptor = <ITreeViewDescriptor>Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).getView(treeViewId);
return viewDescriptor ? viewDescriptor.treeView : null;
}
@@ -132,13 +136,12 @@ export type TreeItemHandle = string;
// {{SQL CARBON EDIT}}
export class TreeViewDataProvider implements ITreeViewDataProvider {
// {{SQL CARBON EDIT}}
protected itemsMap: Map<TreeItemHandle, ITreeItem> = new Map<TreeItemHandle, ITreeItem>();
private readonly itemsMap: Map<TreeItemHandle, ITreeItem> = new Map<TreeItemHandle, ITreeItem>();
// {{SQL CARBON EDIT}}
constructor(protected treeViewId: string,
protected _proxy: ExtHostTreeViewsShape,
protected notificationService: INotificationService
constructor(protected readonly treeViewId: string,
protected readonly _proxy: ExtHostTreeViewsShape,
private readonly notificationService: INotificationService
) {
}
@@ -178,7 +181,7 @@ export class TreeViewDataProvider implements ITreeViewDataProvider {
return itemsToRefresh;
}
getItem(treeItemHandle: string): ITreeItem {
getItem(treeItemHandle: string): ITreeItem | undefined {
return this.itemsMap.get(treeItemHandle);
}
@@ -198,7 +201,7 @@ export class TreeViewDataProvider implements ITreeViewDataProvider {
}
private updateTreeItem(current: ITreeItem, treeItem: ITreeItem): void {
treeItem.children = treeItem.children ? treeItem.children : null;
treeItem.children = treeItem.children ? treeItem.children : undefined;
if (current) {
const properties = distinct([...Object.keys(current), ...Object.keys(treeItem)]);
for (const property of properties) {

View File

@@ -2,84 +2,99 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as map from 'vs/base/common/map';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { localize } from 'vs/nls';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/node/extHost.protocol';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewInsetHandle, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/node/extHost.protocol';
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/shared/editor';
import { WebviewEditor } from 'vs/workbench/parts/webview/electron-browser/webviewEditor';
import { WebviewEditorInput } from 'vs/workbench/parts/webview/electron-browser/webviewEditorInput';
import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions, WebviewReviver } from 'vs/workbench/parts/webview/electron-browser/webviewEditorService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { CodeInsetController } from 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution';
import { WebviewEditor } from 'vs/workbench/contrib/webview/electron-browser/webviewEditor';
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput';
import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorService';
import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import * as vscode from 'vscode';
import { extHostNamedCustomer } from './extHostCustomers';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { onUnexpectedError } from 'vs/base/common/errors';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver {
private static readonly viewType = 'mainThreadWebview';
export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape {
private static readonly standardSupportedLinkSchemes = ['http', 'https', 'mailto'];
private static revivalPool = 0;
private _toDispose: IDisposable[] = [];
private readonly _proxy: ExtHostWebviewsShape;
private readonly _webviews = new Map<WebviewPanelHandle, WebviewEditorInput>();
private readonly _revivers = new Set<string>();
private readonly _webviewsElements = new Map<WebviewInsetHandle, WebviewElement>();
private readonly _revivers = new Map<string, IDisposable>();
private _activeWebview: WebviewPanelHandle | undefined = undefined;
constructor(
context: IExtHostContext,
@ILifecycleService lifecycleService: ILifecycleService,
@IExtensionService extensionService: IExtensionService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IEditorService private readonly _editorService: IEditorService,
@IWebviewEditorService private readonly _webviewService: IWebviewEditorService,
@IOpenerService private readonly _openerService: IOpenerService,
@IExtensionService private readonly _extensionService: IExtensionService,
@ITelemetryService private readonly _telemetryService: ITelemetryService
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
@IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService,
) {
super();
this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews);
_editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this, this._toDispose);
_editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this, this._toDispose);
this._toDispose.push(_webviewService.registerReviver(MainThreadWebviews.viewType, this));
// This reviver's only job is to activate webview extensions
// This should trigger the real reviver to be registered from the extension host side.
this._toDispose.push(_webviewService.registerReviver({
canRevive: (webview) => {
const viewType = webview.state.viewType;
if (viewType) {
extensionService.activateByEvent(`onWebviewPanel:${viewType}`);
}
return false;
},
reviveWebview: () => { throw new Error('not implemented'); }
}));
lifecycleService.onBeforeShutdown(e => {
e.veto(this._onBeforeShutdown());
}, this, this._toDispose);
}
public dispose(): void {
this._toDispose = dispose(this._toDispose);
}
public $createWebviewPanel(
handle: WebviewPanelHandle,
viewType: string,
title: string,
showOptions: { viewColumn: EditorViewColumn | null, preserveFocus: boolean },
showOptions: { viewColumn?: EditorViewColumn, preserveFocus?: boolean },
options: WebviewInputOptions,
extensionId: ExtensionIdentifier,
extensionLocation: UriComponents
): void {
const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null);
if (showOptions) {
mainThreadShowOptions.preserveFocus = showOptions.preserveFocus;
mainThreadShowOptions.preserveFocus = !!showOptions.preserveFocus;
mainThreadShowOptions.group = viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn);
}
const webview = this._webviewService.createWebview(MainThreadWebviews.viewType, title, mainThreadShowOptions, reviveWebviewOptions(options), URI.revive(extensionLocation), this.createWebviewEventDelegate(handle));
const webview = this._webviewService.createWebview(this.getInternalWebviewId(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), URI.revive(extensionLocation), this.createWebviewEventDelegate(handle));
webview.state = {
viewType: viewType,
state: undefined
@@ -96,6 +111,44 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value });
}
$createWebviewCodeInset(handle: WebviewInsetHandle, symbolId: string, options: vscode.WebviewOptions, extensionLocation: UriComponents): void {
// todo@joh main is for the lack of a code-inset service
// which we maybe wanna have... this is how it now works
// 1) create webview element
// 2) find the code inset controller that request it
// 3) let the controller adopt the widget
// 4) continue to forward messages to the webview
const webview = this._instantiationService.createInstance(
WebviewElement,
this._layoutService.getContainer(Parts.EDITOR_PART),
{
extensionLocation: URI.revive(extensionLocation),
enableFindWidget: false,
},
{
allowScripts: options.enableScripts,
}
);
let found = false;
for (const editor of this._codeEditorService.listCodeEditors()) {
const ctrl = CodeInsetController.get(editor);
if (ctrl && ctrl.acceptWebview(symbolId, webview)) {
found = true;
break;
}
}
if (!found) {
webview.dispose();
return;
}
// this will leak... the adopted webview will be disposed by the
// code inset controller. we might need a dispose-event here so that
// we can clean up things.
this._webviewsElements.set(handle, webview);
}
public $disposeWebview(handle: WebviewPanelHandle): void {
const webview = this.getWebview(handle);
webview.dispose();
@@ -111,14 +164,22 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
webview.iconPath = reviveWebviewIcon(value);
}
public $setHtml(handle: WebviewPanelHandle, value: string): void {
const webview = this.getWebview(handle);
webview.html = value;
public $setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void {
if (typeof handle === 'number') {
this.getWebviewElement(handle).contents = value;
} else {
const webview = this.getWebview(handle);
webview.html = value;
}
}
public $setOptions(handle: WebviewPanelHandle, options: vscode.WebviewOptions): void {
const webview = this.getWebview(handle);
webview.setOptions(reviveWebviewOptions(options));
public $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: vscode.WebviewOptions): void {
if (typeof handle === 'number') {
this.getWebviewElement(handle).options = reviveWebviewOptions(options);
} else {
const webview = this.getWebview(handle);
webview.setOptions(reviveWebviewOptions(options));
}
}
public $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void {
@@ -128,69 +189,82 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
}
const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn));
this._webviewService.revealWebview(webview, targetGroup || this._editorGroupService.activeGroup, showOptions.preserveFocus);
if (targetGroup) {
this._webviewService.revealWebview(webview, targetGroup || this._editorGroupService.getGroup(webview.group || ACTIVE_GROUP), !!showOptions.preserveFocus);
}
}
public $postMessage(handle: WebviewPanelHandle, message: any): Promise<boolean> {
const webview = this.getWebview(handle);
const editors = this._editorService.visibleControls
.filter(e => e instanceof WebviewEditor)
.map(e => e as WebviewEditor)
.filter(e => e.input.matches(webview));
public $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, message: any): Promise<boolean> {
if (typeof handle === 'number') {
this.getWebviewElement(handle).sendMessage(message);
return Promise.resolve(true);
for (const editor of editors) {
editor.sendMessage(message);
} else {
const webview = this.getWebview(handle);
const editors = this._editorService.visibleControls
.filter(e => e instanceof WebviewEditor)
.map(e => e as WebviewEditor)
.filter(e => e.input!.matches(webview));
for (const editor of editors) {
editor.sendMessage(message);
}
return Promise.resolve(editors.length > 0);
}
return Promise.resolve(editors.length > 0);
}
public $registerSerializer(viewType: string): void {
this._revivers.add(viewType);
}
if (this._revivers.has(viewType)) {
throw new Error(`Reviver for ${viewType} already registered`);
}
public $unregisterSerializer(viewType: string): void {
this._revivers.delete(viewType);
}
this._revivers.set(viewType, this._webviewService.registerReviver({
canRevive: (webview) => {
return webview.state && webview.state.viewType === viewType;
},
reviveWebview: async (webview): Promise<void> => {
const viewType = webview.state.viewType;
const handle = 'revival-' + MainThreadWebviews.revivalPool++;
this._webviews.set(handle, webview);
webview._events = this.createWebviewEventDelegate(handle);
let state = undefined;
if (webview.state.state) {
try {
state = JSON.parse(webview.state.state);
} catch {
// noop
}
}
public reviveWebview(webview: WebviewEditorInput): Promise<void> {
const viewType = webview.state.viewType;
return Promise.resolve(this._extensionService.activateByEvent(`onWebviewPanel:${viewType}`).then(() => {
const handle = 'revival-' + MainThreadWebviews.revivalPool++;
this._webviews.set(handle, webview);
webview._events = this.createWebviewEventDelegate(handle);
let state = undefined;
if (webview.state.state) {
try {
state = JSON.parse(webview.state.state);
} catch {
// noop
await this._proxy.$deserializeWebviewPanel(handle, viewType, webview.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webview.group || ACTIVE_GROUP), webview.options);
} catch (error) {
onUnexpectedError(error);
webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
}
}
return this._proxy.$deserializeWebviewPanel(handle, webview.state.viewType, webview.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webview.group), webview.options)
.then(undefined, error => {
onUnexpectedError(error);
webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
});
}));
}
public canRevive(webview: WebviewEditorInput): boolean {
if (webview.isDisposed() || !webview.state) {
return false;
public $unregisterSerializer(viewType: string): void {
const reviver = this._revivers.get(viewType);
if (!reviver) {
throw new Error(`No reviver for ${viewType} registered`);
}
return this._revivers.has(webview.state.viewType) || !!webview.reviver;
reviver.dispose();
this._revivers.delete(viewType);
}
private getInternalWebviewId(viewType: string): string {
return `mainThreadWebview-${viewType}`;
}
private _onBeforeShutdown(): boolean {
this._webviews.forEach((view) => {
if (this.canRevive(view)) {
view.state.state = view.webviewState;
this._webviews.forEach((webview) => {
if (!webview.isDisposed() && webview.state && this._revivers.has(webview.state.viewType)) {
webview.state.state = webview.webviewState;
}
});
return false; // Don't veto shutdown
@@ -201,12 +275,9 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
onDidClickLink: uri => this.onDidClickLink(handle, uri),
onMessage: message => this._proxy.$onMessage(handle, message),
onDispose: () => {
const cleanUp = () => {
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
this._webviews.delete(handle);
};
this._proxy.$onDidDisposeWebviewPanel(handle).then(
cleanUp,
cleanUp);
});
}
};
}
@@ -216,7 +287,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
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);
const input = this._webviews.get(handle)!;
if (input.matches(activeEditor.input)) {
newActiveWebview = { input, handle };
break;
@@ -229,7 +300,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, {
active: true,
visible: true,
position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group)
position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group || ACTIVE_GROUP)
});
return;
}
@@ -240,8 +311,8 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
if (oldActiveWebview) {
this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, {
active: false,
visible: this._editorService.visibleControls.some(editor => editor.input && editor.input.matches(oldActiveWebview)),
position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group),
visible: this._editorService.visibleControls.some(editor => !!editor.input && editor.input.matches(oldActiveWebview)),
position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group || ACTIVE_GROUP),
});
}
}
@@ -251,7 +322,7 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, {
active: true,
visible: true,
position: editorGroupToViewColumn(this._editorGroupService, activeEditor.group)
position: editorGroupToViewColumn(this._editorGroupService, activeEditor ? activeEditor.group : ACTIVE_GROUP),
});
this._activeWebview = newActiveWebview.handle;
} else {
@@ -263,9 +334,9 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
this._webviews.forEach((input, handle) => {
for (const workbenchEditor of this._editorService.visibleControls) {
if (workbenchEditor.input && workbenchEditor.input.matches(input)) {
const editorPosition = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group);
const editorPosition = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group!);
input.updateGroup(workbenchEditor.group.id);
input.updateGroup(workbenchEditor.group!.id);
this._proxy.$onDidChangeWebviewPanelViewState(handle, {
active: handle === this._activeWebview,
visible: true,
@@ -297,6 +368,14 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
return webview;
}
private getWebviewElement(handle: number): WebviewElement {
const webview = this._webviewsElements.get(handle);
if (!webview) {
throw new Error('Unknown webview handle:' + handle);
}
return webview;
}
private static getDeserializationFailedContents(viewType: string) {
return `<!DOCTYPE html>
<html>

View File

@@ -3,27 +3,26 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ILabelService } from 'vs/platform/label/common/label';
import { IFolderQuery, IPatternInfo, ISearchConfiguration, ISearchProgressItem, ISearchService, QueryType, IFileQuery, IFileMatch } from 'vs/platform/search/common/search';
import { IFileMatch, IPatternInfo, ISearchProgressItem, ISearchService } from 'vs/workbench/services/search/common/search';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { QueryBuilder, ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder';
import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape } from '../node/extHost.protocol';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape, IWorkspaceData } from '../node/extHost.protocol';
import { TextSearchComplete } from 'vscode';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@@ -31,13 +30,13 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
private readonly _toDispose: IDisposable[] = [];
private readonly _activeCancelTokens: { [id: number]: CancellationTokenSource } = Object.create(null);
private readonly _proxy: ExtHostWorkspaceShape;
private readonly _queryBuilder = this._instantiationService.createInstance(QueryBuilder);
constructor(
extHostContext: IExtHostContext,
@ISearchService private readonly _searchService: ISearchService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@ITextFileService private readonly _textFileService: ITextFileService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService,
@IStatusbarService private readonly _statusbarService: IStatusbarService,
@IWindowService private readonly _windowService: IWindowService,
@@ -45,6 +44,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@ILabelService private readonly _labelService: ILabelService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace);
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace)));
this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose);
this._contextService.onDidChangeWorkbenchState(this._onDidChangeWorkspace, this, this._toDispose);
}
@@ -102,61 +102,41 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
}
private _onDidChangeWorkspace(): void {
const workspace = this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : this._contextService.getWorkspace();
this._proxy.$acceptWorkspaceData(workspace ? {
configuration: workspace.configuration,
this._proxy.$acceptWorkspaceData(this.getWorkspaceData(this._contextService.getWorkspace()));
}
private getWorkspaceData(workspace: IWorkspace): IWorkspaceData | null {
if (this._contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
return null;
}
return {
configuration: workspace.configuration || undefined,
folders: workspace.folders,
id: workspace.id,
name: this._labelService.getWorkspaceLabel(workspace)
} : null);
};
}
// --- search ---
$startFileSearch(includePattern: string, _includeFolder: UriComponents, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise<URI[]> {
$startFileSearch(includePattern: string, _includeFolder: UriComponents | undefined, excludePatternOrDisregardExcludes: string | false | undefined, maxResults: number, token: CancellationToken): Promise<URI[] | undefined> {
const includeFolder = URI.revive(_includeFolder);
const workspace = this._contextService.getWorkspace();
if (!workspace.folders.length) {
return undefined;
return Promise.resolve(undefined);
}
let folderQueries: IFolderQuery[];
if (includeFolder) {
folderQueries = [{ folder: includeFolder }]; // if base provided, only search in that folder
} else {
folderQueries = workspace.folders.map(folder => ({ folder: folder.uri })); // absolute pattern: search across all folders
}
if (!folderQueries) {
return undefined; // invalid query parameters
}
const ignoreSymlinks = folderQueries.every(folderQuery => {
const folderConfig = this._configurationService.getValue<ISearchConfiguration>({ resource: folderQuery.folder });
return !folderConfig.search.followSymlinks;
});
// TODO replace wth QueryBuilder
folderQueries.forEach(fq => {
fq.ignoreSymlinks = ignoreSymlinks;
});
const query: IFileQuery = {
folderQueries,
type: QueryType.File,
maxResults,
disregardExcludeSettings: excludePatternOrDisregardExcludes === false,
_reason: 'startFileSearch'
};
if (typeof includePattern === 'string') {
query.includePattern = { [includePattern]: true };
}
if (typeof excludePatternOrDisregardExcludes === 'string') {
query.excludePattern = { [excludePatternOrDisregardExcludes]: true };
}
this._searchService.extendQuery(query);
const query = this._queryBuilder.file(
includeFolder ? [includeFolder] : workspace.folders.map(f => f.uri),
{
maxResults,
disregardExcludeSettings: (excludePatternOrDisregardExcludes === false) || undefined,
disregardSearchExcludeSettings: true,
disregardIgnoreFiles: true,
includePattern,
excludePattern: typeof excludePatternOrDisregardExcludes === 'string' ? excludePatternOrDisregardExcludes : undefined,
_reason: 'startFileSearch'
});
return this._searchService.fileSearch(query, token).then(result => {
return result.results.map(m => m.resource);
@@ -172,8 +152,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
const workspace = this._contextService.getWorkspace();
const folders = workspace.folders.map(folder => folder.uri);
const queryBuilder = this._instantiationService.createInstance(QueryBuilder);
const query = queryBuilder.text(pattern, folders, options);
const query = this._queryBuilder.text(pattern, folders, options);
query._reason = 'startTextSearch';
const onProgress = (p: ISearchProgressItem) => {
@@ -203,6 +182,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
const query = queryBuilder.file(folders, {
_reason: 'checkExists',
includePattern: includes.join(', '),
expandPatterns: true,
exists: true
});
@@ -227,7 +207,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
});
}
$resolveProxy(url: string): Promise<string> {
$resolveProxy(url: string): Promise<string | undefined> {
return this._windowService.resolveProxy(url);
}
}
@@ -241,7 +221,7 @@ CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (ac
const runningExtensions = await extensionService.getExtensions();
// If requested extension to disable is running, then reload window with given workspace
if (disableExtensions && runningExtensions.some(runningExtension => disableExtensions.some(id => ExtensionIdentifier.equals(runningExtension.identifier, id)))) {
return windowService.openWindow([URI.file(workspace.fsPath)], { args: { _: [], 'disable-extension': disableExtensions } });
return windowService.openWindow([{ uri: workspace, typeHint: 'file' }], { args: { _: [], 'disable-extension': disableExtensions } });
}
}

View File

@@ -3,21 +3,16 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { tmpdir } from 'os';
import { posix } from 'path';
import * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { isMalformedFileUri } from 'vs/base/common/resources';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IWindowsService, IOpenSettings } from 'vs/platform/windows/common/windows';
import { IDownloadService } from 'vs/platform/download/common/download';
import { generateUuid } from 'vs/base/common/uuid';
// -----------------------------------------------------------------
// The following commands are registered on both sides separately.
@@ -27,7 +22,7 @@ import { generateUuid } from 'vs/base/common/uuid';
// -----------------------------------------------------------------
export interface ICommandsExecutor {
executeCommand<T>(id: string, ...args: any[]): Promise<T>;
executeCommand<T>(id: string, ...args: any[]): Promise<T | undefined>;
}
function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) => any): ICommandHandler {
@@ -36,36 +31,42 @@ function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) =>
};
}
export class PreviewHTMLAPICommand {
public static ID = 'vscode.previewHtml';
public static execute(executor: ICommandsExecutor, uri: URI, position?: vscode.ViewColumn, label?: string, options?: any): Promise<any> {
return executor.executeCommand('_workbench.previewHtml',
uri,
typeof position === 'number' && typeConverters.ViewColumn.from(position),
label,
options
);
}
interface IOpenFolderAPICommandOptions {
forceNewWindow?: boolean;
noRecentEntry?: boolean;
recentEntryLabel?: string;
}
CommandsRegistry.registerCommand(PreviewHTMLAPICommand.ID, adjustHandler(PreviewHTMLAPICommand.execute));
export class OpenFolderAPICommand {
public static ID = 'vscode.openFolder';
public static execute(executor: ICommandsExecutor, uri?: URI, forceNewWindow?: boolean): Promise<any> {
public static execute(executor: ICommandsExecutor, uri?: URI, forceNewWindow?: boolean): Promise<any>;
public static execute(executor: ICommandsExecutor, uri?: URI, options?: IOpenFolderAPICommandOptions): Promise<any>;
public static execute(executor: ICommandsExecutor, uri?: URI, arg: boolean | IOpenFolderAPICommandOptions = {}): Promise<any> {
if (typeof arg === 'boolean') {
arg = { forceNewWindow: arg };
}
if (!uri) {
return executor.executeCommand('_files.pickFolderAndOpen', forceNewWindow);
return executor.executeCommand('_files.pickFolderAndOpen', arg.forceNewWindow);
}
let correctedUri = isMalformedFileUri(uri);
if (correctedUri) {
// workaround for #55916 and #55891, will be removed in 1.28
console.warn(`'vscode.openFolder' command invoked with an invalid URI (file:// scheme missing): '${uri}'. Converted to a 'file://' URI: ${correctedUri}`);
uri = correctedUri;
const options: IOpenSettings = { forceNewWindow: arg.forceNewWindow };
if (arg.noRecentEntry) {
options.args = { _: [], 'skip-add-to-recently-opened': true };
}
return executor.executeCommand('_files.windowOpen', { folderURIs: [uri], forceNewWindow });
uri = URI.revive(uri);
return executor.executeCommand('_files.windowOpen', [{ uri, label: arg.recentEntryLabel }], options);
}
}
CommandsRegistry.registerCommand(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute));
CommandsRegistry.registerCommand({
id: OpenFolderAPICommand.ID,
handler: adjustHandler(OpenFolderAPICommand.execute),
description: {
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: any) => value === undefined || value instanceof URI },
{ name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Wheter the opened URI will appear in the \'Open Recent\' list. Defaults to true. `recentEntryLabel`: The label used for \'Open Recent\' list. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' }
]
}
});
export class DiffAPICommand {
public static ID = 'vscode.diff';
@@ -84,8 +85,8 @@ CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand
export class OpenAPICommand {
public static ID = 'vscode.open';
public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, label?: string): Promise<any> {
let options: ITextEditorOptions;
let position: EditorViewColumn;
let options: ITextEditorOptions | undefined;
let position: EditorViewColumn | undefined;
if (columnOrOptions) {
if (typeof columnOrOptions === 'number') {
@@ -106,15 +107,19 @@ export class OpenAPICommand {
}
CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute));
CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string) {
CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, uri: URI) {
const windowsService = accessor.get(IWindowsService);
return windowsService.removeFromRecentlyOpened([path]).then(() => undefined);
return windowsService.removeFromRecentlyOpened([uri]).then(() => undefined);
});
export class RemoveFromRecentlyOpenedAPICommand {
public static ID = 'vscode.removeFromRecentlyOpened';
public static execute(executor: ICommandsExecutor, path: string): Promise<any> {
public static execute(executor: ICommandsExecutor, path: string | URI): Promise<any> {
if (typeof path === 'string') {
path = path.match(/^[^:/?#]+:\/\//) ? URI.parse(path) : URI.file(path);
} else {
path = URI.revive(path); // called from extension host
}
return executor.executeCommand('_workbench.removeFromRecentlyOpened', path);
}
}
@@ -126,11 +131,33 @@ export class SetEditorLayoutAPICommand {
return executor.executeCommand('layoutEditorGroups', layout);
}
}
CommandsRegistry.registerCommand(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute));
CommandsRegistry.registerCommand({
id: SetEditorLayoutAPICommand.ID,
handler: adjustHandler(SetEditorLayoutAPICommand.execute),
description: {
description: 'Set Editor Layout',
args: [{
name: 'args',
schema: {
'type': 'object',
'required': ['groups'],
'properties': {
'orientation': {
'type': 'number',
'default': 0,
'enum': [0, 1]
},
'groups': {
'$ref': '#/definitions/editorGroupsSchema', // defined in keybindingService.ts ...
'default': [{}, {}],
}
}
}
}]
}
});
CommandsRegistry.registerCommand('_workbench.downloadResource', function (accessor: ServicesAccessor, resource: URI) {
const downloadService = accessor.get(IDownloadService);
const location = posix.join(tmpdir(), generateUuid());
return downloadService.download(resource, location).then(() => URI.file(location));
});
return downloadService.download(resource).then(location => URI.file(location));
});

View File

@@ -8,7 +8,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import * as paths from 'vs/base/common/paths';
import * as path from 'vs/base/common/path';
import * as platform from 'vs/base/common/platform';
import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
@@ -17,8 +17,8 @@ import { OverviewRulerLane } from 'vs/editor/common/model';
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
import { score } from 'vs/editor/common/modes/languageSelector';
import * as files from 'vs/platform/files/common/files';
import pkg from 'vs/platform/node/package';
import product from 'vs/platform/node/product';
import pkg from 'vs/platform/product/node/package';
import product from 'vs/platform/product/node/product';
import { ExtHostContext, IInitData, IMainContext, MainContext } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands';
import { ExtHostClipboard } from 'vs/workbench/api/node/extHostClipboard';
@@ -60,11 +60,13 @@ import { ExtHostUrls } from 'vs/workbench/api/node/extHostUrls';
import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview';
import { ExtHostWindow } from 'vs/workbench/api/node/extHostWindow';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { IExtensionDescription, throwProposedApiError, checkProposedApiEnabled, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { throwProposedApiError, checkProposedApiEnabled, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import * as vscode from 'vscode';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { originalFSPath } from 'vs/base/common/resources';
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -91,7 +93,7 @@ export function createApiFactory(
extHostStorage: ExtHostStorage
): IExtensionApiFactory {
let schemeTransformer: ISchemeTransformer | null = null;
const schemeTransformer: ISchemeTransformer | null = null;
// Addressable instances
rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService);
@@ -113,18 +115,22 @@ export function createApiFactory(
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors));
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService, extHostCommands));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService));
// {{SQL CARBON EDIT}}
// const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer, extHostLogService));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService));
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)));
const exthostCommentProviders = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands.converter, extHostDocuments));
const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, new ExtHostOutputService(initData.logsLocation, rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage);
if (initData.remoteAuthority) {
const cliServer = new CLIServer(extHostCommands);
process.env['VSCODE_IPC_HOOK_CLI'] = cliServer.ipcHandlePath;
}
// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = Object.keys(ExtHostContext).map((key) => (<any>ExtHostContext)[key]);
@@ -176,23 +182,6 @@ export function createApiFactory(
};
})();
// Warn when trying to use the vscode.previewHtml command as it does not work properly in all scenarios and
// has security concerns.
const checkCommand = (() => {
let done = !extension.isUnderDevelopment;
const informOnce = () => {
if (!done) {
done = true;
console.warn(`Extension '${extension.identifier.value}' uses the 'vscode.previewHtml' command which is deprecated and will be removed. Please update your extension to use the Webview API: https://go.microsoft.com/fwlink/?linkid=2039309`);
}
};
return (commandId: string) => {
if (commandId === 'vscode.previewHtml') {
informOnce();
}
return commandId;
};
})();
// namespace: commands
const commands: typeof vscode.commands = {
@@ -201,7 +190,7 @@ export function createApiFactory(
},
registerTextEditorCommand(id: string, callback: (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit, ...args: any[]) => void, thisArg?: any): vscode.Disposable {
return extHostCommands.registerCommand(true, id, (...args: any[]): any => {
let activeTextEditor = extHostEditors.getActiveTextEditor();
const activeTextEditor = extHostEditors.getActiveTextEditor();
if (!activeTextEditor) {
console.warn('Cannot execute ' + id + ' because there is no active text editor.');
return undefined;
@@ -221,8 +210,8 @@ export function createApiFactory(
});
},
registerDiffInformationCommand: proposedApiFunction(extension, (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => {
return extHostCommands.registerCommand(true, id, async (...args: any[]) => {
let activeTextEditor = extHostEditors.getActiveTextEditor();
return extHostCommands.registerCommand(true, id, async (...args: any[]): Promise<any> => {
const activeTextEditor = extHostEditors.getActiveTextEditor();
if (!activeTextEditor) {
console.warn('Cannot execute ' + id + ' because there is no active text editor.');
return undefined;
@@ -233,7 +222,7 @@ export function createApiFactory(
});
}),
executeCommand<T>(id: string, ...args: any[]): Thenable<T> {
return extHostCommands.executeCommand<T>(checkCommand(id), ...args);
return extHostCommands.executeCommand<T>(id, ...args);
},
getCommands(filterInternal: boolean = false): Thenable<string[]> {
return extHostCommands.getCommands(filterInternal);
@@ -244,9 +233,9 @@ export function createApiFactory(
const env: typeof vscode.env = Object.freeze({
get machineId() { return initData.telemetryInfo.machineId; },
get sessionId() { return initData.telemetryInfo.sessionId; },
get language() { return platform.language; },
get language() { return platform.language!; },
get appName() { return product.nameLong; },
get appRoot() { return initData.environment.appRoot.fsPath; },
get appRoot() { return initData.environment.appRoot!.fsPath; },
get logLevel() {
checkProposedApiEnabled(extension);
return typeConverters.LogLevel.to(extHostLogService.getLevel());
@@ -265,8 +254,8 @@ export function createApiFactory(
// namespace: extensions
const extensions: typeof vscode.extensions = {
getExtension(extensionId: string): Extension<any> {
let desc = extensionRegistry.getExtensionDescription(extensionId);
getExtension(extensionId: string): Extension<any> | undefined {
const desc = extensionRegistry.getExtensionDescription(extensionId);
if (desc) {
return new Extension(extensionService, desc);
}
@@ -306,6 +295,10 @@ export function createApiFactory(
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
},
registerCodeInsetProvider(selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerCodeInsetProvider(extension, checkSelector(selector), provider);
},
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider);
},
@@ -426,6 +419,9 @@ export function createApiFactory(
onDidChangeActiveTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidChangeActiveTerminal(listener, thisArg, disposables);
},
onDidChangeTerminalDimensions(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidChangeTerminalDimensions(listener, thisArg, disposables);
},
get state() {
return extHostWindow.state;
},
@@ -442,7 +438,7 @@ export function createApiFactory(
return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest);
},
showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken): any {
return extHostQuickOpen.showQuickPick(items, extension.enableProposedApi, options, token);
return extHostQuickOpen.showQuickPick(items, !!extension.enableProposedApi, options, token);
},
showWorkspaceFolderPick(options: vscode.WorkspaceFolderPickOptions) {
return extHostQuickOpen.showWorkspaceFolderPick(options);
@@ -473,17 +469,17 @@ export function createApiFactory(
return extHostOutputService.createOutputChannel(name);
},
createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
return extHostWebviews.createWebview(extension, viewType, title, showOptions, options);
return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options);
},
createTerminal(nameOrOptions: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
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);
},
createTerminalRenderer: proposedApiFunction(extension, (name: string) => {
createTerminalRenderer(name: string): vscode.TerminalRenderer {
return extHostTerminalService.createTerminalRenderer(name);
}),
},
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension);
},
@@ -500,7 +496,7 @@ export function createApiFactory(
return extHostUrls.registerUriHandler(extension.identifier, handler);
},
createQuickPick<T extends vscode.QuickPickItem>(): vscode.QuickPick<T> {
return extHostQuickOpen.createQuickPick(extension.identifier, extension.enableProposedApi);
return extHostQuickOpen.createQuickPick(extension.identifier, !!extension.enableProposedApi);
},
createInputBox(): vscode.InputBox {
return extHostQuickOpen.createInputBox(extension.identifier);
@@ -533,7 +529,7 @@ export function createApiFactory(
onDidChangeWorkspaceFolders: function (listener, thisArgs?, disposables?) {
return extHostWorkspace.onDidChangeWorkspace(listener, thisArgs, disposables);
},
asRelativePath: (pathOrUri, includeWorkspace) => {
asRelativePath: (pathOrUri, includeWorkspace?) => {
return extHostWorkspace.getRelativePath(pathOrUri, includeWorkspace);
},
findFiles: (include, exclude, maxResults?, token?) => {
@@ -572,7 +568,7 @@ export function createApiFactory(
openTextDocument(uriOrFileNameOrOptions?: vscode.Uri | string | { language?: string; content?: string; }) {
let uriPromise: Thenable<URI>;
let options = uriOrFileNameOrOptions as { language?: string; content?: string; };
const options = uriOrFileNameOrOptions as { language?: string; content?: string; };
if (typeof uriOrFileNameOrOptions === 'string') {
uriPromise = Promise.resolve(URI.file(uriOrFileNameOrOptions));
} else if (uriOrFileNameOrOptions instanceof URI) {
@@ -585,8 +581,7 @@ export function createApiFactory(
return uriPromise.then(uri => {
return extHostDocuments.ensureDocumentData(uri).then(() => {
const data = extHostDocuments.getDocumentData(uri);
return data && data.document;
return extHostDocuments.getDocument(uri);
});
});
},
@@ -631,14 +626,17 @@ export function createApiFactory(
registerTextSearchProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostSearch.registerTextSearchProvider(scheme, provider);
}),
registerFileIndexProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostSearch.registerFileIndexProvider(scheme, provider);
}),
registerDocumentCommentProvider: proposedApiFunction(extension, (provider: vscode.DocumentCommentProvider) => {
return exthostCommentProviders.registerDocumentCommentProvider(extension.identifier, provider);
return extHostComment.registerDocumentCommentProvider(extension.identifier, provider);
}),
registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => {
return exthostCommentProviders.registerWorkspaceCommentProvider(extension.identifier, provider);
return extHostComment.registerWorkspaceCommentProvider(extension.identifier, provider);
}),
registerRemoteAuthorityResolver: proposedApiFunction(extension, (authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver) => {
return extensionService.registerRemoteAuthorityResolver(authorityPrefix, resolver);
}),
registerResourceLabelFormatter: proposedApiFunction(extension, (formatter: vscode.ResourceLabelFormatter) => {
return extHostFileSystem.registerResourceLabelFormatter(formatter);
}),
onDidRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables);
@@ -651,13 +649,19 @@ export function createApiFactory(
// namespace: scm
const scm: typeof vscode.scm = {
get inputBox() {
return extHostSCM.getLastInputBox(extension);
return extHostSCM.getLastInputBox(extension)!; // Strict null override - Deprecated api
},
createSourceControl(id: string, label: string, rootUri?: vscode.Uri) {
return extHostSCM.createSourceControl(extension, id, label, rootUri);
}
};
const comment: typeof vscode.comment = {
createCommentController(id: string, label: string) {
return extHostComment.createCommentController(extension, id, label);
}
};
// {{SQL CARBON EDIT}} -- no-op debug extensibility API
// namespace: debug
const debug: typeof vscode.debug = {
@@ -694,7 +698,7 @@ export function createApiFactory(
registerDebugAdapterTrackerFactory(debugType: string, factory: vscode.DebugAdapterTrackerFactory) {
return undefined;
},
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration) {
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession) {
return undefined;
},
addBreakpoints(breakpoints: vscode.Breakpoint[]) {
@@ -742,6 +746,7 @@ export function createApiFactory(
extensions,
languages,
scm,
comment,
tasks,
window,
workspace,
@@ -752,6 +757,7 @@ export function createApiFactory(
CodeActionKind: extHostTypes.CodeActionKind,
CodeActionTrigger: extHostTypes.CodeActionTrigger,
CodeLens: extHostTypes.CodeLens,
CodeInset: extHostTypes.CodeInset,
Color: extHostTypes.Color,
ColorInformation: extHostTypes.ColorInformation,
ColorPresentation: extHostTypes.ColorPresentation,
@@ -775,6 +781,7 @@ export function createApiFactory(
DocumentSymbol: extHostTypes.DocumentSymbol,
EndOfLine: extHostTypes.EndOfLine,
EventEmitter: Emitter,
CustomExecution: extHostTypes.CustomExecution,
FileChangeType: extHostTypes.FileChangeType,
FileSystemError: extHostTypes.FileSystemError,
FileType: files.FileType,
@@ -794,9 +801,9 @@ export function createApiFactory(
QuickInputButtons: extHostTypes.QuickInputButtons,
Range: extHostTypes.Range,
RelativePattern: extHostTypes.RelativePattern,
ResolvedAuthority: extHostTypes.ResolvedAuthority,
Selection: extHostTypes.Selection,
SelectionRange: extHostTypes.SelectionRange,
SelectionRangeKind: extHostTypes.SelectionRangeKind,
ShellExecution: extHostTypes.ShellExecution,
ShellQuoting: extHostTypes.ShellQuoting,
SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind,
@@ -833,18 +840,6 @@ export function createApiFactory(
};
}
/**
* Returns the original fs path (using the original casing for the drive letter)
*/
export function originalFSPath(uri: URI): string {
const result = uri.fsPath;
if (/^[a-zA-Z]:/.test(result) && uri.path.charAt(1).toLowerCase() === result.charAt(0)) {
// Restore original drive letter casing
return uri.path.charAt(1) + result.substr(1);
}
return result;
}
class Extension<T> implements vscode.Extension<T> {
private _extensionService: ExtHostExtensionService;
@@ -852,13 +847,13 @@ class Extension<T> implements vscode.Extension<T> {
public id: string;
public extensionPath: string;
public packageJSON: any;
public packageJSON: IExtensionDescription;
constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription) {
this._extensionService = extensionService;
this._identifier = description.identifier;
this.id = description.identifier.value;
this.extensionPath = paths.normalize(originalFSPath(description.extensionLocation), true);
this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));
this.packageJSON = description;
}
@@ -867,6 +862,9 @@ class Extension<T> implements vscode.Extension<T> {
}
get exports(): T {
if (this.packageJSON.api === 'none') {
return undefined!; // Strict nulloverride - Public api
}
return <T>this._extensionService.getExtensionExports(this._identifier);
}

View File

@@ -25,7 +25,7 @@ import { ResourceLabelFormatter } from 'vs/platform/label/common/label';
import { LogLevel } from 'vs/platform/log/common/log';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { IPickOptions, IQuickInputButton, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IPatternInfo, IRawFileMatch2, IRawQuery, IRawTextQuery, ISearchCompleteStats } from 'vs/platform/search/common/search';
import { IPatternInfo, IRawFileMatch2, IRawQuery, IRawTextQuery, ISearchCompleteStats } from 'vs/workbench/services/search/common/search';
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
@@ -33,44 +33,50 @@ import { EndOfLine, IFileOperationOptions, TextEditorLineNumbersStyle } from 'vs
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { TaskDTO, TaskExecutionDTO, TaskFilterDTO, TaskHandleDTO, TaskProcessEndedDTO, TaskProcessStartedDTO, TaskSystemInfoDTO, TaskSetDTO } from 'vs/workbench/api/shared/tasks';
import { ITreeItem, IRevealOptions } from 'vs/workbench/common/views';
import { IAdapterDescriptor, IConfig, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug';
import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder';
import { ITerminalDimensions } from 'vs/workbench/parts/terminal/common/terminal';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IAdapterDescriptor, IConfig, ITerminalSettings } from 'vs/workbench/contrib/debug/common/debug';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import { ITerminalDimensions } from 'vs/workbench/contrib/terminal/common/terminal';
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
import { IRPCProtocol, createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IRemoteConsoleLog } from 'vs/base/node/console';
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
// {{SQL CARBON EDIT}}
import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
appRoot: URI;
appSettingsHome: URI;
extensionDevelopmentLocationURI: URI;
extensionTestsPath: string;
appRoot?: URI;
appSettingsHome?: URI;
extensionDevelopmentLocationURI?: URI;
extensionTestsLocationURI?: URI;
globalStorageHome: URI;
userHome: URI;
}
export interface IWorkspaceData {
export interface IStaticWorkspaceData {
id: string;
name: string;
configuration?: UriComponents | null;
}
export interface IWorkspaceData extends IStaticWorkspaceData {
folders: { uri: UriComponents, name: string, index: number }[];
configuration?: UriComponents;
}
export interface IInitData {
commit: string;
commit?: string;
parentPid: number;
environment: IEnvironment;
workspace: IWorkspaceData;
workspace?: IStaticWorkspaceData | null;
resolvedExtensions: ExtensionIdentifier[];
hostExtensions: ExtensionIdentifier[];
extensions: IExtensionDescription[];
telemetryInfo: ITelemetryInfo;
logLevel: LogLevel;
@@ -105,7 +111,7 @@ export interface MainThreadClipboardShape extends IDisposable {
export interface MainThreadCommandsShape extends IDisposable {
$registerCommand(id: string): void;
$unregisterCommand(id: string): void;
$executeCommand<T>(id: string, args: any[]): Promise<T>;
$executeCommand<T>(id: string, args: any[]): Promise<T | undefined>;
$getCommands(): Promise<string[]>;
}
@@ -113,10 +119,22 @@ export interface CommentProviderFeatures {
startDraftLabel?: string;
deleteDraftLabel?: string;
finishDraftLabel?: string;
reactionGroup?: vscode.CommentReaction[];
reactionGroup?: modes.CommentReaction[];
}
export interface MainThreadCommentsShape extends IDisposable {
$registerCommentController(handle: number, id: string, label: string): void;
$unregisterCommentController(handle: number): void;
$updateCommentControllerFeatures(handle: number, features: CommentProviderFeatures): void;
$createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, comments: modes.Comment[], acceptInputCommand: modes.Command | undefined, additionalCommands: modes.Command[], collapseState: modes.CommentThreadCollapsibleState): modes.CommentThread2 | undefined;
$deleteCommentThread(handle: number, commentThreadHandle: number): void;
$updateComments(handle: number, commentThreadHandle: number, comments: modes.Comment[]): void;
$setInputValue(handle: number, input: string): void;
$updateCommentThreadAcceptInputCommand(handle: number, commentThreadHandle: number, acceptInputCommand: modes.Command): void;
$updateCommentThreadAdditionalCommands(handle: number, commentThreadHandle: number, additionalCommands: modes.Command[]): void;
$updateCommentThreadCollapsibleState(handle: number, commentThreadHandle: number, collapseState: modes.CommentThreadCollapsibleState): void;
$updateCommentThreadRange(handle: number, commentThreadHandle: number, range: IRange): void;
$updateCommentThreadLabel(handle: number, commentThreadHandle: number, label: string): void;
$registerDocumentCommentProvider(handle: number, features: CommentProviderFeatures): void;
$unregisterDocumentCommentProvider(handle: number): void;
$registerWorkspaceCommentProvider(handle: number, extensionId: ExtensionIdentifier): void;
@@ -125,12 +143,12 @@ export interface MainThreadCommentsShape extends IDisposable {
}
export interface MainThreadConfigurationShape extends IDisposable {
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: UriComponents): Promise<void>;
$removeConfigurationOption(target: ConfigurationTarget, key: string, resource: UriComponents): Promise<void>;
$updateConfigurationOption(target: ConfigurationTarget | null, key: string, value: any, resource: UriComponents | undefined): Promise<void>;
$removeConfigurationOption(target: ConfigurationTarget | null, key: string, resource: UriComponents | undefined): Promise<void>;
}
export interface MainThreadDiagnosticsShape extends IDisposable {
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void;
$changeMany(owner: string, entries: [UriComponents, IMarkerData[] | undefined][]): void;
$clear(owner: string): void;
}
@@ -150,14 +168,14 @@ export interface MainThreadDialogSaveOptions {
}
export interface MainThreadDiaglogsShape extends IDisposable {
$showOpenDialog(options: MainThreadDialogOpenOptions): Promise<UriComponents[]>;
$showSaveDialog(options: MainThreadDialogSaveOptions): Promise<UriComponents>;
$showOpenDialog(options: MainThreadDialogOpenOptions): Promise<UriComponents[] | undefined>;
$showSaveDialog(options: MainThreadDialogSaveOptions): Promise<UriComponents | undefined>;
}
export interface MainThreadDecorationsShape extends IDisposable {
$registerDecorationProvider(handle: number, label: string): void;
$unregisterDecorationProvider(handle: number): void;
$onDidChange(handle: number, resources: UriComponents[]): void;
$onDidChange(handle: number, resources: UriComponents[] | null): void;
}
export interface MainThreadDocumentContentProvidersShape extends IDisposable {
@@ -174,6 +192,7 @@ export interface MainThreadDocumentsShape extends IDisposable {
export interface ITextEditorConfigurationUpdate {
tabSize?: number | 'auto';
indentSize?: number | 'tabSize';
insertSpaces?: boolean | 'auto';
cursorStyle?: TextEditorCursorStyle;
lineNumbers?: TextEditorLineNumbersStyle;
@@ -181,6 +200,7 @@ export interface ITextEditorConfigurationUpdate {
export interface IResolvedTextEditorConfiguration {
tabSize: number;
indentSize: number;
insertSpaces: boolean;
cursorStyle: TextEditorCursorStyle;
lineNumbers: TextEditorLineNumbersStyle;
@@ -210,7 +230,7 @@ export interface ITextDocumentShowOptions {
}
export interface MainThreadTextEditorsShape extends IDisposable {
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise<string>;
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise<string | undefined>;
$registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void;
$removeTextEditorDecorationType(key: string): void;
$tryShowEditor(id: string, position: EditorViewColumn): Promise<void>;
@@ -297,7 +317,8 @@ export interface ISerializedSignatureHelpProviderMetadata {
export interface MainThreadLanguageFeaturesShape extends IDisposable {
$unregister(handle: number): void;
$registerDocumentSymbolProvider(handle: number, selector: ISerializedDocumentFilter[], label: string): void;
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void;
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void;
$registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void;
$emitCodeLensEvent(eventHandle: number, event?: any): void;
$registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
@@ -307,9 +328,9 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$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[], label: string): void;
$registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], label: string): void;
$registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[]): void;
$registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier): void;
$registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: ExtensionIdentifier): void;
$registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[], extensionId: ExtensionIdentifier): void;
$registerNavigateTypeSupport(handle: number): void;
$registerRenameSupport(handle: number, selector: ISerializedDocumentFilter[], supportsResolveInitialValues: boolean): void;
$registerSuggestSupport(handle: number, selector: ISerializedDocumentFilter[], triggerCharacters: string[], supportsResolveDetails: boolean): void;
@@ -332,17 +353,17 @@ export interface MainThreadMessageOptions {
}
export interface MainThreadMessageServiceShape extends IDisposable {
$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Promise<number>;
$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Promise<number | undefined>;
}
export interface MainThreadOutputServiceShape extends IDisposable {
$register(label: string, log: boolean, file?: UriComponents): Promise<string>;
$append(channelId: string, value: string): Promise<void>;
$update(channelId: string): Promise<void>;
$clear(channelId: string, till: number): Promise<void>;
$reveal(channelId: string, preserveFocus: boolean): Promise<void>;
$close(channelId: string): Promise<void>;
$dispose(channelId: string): Promise<void>;
$append(channelId: string, value: string): Promise<void> | undefined;
$update(channelId: string): Promise<void> | undefined;
$clear(channelId: string, till: number): Promise<void> | undefined;
$reveal(channelId: string, preserveFocus: boolean): Promise<void> | undefined;
$close(channelId: string): Promise<void> | undefined;
$dispose(channelId: string): Promise<void> | undefined;
}
export interface MainThreadProgressShape extends IDisposable {
@@ -353,7 +374,7 @@ export interface MainThreadProgressShape extends IDisposable {
}
export interface MainThreadTerminalServiceShape extends IDisposable {
$createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string | URI, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }>;
$createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string | UriComponents, env?: { [key: string]: string | null }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }>;
$createTerminalRenderer(name: string): Promise<number>;
$dispose(terminalId: number): void;
$hide(terminalId: number): void;
@@ -442,21 +463,21 @@ export interface TransferInputBox extends BaseTransferQuickInput {
}
export interface MainThreadQuickOpenShape extends IDisposable {
$show(instance: number, options: IPickOptions<TransferQuickPickItems>, token: CancellationToken): Promise<number | number[]>;
$show(instance: number, options: IPickOptions<TransferQuickPickItems>, token: CancellationToken): Promise<number | number[] | undefined>;
$setItems(instance: number, items: TransferQuickPickItems[]): Promise<void>;
$setError(instance: number, error: Error): Promise<void>;
$input(options: vscode.InputBoxOptions, validateInput: boolean, token: CancellationToken): Promise<string>;
$input(options: vscode.InputBoxOptions | undefined, validateInput: boolean, token: CancellationToken): Promise<string>;
$createOrUpdate(params: TransferQuickInput): Promise<void>;
$dispose(id: number): Promise<void>;
}
export interface MainThreadStatusBarShape extends IDisposable {
$setEntry(id: number, extensionId: ExtensionIdentifier, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void;
$setEntry(id: number, extensionId: ExtensionIdentifier | undefined, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number | undefined): void;
$dispose(id: number): void;
}
export interface MainThreadStorageShape extends IDisposable {
$getValue<T>(shared: boolean, key: string): Promise<T>;
$getValue<T>(shared: boolean, key: string): Promise<T | undefined>;
$setValue(shared: boolean, key: string, value: object): Promise<void>;
}
@@ -466,6 +487,8 @@ export interface MainThreadTelemetryShape extends IDisposable {
export type WebviewPanelHandle = string;
export type WebviewInsetHandle = number;
export interface WebviewPanelShowOptions {
readonly viewColumn?: EditorViewColumn;
readonly preserveFocus?: boolean;
@@ -473,13 +496,15 @@ export interface WebviewPanelShowOptions {
export interface MainThreadWebviewsShape extends IDisposable {
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void;
$createWebviewCodeInset(handle: WebviewInsetHandle, symbolId: string, options: vscode.WebviewOptions, extensionLocation: UriComponents | undefined): void;
$disposeWebview(handle: WebviewPanelHandle): void;
$reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void;
$setTitle(handle: WebviewPanelHandle, value: string): void;
$setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void;
$setHtml(handle: WebviewPanelHandle, value: string): void;
$setOptions(handle: WebviewPanelHandle, options: vscode.WebviewOptions): void;
$postMessage(handle: WebviewPanelHandle, value: any): Promise<boolean>;
$setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void;
$setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: vscode.WebviewOptions): void;
$postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, value: any): Promise<boolean>;
$registerSerializer(viewType: string): void;
$unregisterSerializer(viewType: string): void;
@@ -508,12 +533,12 @@ export interface ExtHostUrlsShape {
}
export interface MainThreadWorkspaceShape extends IDisposable {
$startFileSearch(includePattern: string, includeFolder: URI, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise<UriComponents[]>;
$startFileSearch(includePattern: string | undefined, includeFolder: UriComponents | undefined, excludePatternOrDisregardExcludes: string | false | undefined, maxResults: number | undefined, token: CancellationToken): Promise<UriComponents[] | undefined>;
$startTextSearch(query: IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<vscode.TextSearchComplete>;
$checkExists(includes: string[], token: CancellationToken): Promise<boolean>;
$saveAll(includeUntitled?: boolean): Promise<boolean>;
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Promise<void>;
$resolveProxy(url: string): Promise<string>;
$resolveProxy(url: string): Promise<string | undefined>;
}
export interface IFileChangeDto {
@@ -524,14 +549,14 @@ export interface IFileChangeDto {
export interface MainThreadFileSystemShape extends IDisposable {
$registerFileSystemProvider(handle: number, scheme: string, capabilities: FileSystemProviderCapabilities): void;
$unregisterProvider(handle: number): void;
$setUriFormatter(formatter: ResourceLabelFormatter): void;
$registerResourceLabelFormatter(handle: number, formatter: ResourceLabelFormatter): void;
$unregisterResourceLabelFormatter(handle: number): void;
$onFileSystemChange(handle: number, resource: IFileChangeDto[]): void;
}
export interface MainThreadSearchShape extends IDisposable {
$registerFileSearchProvider(handle: number, scheme: string): void;
$registerTextSearchProvider(handle: number, scheme: string): void;
$registerFileIndexProvider(handle: number, scheme: string): void;
$unregisterProvider(handle: number): void;
$handleFileMatch(handle: number, session: number, data: UriComponents[]): void;
$handleTextMatch(handle: number, session: number, data: IRawFileMatch2[]): void;
@@ -539,21 +564,22 @@ export interface MainThreadSearchShape extends IDisposable {
}
export interface MainThreadTaskShape extends IDisposable {
$createTaskId(task: TaskDTO): Promise<string>;
$registerTaskProvider(handle: number): Promise<void>;
$unregisterTaskProvider(handle: number): Promise<void>;
$fetchTasks(filter?: TaskFilterDTO): Promise<TaskDTO[]>;
$executeTask(task: TaskHandleDTO | TaskDTO): Promise<TaskExecutionDTO>;
$terminateTask(id: string): Promise<void>;
$registerTaskSystem(scheme: string, info: TaskSystemInfoDTO): void;
$customExecutionComplete(id: string, result?: number): Promise<void>;
}
export interface MainThreadExtensionServiceShape extends IDisposable {
$localShowMessage(severity: Severity, msg: string): void;
$activateExtension(extensionId: ExtensionIdentifier, activationEvent: string | null): Promise<void>;
$onWillActivateExtension(extensionId: ExtensionIdentifier): void;
$onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void;
$onExtensionActivationFailed(extensionId: ExtensionIdentifier): void;
$onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string | null): void;
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise<void>;
$onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void;
$addMessage(extensionId: ExtensionIdentifier, severity: Severity, message: string): void;
}
export interface SCMProviderFeatures {
@@ -561,7 +587,7 @@ export interface SCMProviderFeatures {
count?: number;
commitTemplate?: string;
acceptInputCommand?: modes.Command;
statusBarCommands?: modes.Command[];
statusBarCommands?: CommandDto[];
}
export interface SCMGroupFeatures {
@@ -614,16 +640,17 @@ export type DebugSessionUUID = string;
export interface MainThreadDebugServiceShape extends IDisposable {
$registerDebugTypes(debugTypes: string[]): void;
$sessionCached(sessionID: string): void;
$acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
$acceptDAError(handle: number, name: string, message: string, stack: string): void;
$acceptDAExit(handle: number, code: number, signal: string): void;
$acceptDAError(handle: number, name: string, message: string, stack: string | undefined): void;
$acceptDAExit(handle: number, code: number | undefined, signal: string | undefined): void;
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasProvideDaMethod: boolean, handle: number): Promise<void>;
$registerDebugAdapterDescriptorFactory(type: string, handle: number): Promise<void>;
$registerDebugAdapterTrackerFactory(type: string, handle: number);
$unregisterDebugConfigurationProvider(handle: number): void;
$unregisterDebugAdapterDescriptorFactory(handle: number): void;
$unregisterDebugAdapterTrackerFactory(handle: number): void;
$startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration): Promise<boolean>;
$startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSessionID: string | undefined): Promise<boolean>;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise<any>;
$appendDebugConsole(value: string): void;
$startBreakpointEvents(): void;
@@ -653,7 +680,7 @@ export interface ExtHostDiagnosticsShape {
}
export interface ExtHostDocumentContentProvidersShape {
$provideTextDocumentContent(handle: number, uri: UriComponents): Promise<string>;
$provideTextDocumentContent(handle: number, uri: UriComponents): Promise<string | null | undefined>;
}
export interface IModelAddedData {
@@ -681,7 +708,7 @@ export interface ITextEditorAddData {
options: IResolvedTextEditorConfiguration;
selections: ISelection[];
visibleRanges: IRange[];
editorPosition: EditorViewColumn;
editorPosition: EditorViewColumn | undefined;
}
export interface ITextEditorPositionData {
[id: string]: EditorViewColumn;
@@ -706,7 +733,7 @@ export interface IDocumentsAndEditorsDelta {
addedDocuments?: IModelAddedData[];
removedEditors?: string[];
addedEditors?: ITextEditorAddData[];
newActiveEditor?: string;
newActiveEditor?: string | null;
}
export interface ExtHostDocumentsAndEditorsShape {
@@ -722,7 +749,8 @@ export interface ExtHostTreeViewsShape {
}
export interface ExtHostWorkspaceShape {
$acceptWorkspaceData(workspace: IWorkspaceData): void;
$initializeWorkspace(workspace: IWorkspaceData | null): void;
$acceptWorkspaceData(workspace: IWorkspaceData | null): void;
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void;
}
@@ -753,7 +781,7 @@ export interface ExtHostExtensionServiceShape {
$resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority>;
$startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
$activateByEvent(activationEvent: string): Promise<void>;
$activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise<void>;
$activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise<boolean>;
$deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void>;
@@ -774,7 +802,7 @@ export interface ExtHostFileSystemEventServiceShape {
}
export interface ObjectIdentifier {
$ident: number;
$ident?: number;
}
export namespace ObjectIdentifier {
@@ -839,9 +867,9 @@ export interface WorkspaceSymbolsDto extends IdObject {
}
export interface ResourceFileEditDto {
oldUri: UriComponents;
newUri: UriComponents;
options: IFileOperationOptions;
oldUri?: UriComponents;
newUri?: UriComponents;
options?: IFileOperationOptions;
}
export interface ResourceTextEditDto {
@@ -857,7 +885,7 @@ export interface WorkspaceEditDto {
rejectReason?: string;
}
export function reviveWorkspaceEditDto(data: WorkspaceEditDto): modes.WorkspaceEdit {
export function reviveWorkspaceEditDto(data: WorkspaceEditDto | undefined): modes.WorkspaceEdit {
if (data && data.edits) {
for (const edit of data.edits) {
if (typeof (<ResourceTextEditDto>edit).resource === 'object') {
@@ -871,50 +899,67 @@ export function reviveWorkspaceEditDto(data: WorkspaceEditDto): modes.WorkspaceE
return <modes.WorkspaceEdit>data;
}
export type CommandDto = ObjectIdentifier & modes.Command;
export interface CodeActionDto {
title: string;
edit?: WorkspaceEditDto;
diagnostics?: IMarkerData[];
command?: modes.Command;
command?: CommandDto;
kind?: string;
isPreferred?: boolean;
}
export interface LinkDto extends ObjectIdentifier {
range: IRange;
url?: string | UriComponents;
}
export interface CodeLensDto extends ObjectIdentifier {
range: IRange;
id?: string;
command?: CommandDto;
}
export type CodeInsetDto = ObjectIdentifier & codeInset.ICodeInsetSymbol;
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[]>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.ICodeLensSymbol[]>;
$resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol>;
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensDto[]>;
$resolveCodeLens(handle: number, resource: UriComponents, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined>;
$provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeInsetDto[] | undefined>;
$resolveCodeInset(handle: number, resource: UriComponents, symbol: CodeInsetDto, token: CancellationToken): Promise<CodeInsetDto>;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.Hover>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DocumentHighlight[]>;
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<LocationDto[]>;
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionDto[]>;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]>;
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]>;
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]>;
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.Hover | undefined>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DocumentHighlight[] | undefined>;
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<LocationDto[] | undefined>;
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionDto[] | undefined>;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[] | undefined>;
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise<WorkspaceSymbolsDto>;
$resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto, token: CancellationToken): Promise<WorkspaceSymbolDto>;
$resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto, token: CancellationToken): Promise<WorkspaceSymbolDto | undefined>;
$releaseWorkspaceSymbols(handle: number, id: number): void;
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<WorkspaceEditDto>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation>;
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<SuggestResultDto>;
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<WorkspaceEditDto | undefined>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation | undefined>;
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<SuggestResultDto | undefined>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.CompletionItem, token: CancellationToken): Promise<modes.CompletionItem>;
$releaseCompletionItems(handle: number, id: number): void;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<modes.SignatureHelp>;
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.ILink[]>;
$resolveDocumentLink(handle: number, link: modes.ILink, token: CancellationToken): Promise<modes.ILink>;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<modes.SignatureHelp | undefined>;
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<LinkDto[] | undefined>;
$resolveDocumentLink(handle: number, link: LinkDto, token: CancellationToken): Promise<LinkDto | undefined>;
$provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): Promise<IRawColorInfo[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo, token: CancellationToken): Promise<modes.IColorPresentation[]>;
$provideFoldingRanges(handle: number, resource: UriComponents, context: modes.FoldingContext, token: CancellationToken): Promise<modes.FoldingRange[]>;
$provideSelectionRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.SelectionRange[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo, token: CancellationToken): Promise<modes.IColorPresentation[] | undefined>;
$provideFoldingRanges(handle: number, resource: UriComponents, context: modes.FoldingContext, token: CancellationToken): Promise<modes.FoldingRange[] | undefined>;
$provideSelectionRanges(handle: number, resource: UriComponents, positions: IPosition[], token: CancellationToken): Promise<modes.SelectionRange[][]>;
}
export interface ExtHostQuickOpenShape {
$onItemSelected(handle: number): void;
$validateInput(input: string): Promise<string>;
$validateInput(input: string): Promise<string | null | undefined>;
$onDidChangeActive(sessionId: number, handles: number[]): void;
$onDidChangeSelection(sessionId: number, handles: number[]): void;
$onDidAccept(sessionId: number): void;
@@ -927,8 +972,8 @@ export interface ShellLaunchConfigDto {
name?: string;
executable?: string;
args?: string[] | string;
cwd?: string | URI;
env?: { [key: string]: string };
cwd?: string | UriComponents;
env?: { [key: string]: string | null };
}
export interface ExtHostTerminalServiceShape {
@@ -939,8 +984,8 @@ export interface ExtHostTerminalServiceShape {
$acceptTerminalProcessData(id: number, data: string): void;
$acceptTerminalRendererInput(id: number, data: string): void;
$acceptTerminalTitleChange(id: number, name: string): void;
$acceptTerminalRendererDimensions(id: number, cols: number, rows: number): void;
$createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUri: URI, cols: number, rows: number): void;
$acceptTerminalDimensions(id: number, cols: number, rows: number): void;
$createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents, cols: number, rows: number): void;
$acceptProcessInput(id: number, data: string): void;
$acceptProcessResize(id: number, cols: number, rows: number): void;
$acceptProcessShutdown(id: number, immediate: boolean): void;
@@ -949,7 +994,7 @@ export interface ExtHostTerminalServiceShape {
}
export interface ExtHostSCMShape {
$provideOriginalResource(sourceControlHandle: number, uri: UriComponents, token: CancellationToken): Promise<UriComponents>;
$provideOriginalResource(sourceControlHandle: number, uri: UriComponents, token: CancellationToken): Promise<UriComponents | null>;
$onInputBoxValueChange(sourceControlHandle: number, value: string): void;
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): Promise<void>;
$validateInput(sourceControlHandle: number, value: string, cursorPosition: number): Promise<[string, number] | undefined>;
@@ -958,7 +1003,7 @@ export interface ExtHostSCMShape {
export interface ExtHostTaskShape {
$provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<TaskSetDTO>;
$onDidStartTask(execution: TaskExecutionDTO): void;
$onDidStartTask(execution: TaskExecutionDTO, terminalId: number): void;
$onDidStartTaskProcess(value: TaskProcessStartedDTO): void;
$onDidEndTaskProcess(value: TaskProcessEndedDTO): void;
$OnDidEndTask(execution: TaskExecutionDTO): void;
@@ -1024,13 +1069,13 @@ export interface ExtHostDebugServiceShape {
$startDASession(handle: number, session: IDebugSessionDto): Promise<void>;
$stopDASession(handle: number): Promise<void>;
$sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): void;
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): Promise<IConfig>;
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): Promise<IConfig | null | undefined>;
$provideDebugConfigurations(handle: number, folder: UriComponents | undefined): Promise<IConfig[]>;
$legacyDebugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): Promise<IAdapterDescriptor>; // TODO@AW legacy
$provideDebugAdapter(handle: number, session: IDebugSessionDto): Promise<IAdapterDescriptor>;
$acceptDebugSessionStarted(session: IDebugSessionDto): void;
$acceptDebugSessionTerminated(session: IDebugSessionDto): void;
$acceptDebugSessionActiveChanged(session: IDebugSessionDto): void;
$acceptDebugSessionActiveChanged(session: IDebugSessionDto | undefined): void;
$acceptDebugSessionCustomEvent(session: IDebugSessionDto, event: any): void;
$acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void;
}
@@ -1067,9 +1112,14 @@ export interface ExtHostProgressShape {
}
export interface ExtHostCommentsShape {
$provideDocumentComments(handle: number, document: UriComponents): Promise<modes.CommentInfo>;
$createNewCommentThread(handle: number, document: UriComponents, range: IRange, text: string): Promise<modes.CommentThread>;
$replyToCommentThread(handle: number, document: UriComponents, range: IRange, commentThread: modes.CommentThread, text: string): Promise<modes.CommentThread>;
$provideDocumentComments(handle: number, document: UriComponents): Promise<modes.CommentInfo | null>;
$createNewCommentThread(handle: number, document: UriComponents, range: IRange, text: string): Promise<modes.CommentThread | null>;
$onCommentWidgetInputChange(commentControllerHandle: number, input: string | undefined): Promise<number | undefined>;
$provideCommentingRanges(commentControllerHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise<IRange[] | undefined>;
$provideReactionGroup(commentControllerHandle: number): Promise<modes.CommentReaction[] | undefined>;
$toggleReaction(commentControllerHandle: number, threadHandle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void>;
$createNewCommentWidgetCallback(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, token: CancellationToken): void;
$replyToCommentThread(handle: number, document: UriComponents, range: IRange, commentThread: modes.CommentThread, text: string): Promise<modes.CommentThread | null>;
$editComment(handle: number, document: UriComponents, comment: modes.Comment, text: string): Promise<void>;
$deleteComment(handle: number, document: UriComponents, comment: modes.Comment): Promise<void>;
$startDraft(handle: number, document: UriComponents): Promise<void>;
@@ -1077,11 +1127,11 @@ export interface ExtHostCommentsShape {
$finishDraft(handle: number, document: UriComponents): Promise<void>;
$addReaction(handle: number, document: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void>;
$deleteReaction(handle: number, document: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void>;
$provideWorkspaceComments(handle: number): Promise<modes.CommentThread[]>;
$provideWorkspaceComments(handle: number): Promise<modes.CommentThread[] | null>;
}
export interface ExtHostStorageShape {
$acceptValue(shared: boolean, key: string, value: object): void;
$acceptValue(shared: boolean, key: string, value: object | undefined): void;
}
// --- proxy identifiers

View File

@@ -11,13 +11,13 @@ import * as types from 'vs/workbench/api/node/extHostTypes';
import { IRawColorInfo, WorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import * as search from 'vs/workbench/parts/search/common/search';
import * as search from 'vs/workbench/contrib/search/common/search';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { CustomCodeAction } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { ICommandsExecutor, PreviewHTMLAPICommand, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand } from './apiCommands';
import { EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
import { isFalsyOrEmpty, isNonEmptyArray } from 'vs/base/common/arrays';
import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand } from './apiCommands';
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
export class ExtHostApiCommands {
@@ -134,7 +134,8 @@ export class ExtHostApiCommands {
description: 'Execute code action provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'range', description: 'Range in a text document', constraint: types.Range }
{ name: 'range', description: 'Range in a text document', constraint: types.Range },
{ name: 'kind', description: '(optional) Code action kind to return code actions for', constraint: (value: any) => !value || typeof value.value === 'string' },
],
returns: 'A promise that resolves to an array of Command-instances.'
});
@@ -199,7 +200,7 @@ export class ExtHostApiCommands {
description: 'Execute selection range provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position in a text document', constraint: types.Position }
{ name: 'positions', description: 'Positions in a text document', constraint: a => Array.isArray(a) }
],
returns: 'A promise that resolves to an array of ranges.'
});
@@ -218,25 +219,11 @@ export class ExtHostApiCommands {
};
};
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: any) => value instanceof URI || typeof value === 'string' },
{ name: 'column', description: '(optional) Column in which to preview.', constraint: (value: any) => 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: any) => typeof v === 'string' || typeof v === 'undefined' },
{ name: 'options', description: '(optional) Options for controlling webview environment.', constraint: (v: any) => typeof v === 'object' || typeof v === 'undefined' }
]
});
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: any) => value === undefined || 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: any) => value === undefined || typeof value === 'boolean' }
{ name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Wheter the opened URI will appear in the \'Open Recent\' list. Defaults to true. Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' }
]
});
@@ -276,7 +263,7 @@ export class ExtHostApiCommands {
// --- command impl
private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void {
let disposable = this._commands.registerCommand(false, id, handler, this, description);
const disposable = this._commands.registerCommand(false, id, handler, this, description);
this._disposables.push(disposable);
}
@@ -298,7 +285,7 @@ export class ExtHostApiCommands {
});
}
private _executeDefinitionProvider(resource: URI, position: types.Position): Promise<types.Location[]> {
private _executeDefinitionProvider(resource: URI, position: types.Position): Promise<types.Location[] | undefined> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -307,7 +294,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.location.to));
}
private _executeDeclaraionProvider(resource: URI, position: types.Position): Promise<types.Location[]> {
private _executeDeclaraionProvider(resource: URI, position: types.Position): Promise<types.Location[] | undefined> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -316,7 +303,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.location.to));
}
private _executeTypeDefinitionProvider(resource: URI, position: types.Position): Promise<types.Location[]> {
private _executeTypeDefinitionProvider(resource: URI, position: types.Position): Promise<types.Location[] | undefined> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -325,7 +312,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.location.to));
}
private _executeImplementationProvider(resource: URI, position: types.Position): Promise<types.Location[]> {
private _executeImplementationProvider(resource: URI, position: types.Position): Promise<types.Location[] | undefined> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -334,7 +321,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.location.to));
}
private _executeHoverProvider(resource: URI, position: types.Position): Promise<types.Hover[]> {
private _executeHoverProvider(resource: URI, position: types.Position): Promise<types.Hover[] | undefined> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -343,7 +330,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.Hover.to));
}
private _executeDocumentHighlights(resource: URI, position: types.Position): Promise<types.DocumentHighlight[]> {
private _executeDocumentHighlights(resource: URI, position: types.Position): Promise<types.DocumentHighlight[] | undefined> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -352,7 +339,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.DocumentHighlight.to));
}
private _executeReferenceProvider(resource: URI, position: types.Position): Promise<types.Location[]> {
private _executeReferenceProvider(resource: URI, position: types.Position): Promise<types.Location[] | undefined> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -372,13 +359,13 @@ export class ExtHostApiCommands {
return undefined;
}
if (value.rejectReason) {
return Promise.reject(new Error(value.rejectReason));
return Promise.reject<any>(new Error(value.rejectReason));
}
return typeConverters.WorkspaceEdit.to(value);
});
}
private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Promise<types.SignatureHelp> {
private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Promise<types.SignatureHelp | undefined> {
const args = {
resource,
position: position && typeConverters.Position.from(position),
@@ -392,7 +379,7 @@ export class ExtHostApiCommands {
});
}
private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string, maxItemsToResolve: number): Promise<types.CompletionList> {
private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string, maxItemsToResolve: number): Promise<types.CompletionList | undefined> {
const args = {
resource,
position: position && typeConverters.Position.from(position),
@@ -420,16 +407,15 @@ export class ExtHostApiCommands {
});
}
private _executeSelectionRangeProvider(resource: URI, position: types.Position): Promise<vscode.SelectionRange[]> {
private _executeSelectionRangeProvider(resource: URI, positions: types.Position[]): Promise<vscode.SelectionRange[][]> {
const pos = positions.map(typeConverters.Position.from);
const args = {
resource,
position: position && typeConverters.Position.from(position)
position: pos[0],
positions: pos
};
return this._commands.executeCommand<modes.SelectionRange[]>('_executeSelectionRangeProvider', args).then(result => {
if (isNonEmptyArray(result)) {
return result.map(typeConverters.SelectionRange.to);
}
return [];
return this._commands.executeCommand<modes.SelectionRange[][]>('_executeSelectionRangeProvider', args).then(result => {
return result.map(oneResult => oneResult.map(typeConverters.SelectionRange.to));
});
}
@@ -447,26 +433,26 @@ export class ExtHostApiCommands {
});
}
private _executeDocumentSymbolProvider(resource: URI): Promise<vscode.SymbolInformation[]> {
private _executeDocumentSymbolProvider(resource: URI): Promise<vscode.SymbolInformation[] | undefined> {
const args = {
resource
};
return this._commands.executeCommand<modes.DocumentSymbol[]>('_executeDocumentSymbolProvider', args).then(value => {
return this._commands.executeCommand<modes.DocumentSymbol[]>('_executeDocumentSymbolProvider', args).then((value): vscode.SymbolInformation[] | undefined => {
if (isFalsyOrEmpty(value)) {
return undefined;
}
class MergedInfo extends types.SymbolInformation implements vscode.DocumentSymbol {
static to(symbol: modes.DocumentSymbol): MergedInfo {
let res = new MergedInfo(
const res = new MergedInfo(
symbol.name,
typeConverters.SymbolKind.to(symbol.kind),
symbol.containerName,
symbol.containerName || '',
new types.Location(resource, typeConverters.Range.to(symbol.range))
);
res.detail = symbol.detail;
res.range = res.location.range;
res.selectionRange = typeConverters.Range.to(symbol.selectionRange);
res.children = symbol.children && symbol.children.map(MergedInfo.to);
res.children = symbol.children ? symbol.children.map(MergedInfo.to) : [];
return res;
}
@@ -474,19 +460,24 @@ export class ExtHostApiCommands {
range: vscode.Range;
selectionRange: vscode.Range;
children: vscode.DocumentSymbol[];
containerName: string;
}
return value.map(MergedInfo.to);
});
}
private _executeCodeActionProvider(resource: URI, range: types.Range): Promise<(vscode.CodeAction | vscode.Command)[]> {
private _executeCodeActionProvider(resource: URI, range: types.Range, kind?: string): Promise<(vscode.CodeAction | vscode.Command)[] | undefined> {
const args = {
resource,
range: typeConverters.Range.from(range)
range: typeConverters.Range.from(range),
kind
};
return this._commands.executeCommand<CustomCodeAction[]>('_executeCodeActionProvider', args)
.then(tryMapWith(codeAction => {
if (codeAction._isSynthetic) {
if (!codeAction.command) {
throw new Error('Synthetic code actions must have a command');
}
return this._commands.converter.fromInternal(codeAction.command);
} else {
const ret = new types.CodeAction(
@@ -504,18 +495,18 @@ export class ExtHostApiCommands {
}));
}
private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Promise<vscode.CodeLens[]> {
private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Promise<vscode.CodeLens[] | undefined> {
const args = { resource, itemResolveCount };
return this._commands.executeCommand<modes.ICodeLensSymbol[]>('_executeCodeLensProvider', args)
.then(tryMapWith(item => {
return new types.CodeLens(
typeConverters.Range.to(item.range),
this._commands.converter.fromInternal(item.command));
item.command ? this._commands.converter.fromInternal(item.command) : undefined);
}));
}
private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Promise<vscode.TextEdit[]> {
private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Promise<vscode.TextEdit[] | undefined> {
const args = {
resource,
options
@@ -524,7 +515,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(edit => new types.TextEdit(typeConverters.Range.to(edit.range), edit.text)));
}
private _executeFormatRangeProvider(resource: URI, range: types.Range, options: vscode.FormattingOptions): Promise<vscode.TextEdit[]> {
private _executeFormatRangeProvider(resource: URI, range: types.Range, options: vscode.FormattingOptions): Promise<vscode.TextEdit[] | undefined> {
const args = {
resource,
range: typeConverters.Range.from(range),
@@ -534,7 +525,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(edit => new types.TextEdit(typeConverters.Range.to(edit.range), edit.text)));
}
private _executeFormatOnTypeProvider(resource: URI, position: types.Position, ch: string, options: vscode.FormattingOptions): Promise<vscode.TextEdit[]> {
private _executeFormatOnTypeProvider(resource: URI, position: types.Position, ch: string, options: vscode.FormattingOptions): Promise<vscode.TextEdit[] | undefined> {
const args = {
resource,
position: typeConverters.Position.from(position),
@@ -545,7 +536,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(edit => new types.TextEdit(typeConverters.Range.to(edit.range), edit.text)));
}
private _executeDocumentLinkProvider(resource: URI): Promise<vscode.DocumentLink[]> {
private _executeDocumentLinkProvider(resource: URI): Promise<vscode.DocumentLink[] | undefined> {
return this._commands.executeCommand<modes.ILink[]>('_executeLinkProvider', resource)
.then(tryMapWith(typeConverters.DocumentLink.to));
}

View File

@@ -0,0 +1,101 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net';
import * as http from 'http';
import * as fs from 'fs';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { IURIToOpen, URIType } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
export class CLIServer {
private _server: http.Server;
private _ipcHandlePath: string | undefined;
constructor(private _commands: ExtHostCommands) {
this._server = http.createServer((req, res) => this.onRequest(req, res));
this.setup().catch(err => {
console.error(err);
return '';
});
}
public get ipcHandlePath() {
return this._ipcHandlePath;
}
private async setup(): Promise<string> {
this._ipcHandlePath = generateRandomPipeName();
try {
this._server.listen(this.ipcHandlePath);
this._server.on('error', err => console.error(err));
} catch (err) {
console.error('Could not start open from terminal server.');
}
return this._ipcHandlePath;
}
private collectURIToOpen(strs: string[], typeHint: URIType, result: IURIToOpen[]): void {
if (Array.isArray(strs)) {
for (const s of strs) {
try {
result.push({ uri: URI.parse(s), typeHint });
} catch (e) {
// ignore
}
}
}
}
private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
const chunks: string[] = [];
req.setEncoding('utf8');
req.on('data', (d: string) => chunks.push(d));
req.on('end', () => {
const data = JSON.parse(chunks.join(''));
switch (data.type) {
case 'open':
this.open(data, res);
break;
default:
res.writeHead(404);
res.write(`Unkown message type: ${data.type}`, err => {
if (err) {
console.error(err);
}
});
res.end();
break;
}
});
}
private open(data: any, res: http.ServerResponse) {
let { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow } = data;
if (folderURIs && folderURIs.length || fileURIs && fileURIs.length) {
const urisToOpen: IURIToOpen[] = [];
this.collectURIToOpen(folderURIs, 'folder', urisToOpen);
this.collectURIToOpen(fileURIs, 'file', urisToOpen);
if (!forceReuseWindow && urisToOpen.some(o => o.typeHint === 'folder' || (o.typeHint === 'file' && hasWorkspaceFileExtension(o.uri.path)))) {
forceNewWindow = true;
}
this._commands.executeCommand('_files.windowOpen', urisToOpen, { forceNewWindow, diffMode, addMode, forceReuseWindow });
}
res.writeHead(200);
res.end();
}
dispose(): void {
this._server.close();
if (this._ipcHandlePath && process.platform !== 'win32' && fs.existsSync(this._ipcHandlePath)) {
fs.unlinkSync(this._ipcHandlePath);
}
}
}

View File

@@ -8,7 +8,7 @@ import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import * as extHostTypeConverter from 'vs/workbench/api/node/extHostTypeConverters';
import { cloneAndChange } from 'vs/base/common/objects';
import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ObjectIdentifier, IMainContext } from './extHost.protocol';
import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ObjectIdentifier, IMainContext, CommandDto } from './extHost.protocol';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import * as modes from 'vs/editor/common/modes';
@@ -22,7 +22,7 @@ import { URI } from 'vs/base/common/uri';
interface CommandHandler {
callback: Function;
thisArg: any;
description: ICommandHandlerDescription;
description?: ICommandHandlerDescription;
}
export interface ArgumentProcessor {
@@ -138,7 +138,11 @@ export class ExtHostCommands implements ExtHostCommandsShape {
}
private _executeContributedCommand<T>(id: string, args: any[]): Promise<T> {
let { callback, thisArg, description } = this._commands.get(id);
const command = this._commands.get(id);
if (!command) {
throw new Error('Unknown command');
}
let { callback, thisArg, description } = command;
if (description) {
for (let i = 0; i < description.args.length; i++) {
try {
@@ -150,7 +154,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
}
try {
let result = callback.apply(thisArg, args);
const result = callback.apply(thisArg, args);
return Promise.resolve(result);
} catch (err) {
this._logService.error(err, id);
@@ -207,15 +211,19 @@ export class CommandsConverter {
this._commands.registerCommand(true, this._delegatingCommandId, this._executeConvertedCommand, this);
}
toInternal(command: vscode.Command): modes.Command {
toInternal(command: vscode.Command): CommandDto;
toInternal(command: undefined): undefined;
toInternal(command: vscode.Command | undefined): CommandDto | undefined;
toInternal(command: vscode.Command | undefined): CommandDto | undefined {
if (!command) {
return undefined;
}
const result: modes.Command = {
const result: CommandDto = {
$ident: undefined,
id: command.command,
title: command.title
title: command.title,
};
if (command.command && isNonEmptyArray(command.arguments)) {
@@ -223,7 +231,7 @@ export class CommandsConverter {
// means we don't want to send the arguments around
const id = this._heap.keep(command);
ObjectIdentifier.mixin(result, id);
result.$ident = id;
result.id = this._delegatingCommandId;
result.arguments = [id];
@@ -238,10 +246,6 @@ export class CommandsConverter {
fromInternal(command: modes.Command): vscode.Command {
if (!command) {
return undefined;
}
const id = ObjectIdentifier.of(command);
if (typeof id === 'number') {
return this._heap.get<vscode.Command>(id);
@@ -257,7 +261,7 @@ export class CommandsConverter {
private _executeConvertedCommand<R>(...args: any[]): Promise<R> {
const actualCmd = this._heap.get<vscode.Command>(args[0]);
return this._commands.executeCommand(actualCmd.command, ...actualCmd.arguments);
return this._commands.executeCommand(actualCmd.command, ...(actualCmd.arguments || []));
}
}

View File

@@ -8,12 +8,14 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import * as extHostTypeConverter from 'vs/workbench/api/node/extHostTypeConverters';
import * as types from 'vs/workbench/api/node/extHostTypes';
import * as vscode from 'vscode';
import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShape } from './extHost.protocol';
import { CommandsConverter } from './extHostCommands';
import { CommandsConverter, ExtHostCommands } from './extHostCommands';
import { IRange } from 'vs/editor/common/core/range';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Event, Emitter } from 'vs/base/common/event';
interface HandlerData<T> {
@@ -21,20 +23,137 @@ interface HandlerData<T> {
provider: T;
}
type ProviderHandle = number;
export class ExtHostComments implements ExtHostCommentsShape {
private static handlePool = 0;
private _proxy: MainThreadCommentsShape;
private _commentControllers: Map<ProviderHandle, ExtHostCommentController> = new Map<ProviderHandle, ExtHostCommentController>();
private _commentControllersByExtension: Map<string, ExtHostCommentController[]> = new Map<string, ExtHostCommentController[]>();
private _documentProviders = new Map<number, HandlerData<vscode.DocumentCommentProvider>>();
private _workspaceProviders = new Map<number, HandlerData<vscode.WorkspaceCommentProvider>>();
constructor(
mainContext: IMainContext,
private readonly _commandsConverter: CommandsConverter,
private _commands: ExtHostCommands,
private readonly _documents: ExtHostDocuments,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadComments);
_commands.registerArgumentProcessor({
processArgument: arg => {
if (arg && arg.$mid === 6) {
const commentController = this._commentControllers.get(arg.handle);
if (!commentController) {
return arg;
}
return commentController;
} else if (arg && arg.$mid === 7) {
const commentController = this._commentControllers.get(arg.commentControlHandle);
if (!commentController) {
return arg;
}
const commentThread = commentController.getCommentThread(arg.commentThreadHandle);
if (!commentThread) {
return arg;
}
return commentThread;
}
return arg;
}
});
}
createCommentController(extension: IExtensionDescription, id: string, label: string): vscode.CommentController {
const handle = ExtHostComments.handlePool++;
const commentController = new ExtHostCommentController(extension, handle, this._commands.converter, this._proxy, id, label);
this._commentControllers.set(commentController.handle, commentController);
const commentControllers = this._commentControllersByExtension.get(ExtensionIdentifier.toKey(extension.identifier)) || [];
commentControllers.push(commentController);
this._commentControllersByExtension.set(ExtensionIdentifier.toKey(extension.identifier), commentControllers);
return commentController;
}
$onCommentWidgetInputChange(commentControllerHandle: number, input: string): Promise<number | undefined> {
const commentController = this._commentControllers.get(commentControllerHandle);
if (!commentController) {
return Promise.resolve(undefined);
}
commentController.$onCommentWidgetInputChange(input);
return Promise.resolve(commentControllerHandle);
}
$provideCommentingRanges(commentControllerHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise<IRange[] | undefined> {
const commentController = this._commentControllers.get(commentControllerHandle);
if (!commentController || !commentController.commentingRangeProvider) {
return Promise.resolve(undefined);
}
const document = this._documents.getDocument(URI.revive(uriComponents));
return asPromise(() => {
return commentController.commentingRangeProvider!.provideCommentingRanges(document, token);
}).then(ranges => ranges ? ranges.map(x => extHostTypeConverter.Range.from(x)) : undefined);
}
$provideReactionGroup(commentControllerHandle: number): Promise<modes.CommentReaction[] | undefined> {
const commentController = this._commentControllers.get(commentControllerHandle);
if (!commentController || !commentController.reactionProvider) {
return Promise.resolve(undefined);
}
return asPromise(() => {
return commentController!.reactionProvider!.availableReactions;
}).then(reactions => reactions.map(reaction => convertToReaction2(commentController.reactionProvider, reaction)));
}
$toggleReaction(commentControllerHandle: number, threadHandle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void> {
const document = this._documents.getDocument(URI.revive(uri));
const commentController = this._commentControllers.get(commentControllerHandle);
if (!commentController || !commentController.reactionProvider || !commentController.reactionProvider.toggleReaction) {
return Promise.resolve(undefined);
}
return asPromise(() => {
const commentThread = commentController.getCommentThread(threadHandle);
if (commentThread) {
const vscodeComment = commentThread.getComment(comment.commentId);
if (commentController !== undefined && commentController.reactionProvider && commentController.reactionProvider.toggleReaction && vscodeComment) {
return commentController.reactionProvider.toggleReaction(document, vscodeComment, convertFromReaction(reaction));
}
}
return Promise.resolve(undefined);
});
}
$createNewCommentWidgetCallback(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, token: CancellationToken): void {
const commentController = this._commentControllers.get(commentControllerHandle);
if (!commentController || !commentController.emptyCommentThreadFactory) {
return;
}
const document = this._documents.getDocument(URI.revive(uriComponents));
commentController.emptyCommentThreadFactory.createEmptyCommentThread(document, extHostTypeConverter.Range.to(range));
}
registerWorkspaceCommentProvider(
@@ -70,7 +189,7 @@ export class ExtHostComments implements ExtHostCommentsShape {
startDraftLabel: provider.startDraftLabel,
deleteDraftLabel: provider.deleteDraftLabel,
finishDraftLabel: provider.finishDraftLabel,
reactionGroup: provider.reactionGroup
reactionGroup: provider.reactionGroup ? provider.reactionGroup.map(reaction => convertToReaction(provider, reaction)) : undefined
});
this.registerListeners(handle, extensionId, provider);
@@ -90,10 +209,10 @@ export class ExtHostComments implements ExtHostCommentsShape {
return Promise.resolve(null);
}
const handlerData = this._documentProviders.get(handle);
const handlerData = this.getDocumentProvider(handle);
return asPromise(() => {
return handlerData.provider.createNewCommentThread(data.document, ran, text, CancellationToken.None);
}).then(commentThread => commentThread ? convertToCommentThread(handlerData.extensionId, handlerData.provider, commentThread, this._commandsConverter) : null);
}).then(commentThread => commentThread ? convertToCommentThread(handlerData.extensionId, handlerData.provider, commentThread, this._commands.converter) : null);
}
$replyToCommentThread(handle: number, uri: UriComponents, range: IRange, thread: modes.CommentThread, text: string): Promise<modes.CommentThread | null> {
@@ -104,115 +223,96 @@ export class ExtHostComments implements ExtHostCommentsShape {
return Promise.resolve(null);
}
const handlerData = this._documentProviders.get(handle);
const handlerData = this.getDocumentProvider(handle);
return asPromise(() => {
return handlerData.provider.replyToCommentThread(data.document, ran, convertFromCommentThread(thread), text, CancellationToken.None);
}).then(commentThread => commentThread ? convertToCommentThread(handlerData.extensionId, handlerData.provider, commentThread, this._commandsConverter) : null);
}).then(commentThread => commentThread ? convertToCommentThread(handlerData.extensionId, handlerData.provider, commentThread, this._commands.converter) : null);
}
$editComment(handle: number, uri: UriComponents, comment: modes.Comment, text: string): Promise<void> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
throw new Error('Unable to retrieve document from URI');
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.editComment) {
return Promise.reject(new Error('not implemented'));
}
const handlerData = this._documentProviders.get(handle);
return asPromise(() => {
return handlerData.provider.editComment(data.document, convertFromComment(comment), text, CancellationToken.None);
return handlerData.provider.editComment!(document, convertFromComment(comment), text, CancellationToken.None);
});
}
$deleteComment(handle: number, uri: UriComponents, comment: modes.Comment): Promise<void> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
throw new Error('Unable to retrieve document from URI');
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.deleteComment) {
return Promise.reject(new Error('not implemented'));
}
const handlerData = this._documentProviders.get(handle);
return asPromise(() => {
return handlerData.provider.deleteComment(data.document, convertFromComment(comment), CancellationToken.None);
return handlerData.provider.deleteComment!(document, convertFromComment(comment), CancellationToken.None);
});
}
$startDraft(handle: number, uri: UriComponents): Promise<void> {
const data = this._documents.getDocumentData(URI.revive(uri));
const document = this._documents.getDocument(URI.revive(uri));
if (!data || !data.document) {
throw new Error('Unable to retrieve document from URI');
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.startDraft) {
return Promise.reject(new Error('not implemented'));
}
const handlerData = this._documentProviders.get(handle);
return asPromise(() => {
return handlerData.provider.startDraft(data.document, CancellationToken.None);
return handlerData.provider.startDraft!(document, CancellationToken.None);
});
}
$deleteDraft(handle: number, uri: UriComponents): Promise<void> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
throw new Error('Unable to retrieve document from URI');
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.deleteDraft) {
return Promise.reject(new Error('not implemented'));
}
const handlerData = this._documentProviders.get(handle);
return asPromise(() => {
return handlerData.provider.deleteDraft(data.document, CancellationToken.None);
return handlerData.provider.deleteDraft!(document, CancellationToken.None);
});
}
$finishDraft(handle: number, uri: UriComponents): Promise<void> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
throw new Error('Unable to retrieve document from URI');
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.finishDraft) {
return Promise.reject(new Error('not implemented'));
}
const handlerData = this._documentProviders.get(handle);
return asPromise(() => {
return handlerData.provider.finishDraft(data.document, CancellationToken.None);
return handlerData.provider.finishDraft!(document, CancellationToken.None);
});
}
$addReaction(handle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
throw new Error('Unable to retrieve document from URI');
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.addReaction) {
return Promise.reject(new Error('not implemented'));
}
const handlerData = this._documentProviders.get(handle);
return asPromise(() => {
return handlerData.provider.addReaction(data.document, convertFromComment(comment), reaction);
return handlerData.provider.addReaction!(document, convertFromComment(comment), convertFromReaction(reaction));
});
}
$deleteReaction(handle: number, uri: UriComponents, comment: modes.Comment, reaction: modes.CommentReaction): Promise<void> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
throw new Error('Unable to retrieve document from URI');
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
if (!handlerData.provider.deleteReaction) {
return Promise.reject(new Error('not implemented'));
}
const handlerData = this._documentProviders.get(handle);
return asPromise(() => {
return handlerData.provider.deleteReaction(data.document, convertFromComment(comment), reaction);
return handlerData.provider.deleteReaction!(document, convertFromComment(comment), convertFromReaction(reaction));
});
}
$provideDocumentComments(handle: number, uri: UriComponents): Promise<modes.CommentInfo> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
return Promise.resolve(null);
}
const handlerData = this._documentProviders.get(handle);
$provideDocumentComments(handle: number, uri: UriComponents): Promise<modes.CommentInfo | null> {
const document = this._documents.getDocument(URI.revive(uri));
const handlerData = this.getDocumentProvider(handle);
return asPromise(() => {
return handlerData.provider.provideDocumentComments(data.document, CancellationToken.None);
}).then(commentInfo => commentInfo ? convertCommentInfo(handle, handlerData.extensionId, handlerData.provider, commentInfo, this._commandsConverter) : null);
return handlerData.provider.provideDocumentComments(document, CancellationToken.None);
}).then(commentInfo => commentInfo ? convertCommentInfo(handle, handlerData.extensionId, handlerData.provider, commentInfo, this._commands.converter) : null);
}
$provideWorkspaceComments(handle: number): Promise<modes.CommentThread[] | null> {
@@ -224,7 +324,7 @@ export class ExtHostComments implements ExtHostCommentsShape {
return asPromise(() => {
return handlerData.provider.provideWorkspaceComments(CancellationToken.None);
}).then(comments =>
comments.map(comment => convertToCommentThread(handlerData.extensionId, handlerData.provider, comment, this._commandsConverter)
comments.map(comment => convertToCommentThread(handlerData.extensionId, handlerData.provider, comment, this._commands.converter)
));
}
@@ -232,13 +332,241 @@ export class ExtHostComments implements ExtHostCommentsShape {
provider.onDidChangeCommentThreads(event => {
this._proxy.$onDidCommentThreadsChange(handle, {
changed: event.changed.map(thread => convertToCommentThread(extensionId, provider, thread, this._commandsConverter)),
added: event.added.map(thread => convertToCommentThread(extensionId, provider, thread, this._commandsConverter)),
removed: event.removed.map(thread => convertToCommentThread(extensionId, provider, thread, this._commandsConverter)),
changed: event.changed.map(thread => convertToCommentThread(extensionId, provider, thread, this._commands.converter)),
added: event.added.map(thread => convertToCommentThread(extensionId, provider, thread, this._commands.converter)),
removed: event.removed.map(thread => convertToCommentThread(extensionId, provider, thread, this._commands.converter)),
draftMode: !!(provider as vscode.DocumentCommentProvider).startDraft && !!(provider as vscode.DocumentCommentProvider).finishDraft ? (event.inDraftMode ? modes.DraftMode.InDraft : modes.DraftMode.NotInDraft) : modes.DraftMode.NotSupported
});
});
}
private getDocumentProvider(handle: number): HandlerData<vscode.DocumentCommentProvider> {
const provider = this._documentProviders.get(handle);
if (!provider) {
throw new Error('unknown provider');
}
return provider;
}
}
export class ExtHostCommentThread implements vscode.CommentThread {
private static _handlePool: number = 0;
readonly handle = ExtHostCommentThread._handlePool++;
get threadId(): string {
return this._threadId;
}
get resource(): vscode.Uri {
return this._resource;
}
set range(range: vscode.Range) {
if (range.isEqual(this._range)) {
this._range = range;
this._proxy.$updateCommentThreadRange(this._commentController.handle, this.handle, extHostTypeConverter.Range.from(this._range));
}
}
get range(): vscode.Range {
return this._range;
}
private _label: string;
get label(): string {
return this._label;
}
set label(label: string) {
this._label = label;
this._proxy.$updateCommentThreadLabel(this._commentController.handle, this.handle, this._label);
}
get comments(): vscode.Comment[] {
return this._comments;
}
set comments(newComments: vscode.Comment[]) {
this._proxy.$updateComments(this._commentController.handle, this.handle, newComments.map(cmt => { return convertToModeComment(this._commentController, cmt, this._commandsConverter); }));
this._comments = newComments;
}
private _acceptInputCommand: vscode.Command;
get acceptInputCommand(): vscode.Command {
return this._acceptInputCommand;
}
set acceptInputCommand(acceptInputCommand: vscode.Command) {
this._acceptInputCommand = acceptInputCommand;
const internal = this._commandsConverter.toInternal(acceptInputCommand);
this._proxy.$updateCommentThreadAcceptInputCommand(this._commentController.handle, this.handle, internal);
}
private _additionalCommands: vscode.Command[] = [];
get additionalCommands(): vscode.Command[] {
return this._additionalCommands;
}
set additionalCommands(additionalCommands: vscode.Command[]) {
this._additionalCommands = additionalCommands;
const internals = additionalCommands.map(x => this._commandsConverter.toInternal(x));
this._proxy.$updateCommentThreadAdditionalCommands(this._commentController.handle, this.handle, internals);
}
private _collapseState?: vscode.CommentThreadCollapsibleState;
get collapsibleState(): vscode.CommentThreadCollapsibleState {
return this._collapseState!;
}
set collapsibleState(newState: vscode.CommentThreadCollapsibleState) {
this._collapseState = newState;
this._proxy.$updateCommentThreadCollapsibleState(this._commentController.handle, this.handle, convertToCollapsibleState(newState));
}
constructor(
private _proxy: MainThreadCommentsShape,
private readonly _commandsConverter: CommandsConverter,
private _commentController: ExtHostCommentController,
private _threadId: string,
private _resource: vscode.Uri,
private _range: vscode.Range,
private _comments: vscode.Comment[]
) {
this._proxy.$createCommentThread(
this._commentController.handle,
this.handle,
this._threadId,
this._resource,
extHostTypeConverter.Range.from(this._range),
this._comments.map(comment => { return convertToModeComment(this._commentController, comment, this._commandsConverter); }),
this._acceptInputCommand ? this._commandsConverter.toInternal(this._acceptInputCommand) : undefined,
this._additionalCommands ? this._additionalCommands.map(x => this._commandsConverter.toInternal(x)) : [],
this._collapseState!
);
}
getComment(commentId: string): vscode.Comment | undefined {
const comments = this._comments.filter(comment => comment.commentId === commentId);
if (comments && comments.length) {
return comments[0];
}
return undefined;
}
dispose() {
this._proxy.$deleteCommentThread(
this._commentController.handle,
this.handle
);
}
}
export class ExtHostCommentInputBox implements vscode.CommentInputBox {
private _onDidChangeValue = new Emitter<string>();
get onDidChangeValue(): Event<string> {
return this._onDidChangeValue.event;
}
private _value: string = '';
get value(): string {
return this._value;
}
set value(newInput: string) {
this._value = newInput;
this._onDidChangeValue.fire(this._value);
this._proxy.$setInputValue(this.commentControllerHandle, newInput);
}
constructor(
private _proxy: MainThreadCommentsShape,
public commentControllerHandle: number,
input: string
) {
this._value = input;
}
setInput(input: string) {
this._value = input;
}
}
class ExtHostCommentController implements vscode.CommentController {
get id(): string {
return this._id;
}
get label(): string {
return this._label;
}
public inputBox?: ExtHostCommentInputBox;
public activeCommentingRange?: vscode.Range;
public get handle(): number {
return this._handle;
}
private _threads: Map<number, ExtHostCommentThread> = new Map<number, ExtHostCommentThread>();
commentingRangeProvider?: vscode.CommentingRangeProvider;
emptyCommentThreadFactory?: vscode.EmptyCommentThreadFactory;
private _commentReactionProvider?: vscode.CommentReactionProvider;
get reactionProvider(): vscode.CommentReactionProvider | undefined {
return this._commentReactionProvider;
}
set reactionProvider(provider: vscode.CommentReactionProvider | undefined) {
this._commentReactionProvider = provider;
if (provider) {
this._proxy.$updateCommentControllerFeatures(this.handle, { reactionGroup: provider.availableReactions.map(reaction => convertToReaction2(provider, reaction)) });
}
}
constructor(
_extension: IExtensionDescription,
private _handle: number,
private readonly _commandsConverter: CommandsConverter,
private _proxy: MainThreadCommentsShape,
private _id: string,
private _label: string
) {
this._proxy.$registerCommentController(this.handle, _id, _label);
}
createCommentThread(id: string, resource: vscode.Uri, range: vscode.Range, comments: vscode.Comment[]): vscode.CommentThread {
const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, id, resource, range, comments);
this._threads.set(commentThread.handle, commentThread);
return commentThread;
}
$onCommentWidgetInputChange(input: string) {
if (!this.inputBox) {
this.inputBox = new ExtHostCommentInputBox(this._proxy, this.handle, input);
} else {
this.inputBox.setInput(input);
}
}
getCommentThread(handle: number) {
return this._threads.get(handle);
}
dispose(): void {
this._threads.forEach(value => {
value.dispose();
});
this._proxy.$unregisterCommentController(this.handle);
}
}
function convertCommentInfo(owner: number, extensionId: ExtensionIdentifier, provider: vscode.DocumentCommentProvider, vscodeCommentInfo: vscode.CommentInfo, commandsConverter: CommandsConverter): modes.CommentInfo {
@@ -263,8 +591,8 @@ function convertToCommentThread(extensionId: ExtensionIdentifier, provider: vsco
function convertFromCommentThread(commentThread: modes.CommentThread): vscode.CommentThread {
return {
threadId: commentThread.threadId,
resource: URI.parse(commentThread.resource),
threadId: commentThread.threadId!,
resource: URI.parse(commentThread.resource!),
range: extHostTypeConverter.Range.to(commentThread.range),
comments: commentThread.comments.map(convertFromComment),
collapsibleState: commentThread.collapsibleState
@@ -272,7 +600,7 @@ function convertFromCommentThread(commentThread: modes.CommentThread): vscode.Co
}
function convertFromComment(comment: modes.Comment): vscode.Comment {
let userIconPath: URI;
let userIconPath: URI | undefined;
if (comment.userIconPath) {
try {
userIconPath = URI.parse(comment.userIconPath);
@@ -289,7 +617,30 @@ function convertFromComment(comment: modes.Comment): vscode.Comment {
canEdit: comment.canEdit,
canDelete: comment.canDelete,
isDraft: comment.isDraft,
commentReactions: comment.commentReactions
commentReactions: comment.commentReactions ? comment.commentReactions.map(reaction => {
return {
label: reaction.label,
count: reaction.count,
hasReacted: reaction.hasReacted
};
}) : undefined
};
}
function convertToModeComment(commentController: ExtHostCommentController, vscodeComment: vscode.Comment, commandsConverter: CommandsConverter): modes.Comment {
const iconPath = vscodeComment.userIconPath ? vscodeComment.userIconPath.toString() : vscodeComment.gravatar;
return {
commentId: vscodeComment.commentId,
body: extHostTypeConverter.MarkdownString.from(vscodeComment.body),
userName: vscodeComment.userName,
userIconPath: iconPath,
isDraft: vscodeComment.isDraft,
selectCommand: vscodeComment.selectCommand ? commandsConverter.toInternal(vscodeComment.selectCommand) : undefined,
editCommand: vscodeComment.editCommand ? commandsConverter.toInternal(vscodeComment.editCommand) : undefined,
deleteCommand: vscodeComment.editCommand ? commandsConverter.toInternal(vscodeComment.deleteCommand) : undefined,
label: vscodeComment.label,
commentReactions: vscodeComment.commentReactions ? vscodeComment.commentReactions.map(reaction => convertToReaction2(commentController.reactionProvider, reaction)) : undefined
};
}
@@ -297,6 +648,7 @@ function convertToComment(provider: vscode.DocumentCommentProvider | vscode.Work
const canEdit = !!(provider as vscode.DocumentCommentProvider).editComment && vscodeComment.canEdit;
const canDelete = !!(provider as vscode.DocumentCommentProvider).deleteComment && vscodeComment.canDelete;
const iconPath = vscodeComment.userIconPath ? vscodeComment.userIconPath.toString() : vscodeComment.gravatar;
return {
commentId: vscodeComment.commentId,
body: extHostTypeConverter.MarkdownString.from(vscodeComment.body),
@@ -304,8 +656,51 @@ function convertToComment(provider: vscode.DocumentCommentProvider | vscode.Work
userIconPath: iconPath,
canEdit: canEdit,
canDelete: canDelete,
command: vscodeComment.command ? commandsConverter.toInternal(vscodeComment.command) : null,
selectCommand: vscodeComment.command ? commandsConverter.toInternal(vscodeComment.command) : undefined,
isDraft: vscodeComment.isDraft,
commentReactions: vscodeComment.commentReactions
commentReactions: vscodeComment.commentReactions ? vscodeComment.commentReactions.map(reaction => convertToReaction(provider, reaction)) : undefined
};
}
function convertToReaction(provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider, reaction: vscode.CommentReaction): modes.CommentReaction {
const providerCanDeleteReaction = !!(provider as vscode.DocumentCommentProvider).deleteReaction;
const providerCanAddReaction = !!(provider as vscode.DocumentCommentProvider).addReaction;
return {
label: reaction.label,
iconPath: reaction.iconPath ? extHostTypeConverter.pathOrURIToURI(reaction.iconPath) : undefined,
count: reaction.count,
hasReacted: reaction.hasReacted,
canEdit: (reaction.hasReacted && providerCanDeleteReaction) || (!reaction.hasReacted && providerCanAddReaction)
};
}
function convertToReaction2(provider: vscode.CommentReactionProvider | undefined, reaction: vscode.CommentReaction): modes.CommentReaction {
return {
label: reaction.label,
iconPath: reaction.iconPath ? extHostTypeConverter.pathOrURIToURI(reaction.iconPath) : undefined,
count: reaction.count,
hasReacted: reaction.hasReacted,
canEdit: provider !== undefined ? !!provider.toggleReaction : false
};
}
function convertFromReaction(reaction: modes.CommentReaction): vscode.CommentReaction {
return {
label: reaction.label,
count: reaction.count,
hasReacted: reaction.hasReacted
};
}
function convertToCollapsibleState(kind: vscode.CommentThreadCollapsibleState | undefined): modes.CommentThreadCollapsibleState {
if (kind !== undefined) {
switch (kind) {
case types.CommentThreadCollapsibleState.Expanded:
return modes.CommentThreadCollapsibleState.Expanded;
case types.CommentThreadCollapsibleState.Collapsed:
return modes.CommentThreadCollapsibleState.Collapsed;
}
}
return modes.CommentThreadCollapsibleState.Collapsed;
}

View File

@@ -43,7 +43,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
private readonly _proxy: MainThreadConfigurationShape;
private readonly _extHostWorkspace: ExtHostWorkspace;
private readonly _barrier: Barrier;
private _actual: ExtHostConfigProvider;
private _actual: ExtHostConfigProvider | null;
constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace) {
this._proxy = proxy;
@@ -53,7 +53,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
}
public getConfigProvider(): Promise<ExtHostConfigProvider> {
return this._barrier.wait().then(_ => this._actual);
return this._barrier.wait().then(_ => this._actual!);
}
$initializeConfiguration(data: IConfigurationInitData): void {
@@ -62,7 +62,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape {
}
$acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData): void {
this._actual.$acceptConfigurationChanged(data, eventData);
this.getConfigProvider().then(provider => provider.$acceptConfigurationChanged(data, eventData));
}
}
@@ -93,14 +93,14 @@ export class ExtHostConfigProvider {
getConfiguration(section?: string, resource?: URI, extensionId?: ExtensionIdentifier): vscode.WorkspaceConfiguration {
const config = this._toReadonlyValue(section
? lookUp(this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace), section)
: this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace));
? lookUp(this._configuration.getValue(undefined, { resource }, this._extHostWorkspace.workspace), section)
: this._configuration.getValue(undefined, { resource }, this._extHostWorkspace.workspace));
if (section) {
this._validateConfigurationAccess(section, resource, extensionId);
}
function parseConfigurationTarget(arg: boolean | ExtHostConfigurationTarget): ConfigurationTarget {
function parseConfigurationTarget(arg: boolean | ExtHostConfigurationTarget): ConfigurationTarget | null {
if (arg === undefined || arg === null) {
return null;
}
@@ -127,7 +127,7 @@ export class ExtHostConfigProvider {
} else {
let clonedConfig = undefined;
const cloneOnWriteProxy = (target: any, accessor: string): any => {
let clonedTarget = undefined;
let clonedTarget: any | undefined = undefined;
const cloneTarget = () => {
clonedConfig = clonedConfig ? clonedConfig : deepClone(config);
clonedTarget = clonedTarget ? clonedTarget : lookUp(clonedConfig, accessor);
@@ -151,17 +151,23 @@ export class ExtHostConfigProvider {
},
set: (_target: any, property: string, value: any) => {
cloneTarget();
clonedTarget[property] = value;
if (clonedTarget) {
clonedTarget[property] = value;
}
return true;
},
deleteProperty: (_target: any, property: string) => {
cloneTarget();
delete clonedTarget[property];
if (clonedTarget) {
delete clonedTarget[property];
}
return true;
},
defineProperty: (_target: any, property: string, descriptor: any) => {
cloneTarget();
Object.defineProperty(clonedTarget, property, descriptor);
if (clonedTarget) {
Object.defineProperty(clonedTarget, property, descriptor);
}
return true;
}
}) : target;
@@ -179,7 +185,7 @@ export class ExtHostConfigProvider {
return this._proxy.$removeConfigurationOption(target, key, resource);
}
},
inspect: <T>(key: string): ConfigurationInspect<T> => {
inspect: <T>(key: string): ConfigurationInspect<T> | undefined => {
key = section ? `${section}.${key}` : key;
const config = deepClone(this._configuration.inspect<T>(key, { resource }, this._extHostWorkspace.workspace));
if (config) {
@@ -218,7 +224,7 @@ export class ExtHostConfigProvider {
return readonlyProxy(result);
}
private _validateConfigurationAccess(key: string, resource: URI, extensionId: ExtensionIdentifier): void {
private _validateConfigurationAccess(key: string, resource: URI | undefined, extensionId?: ExtensionIdentifier): void {
const scope = OVERRIDE_PROPERTY_PATTERN.test(key) ? ConfigurationScope.RESOURCE : this._configurationScopes[key];
const extensionIdText = extensionId ? `[${extensionId.value}] ` : '';
if (ConfigurationScope.RESOURCE === scope) {

View File

@@ -5,7 +5,7 @@
// {{SQL CARBON EDIT}}
/*
import * as paths from 'vs/base/common/paths';
import * as path from 'vs/base/common/path';
import { Schemas } from 'vs/base/common/network';
import { URI, UriComponents } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
@@ -17,24 +17,24 @@ import {
} from 'vs/workbench/api/node/extHost.protocol';
import * as vscode from 'vscode';
import { Disposable, Position, Location, SourceBreakpoint, FunctionBreakpoint, DebugAdapterServer, DebugAdapterExecutable } from 'vs/workbench/api/node/extHostTypes';
import { ExecutableDebugAdapter, SocketDebugAdapter, AbstractDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExecutableDebugAdapter, SocketDebugAdapter, AbstractDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
import { IExtHostWorkspaceProvider } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor } from 'vs/workbench/parts/debug/common/debug';
import { getTerminalLauncher, hasChildProcesses, prepareCommand } from 'vs/workbench/parts/debug/node/terminals';
import { ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter, IDebugAdapterServer, IDebugAdapterExecutable, IAdapterDescriptor } from 'vs/workbench/contrib/debug/common/debug';
import { getTerminalLauncher, hasChildProcesses, prepareCommand } from 'vs/workbench/contrib/debug/node/terminals';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/node/variableResolver';
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/common/variableResolver';
import { ExtHostConfiguration, ExtHostConfigProvider } from './extHostConfiguration';
import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/parts/debug/common/debugUtils';
import { convertToVSCPaths, convertToDAPaths, isDebuggerMainContribution } from 'vs/workbench/contrib/debug/common/debugUtils';
import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
export class ExtHostDebugService implements ExtHostDebugServiceShape {
@@ -79,12 +79,12 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _variableResolver: IConfigurationResolverService;
private _integratedTerminalInstance: vscode.Terminal;
private _integratedTerminalInstance?: vscode.Terminal;
private _terminalDisposedListener: IDisposable;
constructor(mainContext: IMainContext,
private _workspaceService: ExtHostWorkspace,
private _workspaceService: IExtHostWorkspaceProvider,
private _extensionService: ExtHostExtensionService,
private _editorsService: ExtHostDocumentsAndEditors,
private _configurationService: ExtHostConfiguration,
@@ -123,28 +123,35 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._breakpointEventsActive = false;
this._extensionService.getExtensionRegistry().then((extensionRegistry: ExtensionDescriptionRegistry) => {
// register all debug extensions
const debugTypes: string[] = [];
for (const ed of extensionRegistry.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, program, or runtime attribute are considered a "defining" debugger contribution
if (dbg.type && (dbg.label || dbg.program || dbg.runtime)) {
debugTypes.push(dbg.type);
if (dbg.adapterExecutableCommand) {
this._aexCommands.set(dbg.type, dbg.adapterExecutableCommand);
}
extensionRegistry.onDidChange(_ => {
this.registerAllDebugTypes(extensionRegistry);
});
this.registerAllDebugTypes(extensionRegistry);
});
}
private registerAllDebugTypes(extensionRegistry: ExtensionDescriptionRegistry) {
const debugTypes: string[] = [];
this._aexCommands.clear();
for (const ed of extensionRegistry.getAllExtensionDescriptions()) {
if (ed.contributes) {
const debuggers = <IDebuggerContribution[]>ed.contributes['debuggers'];
if (debuggers && debuggers.length > 0) {
for (const dbg of debuggers) {
if (isDebuggerMainContribution(dbg)) {
debugTypes.push(dbg.type);
if (dbg.adapterExecutableCommand) {
this._aexCommands.set(dbg.type, dbg.adapterExecutableCommand);
}
}
}
}
}
if (debugTypes.length > 0) {
this._debugServiceProxy.$registerDebugTypes(debugTypes);
}
});
}
this._debugServiceProxy.$registerDebugTypes(debugTypes);
}
// extension debug API
@@ -236,8 +243,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return this._debugServiceProxy.$unregisterBreakpoints(ids, fids);
}
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration): Promise<boolean> {
return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig);
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, parentSession?: vscode.DebugSession): Promise<boolean> {
return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig, parentSession ? parentSession.id : undefined);
}
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable {
@@ -250,7 +257,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
console.error('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.');
}
let handle = this._configProviderHandleCounter++;
const handle = this._configProviderHandleCounter++;
this._configProviders.push({ type, handle, provider });
this._debugServiceProxy.$registerDebugConfigurationProvider(type,
@@ -281,7 +288,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
throw new Error(`a DebugAdapterDescriptorFactory can only be registered once per a type.`);
}
let handle = this._adapterFactoryHandleCounter++;
const handle = this._adapterFactoryHandleCounter++;
this._adapterFactories.push({ type, handle, factory });
this._debugServiceProxy.$registerDebugAdapterDescriptorFactory(type, handle);
@@ -298,7 +305,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return new Disposable(() => { });
}
let handle = this._trackerFactoryHandleCounter++;
const handle = this._trackerFactoryHandleCounter++;
this._trackerFactories.push({ type, handle, factory });
this._debugServiceProxy.$registerDebugAdapterTrackerFactory(type, handle);
@@ -319,7 +326,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
// React on terminal disposed and check if that is the debug terminal #12956
this._terminalDisposedListener = this._terminalService.onDidCloseTerminal(terminal => {
if (this._integratedTerminalInstance && this._integratedTerminalInstance === terminal) {
this._integratedTerminalInstance = null;
this._integratedTerminalInstance = undefined;
}
});
}
@@ -336,16 +343,17 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
}).then(needNewTerminal => {
if (needNewTerminal) {
if (needNewTerminal || !this._integratedTerminalInstance) {
this._integratedTerminalInstance = this._terminalService.createTerminal(args.title || nls.localize('debug.terminal.title', "debuggee"));
}
const terminal: vscode.Terminal = this._integratedTerminalInstance;
this._integratedTerminalInstance.show();
terminal.show();
return this._integratedTerminalInstance.processId.then(shellProcessId => {
const command = prepareCommand(args, config);
this._integratedTerminalInstance.sendText(command, true);
terminal.sendText(command, true);
return shellProcessId;
});
@@ -358,16 +366,16 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return terminalLauncher.runInTerminal(args, config);
}
}
return undefined;
return Promise.resolve(undefined);
}
public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise<IConfig> {
const configProvider = await this._configurationService.getConfigProvider();
if (!this._variableResolver) {
this._variableResolver = new ExtHostVariableResolverService(this._workspaceService, this._editorsService, configProvider);
const [workspaceFolders, configProvider] = await Promise.all([this._workspaceService.getWorkspaceFolders2(), this._configurationService.getConfigProvider()]);
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._editorsService, configProvider);
}
let ws: IWorkspaceFolder;
const folder = this.getFolder(folderUri);
let ws: IWorkspaceFolder | undefined;
const folder = await this.getFolder(folderUri);
if (folder) {
ws = {
uri: folder.uri,
@@ -381,13 +389,14 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return this._variableResolver.resolveAny(ws, config);
}
public $startDASession(debugAdapterHandle: number, sessionDto: IDebugSessionDto): Promise<void> {
public async $startDASession(debugAdapterHandle: number, sessionDto: IDebugSessionDto): Promise<void> {
const mythis = this;
const session = this.getSession(sessionDto);
return this.getAdapterDescriptor(this.getAdapterFactoryByType(session.type), session).then(x => {
const session = await this.getSession(sessionDto);
const adapter = this.convertToDto(x);
return this.getAdapterDescriptor(this.getAdapterFactoryByType(session.type), session).then(daDescriptor => {
const adapter = this.convertToDto(daDescriptor);
let da: AbstractDebugAdapter | undefined = undefined;
switch (adapter.type) {
@@ -408,8 +417,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
break;
}
if (da) {
this._debugAdapters.set(debugAdapterHandle, da);
const debugAdapter = da;
if (debugAdapter) {
this._debugAdapters.set(debugAdapterHandle, debugAdapter);
return this.getDebugAdapterTrackers(session).then(tracker => {
@@ -417,9 +428,9 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._debugAdaptersTrackers.set(debugAdapterHandle, tracker);
}
da.onMessage(message => {
debugAdapter.onMessage(message => {
if (tracker) {
if (tracker && tracker.onDidSendMessage) {
tracker.onDidSendMessage(message);
}
@@ -428,24 +439,24 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
mythis._debugServiceProxy.$acceptDAMessage(debugAdapterHandle, message);
});
da.onError(err => {
if (tracker) {
debugAdapter.onError(err => {
if (tracker && tracker.onError) {
tracker.onError(err);
}
this._debugServiceProxy.$acceptDAError(debugAdapterHandle, err.name, err.message, err.stack);
});
da.onExit(code => {
if (tracker) {
debugAdapter.onExit((code: number) => {
if (tracker && tracker.onExit) {
tracker.onExit(code, undefined);
}
this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, code, null);
this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, code, undefined);
});
if (tracker) {
if (tracker && tracker.onWillStartSession) {
tracker.onWillStartSession();
}
return da.startSession();
return debugAdapter.startSession();
});
}
@@ -453,13 +464,13 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
});
}
public $sendDAMessage(debugAdapterHandle: number, message: DebugProtocol.ProtocolMessage): Promise<void> {
public $sendDAMessage(debugAdapterHandle: number, message: DebugProtocol.ProtocolMessage): void {
// VS Code -> DA
message = convertToDAPaths(message, false);
const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); // TODO@AW: same handle?
if (tracker) {
if (tracker && tracker.onWillReceiveMessage) {
tracker.onWillReceiveMessage(message);
}
@@ -467,14 +478,13 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
if (da) {
da.sendMessage(message);
}
return undefined;
}
public $stopDASession(debugAdapterHandle: number): Promise<void> {
const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle);
this._debugAdaptersTrackers.delete(debugAdapterHandle);
if (tracker) {
if (tracker && tracker.onWillStopSession) {
tracker.onWillStopSession();
}
@@ -483,20 +493,20 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
if (da) {
return da.stopSession();
} else {
return undefined;
return Promise.resolve(void 0);
}
}
public $acceptBreakpointsDelta(delta: IBreakpointsDeltaDto): void {
let a: vscode.Breakpoint[] = [];
let r: vscode.Breakpoint[] = [];
let c: vscode.Breakpoint[] = [];
const a: vscode.Breakpoint[] = [];
const r: vscode.Breakpoint[] = [];
const c: vscode.Breakpoint[] = [];
if (delta.added) {
for (const bpd of delta.added) {
if (!this._breakpoints.has(bpd.id)) {
const id = bpd.id;
if (id && !this._breakpoints.has(id)) {
let bp: vscode.Breakpoint;
if (bpd.type === 'function') {
bp = new FunctionBreakpoint(bpd.functionName, bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage);
@@ -504,8 +514,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
const uri = URI.revive(bpd.uri);
bp = new SourceBreakpoint(new Location(uri, new Position(bpd.line, bpd.character)), bpd.enabled, bpd.condition, bpd.hitCondition, bpd.logMessage);
}
(bp as any)._id = bpd.id;
this._breakpoints.set(bpd.id, bp);
(bp as any)._id = id;
this._breakpoints.set(id, bp);
a.push(bp);
}
}
@@ -523,24 +533,26 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
if (delta.changed) {
for (const bpd of delta.changed) {
let bp = this._breakpoints.get(bpd.id);
if (bp) {
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 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));
if (bpd.id) {
const bp = this._breakpoints.get(bpd.id);
if (bp) {
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 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);
}
c.push(bp);
}
}
}
@@ -549,71 +561,89 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined): Promise<vscode.DebugConfiguration[]> {
let provider = this.getConfigProviderByHandle(configProviderHandle);
if (!provider) {
return Promise.reject(new Error('no handler found'));
}
if (!provider.provideDebugConfigurations) {
return Promise.reject(new Error('handler has no method provideDebugConfigurations'));
}
return asPromise(() => provider.provideDebugConfigurations(this.getFolder(folderUri), CancellationToken.None));
return asPromise(async () => {
const provider = this.getConfigProviderByHandle(configProviderHandle);
if (!provider) {
throw new Error('no DebugConfigurationProvider found');
}
if (!provider.provideDebugConfigurations) {
throw new Error('DebugConfigurationProvider has no method provideDebugConfigurations');
}
const folder = await this.getFolder(folderUri);
return provider.provideDebugConfigurations(folder, CancellationToken.None);
}).then(debugConfigurations => {
if (!debugConfigurations) {
throw new Error('nothing returned from DebugConfigurationProvider.provideDebugConfigurations');
}
return debugConfigurations;
});
}
public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Promise<vscode.DebugConfiguration> {
let provider = this.getConfigProviderByHandle(configProviderHandle);
if (!provider) {
return Promise.reject(new Error('no handler found'));
}
if (!provider.resolveDebugConfiguration) {
return Promise.reject(new Error('handler has no method resolveDebugConfiguration'));
}
return asPromise(() => provider.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, CancellationToken.None));
public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Promise<vscode.DebugConfiguration | null | undefined> {
return asPromise(async () => {
const provider = this.getConfigProviderByHandle(configProviderHandle);
if (!provider) {
throw new Error('no DebugConfigurationProvider found');
}
if (!provider.resolveDebugConfiguration) {
throw new Error('DebugConfigurationProvider has no method resolveDebugConfiguration');
}
const folder = await this.getFolder(folderUri);
return provider.resolveDebugConfiguration(folder, debugConfiguration, CancellationToken.None);
});
}
// TODO@AW legacy
// TODO@AW deprecated and legacy
public $legacyDebugAdapterExecutable(configProviderHandle: number, folderUri: UriComponents | undefined): Promise<IAdapterDescriptor> {
let provider = this.getConfigProviderByHandle(configProviderHandle);
if (!provider) {
return Promise.reject(new Error('no handler found'));
}
if (!provider.debugAdapterExecutable) {
return Promise.reject(new Error('handler has no method debugAdapterExecutable'));
}
return asPromise(() => provider.debugAdapterExecutable(this.getFolder(folderUri), CancellationToken.None)).then(x => this.convertToDto(x));
return asPromise(async () => {
const provider = this.getConfigProviderByHandle(configProviderHandle);
if (!provider) {
throw new Error('no DebugConfigurationProvider found');
}
if (!provider.debugAdapterExecutable) {
throw new Error('DebugConfigurationProvider has no method debugAdapterExecutable');
}
const folder = await this.getFolder(folderUri);
return provider.debugAdapterExecutable(folder, CancellationToken.None);
}).then(executable => {
if (!executable) {
throw new Error('nothing returned from DebugConfigurationProvider.debugAdapterExecutable');
}
return this.convertToDto(executable);
});
}
public $provideDebugAdapter(adapterProviderHandle: number, sessionDto: IDebugSessionDto): Promise<IAdapterDescriptor> {
let adapterProvider = this.getAdapterProviderByHandle(adapterProviderHandle);
public async $provideDebugAdapter(adapterProviderHandle: number, sessionDto: IDebugSessionDto): Promise<IAdapterDescriptor> {
const adapterProvider = this.getAdapterProviderByHandle(adapterProviderHandle);
if (!adapterProvider) {
return Promise.reject(new Error('no handler found'));
}
return this.getAdapterDescriptor(adapterProvider, this.getSession(sessionDto)).then(x => this.convertToDto(x));
const session = await this.getSession(sessionDto);
return this.getAdapterDescriptor(adapterProvider, session).then(x => this.convertToDto(x));
}
public $acceptDebugSessionStarted(sessionDto: IDebugSessionDto): void {
this._onDidStartDebugSession.fire(this.getSession(sessionDto));
public async $acceptDebugSessionStarted(sessionDto: IDebugSessionDto): Promise<void> {
const session = await this.getSession(sessionDto);
this._onDidStartDebugSession.fire(session);
}
public $acceptDebugSessionTerminated(sessionDto: IDebugSessionDto): void {
const session = this.getSession(sessionDto);
public async $acceptDebugSessionTerminated(sessionDto: IDebugSessionDto): Promise<void> {
const session = await this.getSession(sessionDto);
if (session) {
this._onDidTerminateDebugSession.fire(session);
this._debugSessions.delete(session.id);
}
}
public $acceptDebugSessionActiveChanged(sessionDto: IDebugSessionDto): void {
this._activeDebugSession = sessionDto ? this.getSession(sessionDto) : undefined;
public async $acceptDebugSessionActiveChanged(sessionDto: IDebugSessionDto | undefined): Promise<void> {
this._activeDebugSession = sessionDto ? await this.getSession(sessionDto) : undefined;
this._onDidChangeActiveDebugSession.fire(this._activeDebugSession);
}
public $acceptDebugSessionCustomEvent(sessionDto: IDebugSessionDto, event: any): void {
public async $acceptDebugSessionCustomEvent(sessionDto: IDebugSessionDto, event: any): Promise<void> {
const session = await this.getSession(sessionDto);
const ee: vscode.DebugSessionCustomEvent = {
session: this.getSession(sessionDto),
session: session,
event: event.event,
body: event.body
};
@@ -622,7 +652,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
// private & dto helpers
private convertToDto(x: vscode.DebugAdapterDescriptor): IAdapterDescriptor {
private convertToDto(x: vscode.DebugAdapterDescriptor | undefined): IAdapterDescriptor {
if (x instanceof DebugAdapterExecutable) {
return <IDebugAdapterExecutable>{
type: 'executable',
@@ -642,11 +672,11 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
implementation: x.implementation
};
} else */ /* {{SQL CARBON EDIT}} {
throw new Error('unexpected type');
throw new Error('convertToDto unexpected type');
}
}
private getAdapterFactoryByType(type: string): vscode.DebugAdapterDescriptorFactory {
private getAdapterFactoryByType(type: string): vscode.DebugAdapterDescriptorFactory | undefined {
const results = this._adapterFactories.filter(p => p.type === type);
if (results.length > 0) {
return results[0].factory;
@@ -654,7 +684,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return undefined;
}
private getAdapterProviderByHandle(handle: number): vscode.DebugAdapterDescriptorFactory {
private getAdapterProviderByHandle(handle: number): vscode.DebugAdapterDescriptorFactory | undefined {
const results = this._adapterFactories.filter(p => p.handle === handle);
if (results.length > 0) {
return results[0].factory;
@@ -662,7 +692,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return undefined;
}
private getConfigProviderByHandle(handle: number): vscode.DebugConfigurationProvider {
private getConfigProviderByHandle(handle: number): vscode.DebugConfigurationProvider | undefined {
const results = this._configProviders.filter(p => p.handle === handle);
if (results.length > 0) {
return results[0].provider;
@@ -687,18 +717,18 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return false;
}
private getDebugAdapterTrackers(session: ExtHostDebugSession): Promise<vscode.DebugAdapterTracker> {
private getDebugAdapterTrackers(session: ExtHostDebugSession): Promise<vscode.DebugAdapterTracker | undefined> {
const config = session.configuration;
const type = config.type;
const promises = this._trackerFactories
.filter(tuple => tuple.type === type || tuple.type === '*')
.map(tuple => asPromise(() => tuple.factory.createDebugAdapterTracker(session)).then(p => p).catch(err => null));
.map(tuple => asPromise<vscode.ProviderResult<vscode.DebugAdapterTracker>>(() => tuple.factory.createDebugAdapterTracker(session)).then(p => p, err => null));
return Promise.race([
Promise.all(promises).then(trackers => {
trackers = trackers.filter(t => t); // filter null
Promise.all(promises).then(result => {
const trackers = <vscode.DebugAdapterTracker[]>result.filter(t => !!t); // filter null
if (trackers.length > 0) {
return new MultiTracker(trackers);
}
@@ -716,7 +746,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
});
}
private async getAdapterDescriptor(adapterProvider: vscode.DebugAdapterDescriptorFactory, session: ExtHostDebugSession): Promise<vscode.DebugAdapterDescriptor> {
private async getAdapterDescriptor(adapterProvider: vscode.DebugAdapterDescriptorFactory | undefined, session: ExtHostDebugSession): Promise<vscode.DebugAdapterDescriptor | undefined> {
// a "debugServer" attribute in the launch config takes precedence
const serverPort = session.configuration.debugServer;
@@ -725,16 +755,25 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
// TODO@AW legacy
const pairs = this._configProviders.filter(p => p.type === session.type);
if (pairs.length > 0) {
if (pairs[0].provider.debugAdapterExecutable) {
return asPromise(() => pairs[0].provider.debugAdapterExecutable(session.workspaceFolder, CancellationToken.None));
}
const pair = this._configProviders.filter(p => p.type === session.type).pop();
if (pair && pair.provider.debugAdapterExecutable) {
const func = pair.provider.debugAdapterExecutable;
return asPromise(() => func(session.workspaceFolder, CancellationToken.None)).then(executable => {
if (executable) {
return executable;
}
return undefined;
});
}
if (adapterProvider) {
const extensionRegistry = await this._extensionService.getExtensionRegistry();
return asPromise(() => adapterProvider.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry)));
return asPromise(() => adapterProvider.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry))).then(daDescriptor => {
if (daDescriptor) {
return daDescriptor;
}
return undefined;
});
}
// try deprecated command based extension API "adapterExecutableCommand" to determine the executable
@@ -778,25 +817,33 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
}
private getSession(dto: IDebugSessionDto): ExtHostDebugSession {
private async getSession(dto: IDebugSessionDto): Promise<ExtHostDebugSession> {
if (dto) {
if (typeof dto === 'string') {
return this._debugSessions.get(dto);
const ds = this._debugSessions.get(dto);
if (ds) {
return ds;
}
} else {
const debugSession = new ExtHostDebugSession(this._debugServiceProxy, dto.id, dto.type, dto.name, this.getFolder(dto.folderUri), dto.configuration);
this._debugSessions.set(debugSession.id, debugSession);
return debugSession;
let ds = this._debugSessions.get(dto.id);
if (!ds) {
const folder = await this.getFolder(dto.folderUri);
ds = new ExtHostDebugSession(this._debugServiceProxy, dto.id, dto.type, dto.name, folder, dto.configuration);
this._debugSessions.set(ds.id, ds);
this._debugServiceProxy.$sessionCached(ds.id);
}
return ds;
}
}
return undefined;
throw new Error('cannot find session');
}
private getFolder(_folderUri: UriComponents | undefined): vscode.WorkspaceFolder | undefined {
private getFolder(_folderUri: UriComponents | undefined): Promise<vscode.WorkspaceFolder | undefined> {
if (_folderUri) {
const folderURI = URI.revive(_folderUri);
return this._workspaceService.resolveWorkspaceFolder(folderURI);
}
return undefined;
return Promise.resolve(undefined);
}
}
@@ -855,10 +902,9 @@ export class ExtHostDebugConsole implements vscode.DebugConsole {
export class ExtHostVariableResolverService extends AbstractVariableResolverService {
constructor(workspaceService: ExtHostWorkspace, editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider) {
constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider) {
super({
getFolderUri: (folderName: string): URI => {
const folders = workspaceService.getWorkspaceFolders();
getFolderUri: (folderName: string): URI | undefined => {
const found = folders.filter(f => f.name === folderName);
if (found && found.length > 0) {
return found[0].uri;
@@ -866,9 +912,9 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
return undefined;
},
getWorkspaceFolderCount: (): number => {
return workspaceService.getWorkspaceFolders().length;
return folders.length;
},
getConfigurationValue: (folderUri: URI, section: string) => {
getConfigurationValue: (folderUri: URI, section: string): string | undefined => {
return configurationService.getConfiguration(undefined, folderUri).get<string>(section);
},
getExecPath: (): string | undefined => {
@@ -879,7 +925,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
if (activeEditor) {
const resource = activeEditor.document.uri;
if (resource.scheme === Schemas.file) {
return paths.normalize(resource.fsPath, true);
return path.normalize(resource.fsPath);
}
}
return undefined;
@@ -891,14 +937,14 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
}
return undefined;
},
getLineNumber: (): string => {
getLineNumber: (): string | undefined => {
const activeEditor = editorService.activeEditor();
if (activeEditor) {
return String(activeEditor.selection.end.line + 1);
}
return undefined;
}
});
}, process.env as IProcessEnvironment);
}
}
@@ -1005,4 +1051,4 @@ class DirectDebugAdapter extends AbstractDebugAdapter implements IDapTransport {
}
// {{SQL CARBON EDIT}}
*/
*/

View File

@@ -46,16 +46,20 @@ export class ExtHostDecorations implements ExtHostDecorationsShape {
const result: DecorationReply = Object.create(null);
return Promise.all(requests.map(request => {
const { handle, uri, id } = request;
if (!this._provider.has(handle)) {
const entry = this._provider.get(handle);
if (!entry) {
// might have been unregistered in the meantime
return undefined;
}
const { provider, extensionId } = this._provider.get(handle);
const { provider, extensionId } = entry;
return Promise.resolve(provider.provideDecoration(URI.revive(uri), token)).then(data => {
if (data && data.letter && data.letter.length !== 1) {
console.warn(`INVALID decoration from extension '${extensionId.value}'. The 'letter' must be set and be one character, not '${data.letter}'.`);
}
result[id] = data && <DecorationData>[data.priority, data.bubble, data.title, data.letter, data.color, data.source];
if (data) {
result[id] = <DecorationData>[data.priority, data.bubble, data.title, data.letter, data.color, data.source];
}
}, err => {
console.error(err);
});

View File

@@ -8,9 +8,9 @@ import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'
import { URI } from 'vs/base/common/uri';
import * as vscode from 'vscode';
import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol';
import { DiagnosticSeverity, Diagnostic } from './extHostTypes';
import { DiagnosticSeverity } from './extHostTypes';
import * as converter from './extHostTypeConverters';
import { mergeSort, equals } from 'vs/base/common/arrays';
import { mergeSort } from 'vs/base/common/arrays';
import { Event, Emitter } from 'vs/base/common/event';
import { keys } from 'vs/base/common/map';
@@ -37,7 +37,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
if (!this._isDisposed) {
this._onDidChangeDiagnostics.fire(keys(this._data));
this._proxy.$clear(this._owner);
this._data = undefined;
this._data = undefined!;
this._isDisposed = true;
}
}
@@ -60,14 +60,10 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
// the actual implementation for #set
this._checkDisposed();
let toSync: vscode.Uri[];
let hasChanged = true;
let toSync: vscode.Uri[] = [];
if (first instanceof URI) {
// check if this has actually changed
hasChanged = hasChanged && !equals(diagnostics, this.get(first), Diagnostic.isEqual);
if (!diagnostics) {
// remove this entry
this.delete(first);
@@ -81,7 +77,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
} else if (Array.isArray(first)) {
// update many rows
toSync = [];
let lastUri: vscode.Uri;
let lastUri: vscode.Uri | undefined;
// ensure stable-sort
mergeSort(first, DiagnosticCollection._compareIndexedTuplesByUri);
@@ -89,7 +85,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
for (const tuple of first) {
const [uri, diagnostics] = tuple;
if (!lastUri || uri.toString() !== lastUri.toString()) {
if (lastUri && this._data.get(lastUri.toString()).length === 0) {
if (lastUri && this._data.get(lastUri.toString())!.length === 0) {
this._data.delete(lastUri.toString());
}
lastUri = uri;
@@ -99,9 +95,15 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
if (!diagnostics) {
// [Uri, undefined] means clear this
this._data.get(uri.toString()).length = 0;
const currentDiagnostics = this._data.get(uri.toString());
if (currentDiagnostics) {
currentDiagnostics.length = 0;
}
} else {
this._data.get(uri.toString()).push(...diagnostics);
const currentDiagnostics = this._data.get(uri.toString());
if (currentDiagnostics) {
currentDiagnostics.push(...diagnostics);
}
}
}
}
@@ -109,19 +111,11 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
// send event for extensions
this._onDidChangeDiagnostics.fire(toSync);
// if nothing has changed then there is nothing else to do
// we have updated the diagnostics but we don't send a message
// to the renderer. tho we have still send an event for other
// extensions because the diagnostic might carry more information
// than known to us
if (!hasChanged) {
return;
}
// compute change and send to main side
const entries: [URI, IMarkerData[]][] = [];
for (let uri of toSync) {
let marker: IMarkerData[];
let diagnostics = this._data.get(uri.toString());
let marker: IMarkerData[] = [];
const diagnostics = this._data.get(uri.toString());
if (diagnostics) {
// no more than N diagnostics per file
@@ -149,7 +143,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
endColumn: marker[marker.length - 1].endColumn
});
} else {
marker = diagnostics.map(converter.Diagnostic.from);
marker = diagnostics.map(diag => converter.Diagnostic.from(diag));
}
}
@@ -176,18 +170,18 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
forEach(callback: (uri: URI, diagnostics: vscode.Diagnostic[], collection: DiagnosticCollection) => any, thisArg?: any): void {
this._checkDisposed();
this._data.forEach((value, key) => {
let uri = URI.parse(key);
const uri = URI.parse(key);
callback.apply(thisArg, [uri, this.get(uri), this]);
});
}
get(uri: URI): vscode.Diagnostic[] {
this._checkDisposed();
let result = this._data.get(uri.toString());
const result = this._data.get(uri.toString());
if (Array.isArray(result)) {
return <vscode.Diagnostic[]>Object.freeze(result.slice(0));
}
return undefined;
return [];
}
has(uri: URI): boolean {
@@ -230,8 +224,8 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
}
static _mapper(last: (vscode.Uri | string)[]): { uris: vscode.Uri[] } {
let uris: vscode.Uri[] = [];
let map = new Set<string>();
const uris: vscode.Uri[] = [];
const map = new Set<string>();
for (const uri of last) {
if (typeof uri === 'string') {
if (!map.has(uri)) {
@@ -255,7 +249,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
this._proxy = mainContext.getProxy(MainContext.MainThreadDiagnostics);
}
createDiagnosticCollection(name: string): vscode.DiagnosticCollection {
createDiagnosticCollection(name?: string): vscode.DiagnosticCollection {
let { _collections, _proxy, _onDidChangeDiagnostics } = this;
let owner: string;
if (!name) {
@@ -272,7 +266,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
const result = new class extends DiagnosticCollection {
constructor() {
super(name, owner, ExtHostDiagnostics._maxDiagnosticsPerFile, _proxy, _onDidChangeDiagnostics);
super(name!, owner, ExtHostDiagnostics._maxDiagnosticsPerFile, _proxy, _onDidChangeDiagnostics);
_collections.set(owner, this);
}
dispose() {
@@ -286,12 +280,13 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
getDiagnostics(resource: vscode.Uri): vscode.Diagnostic[];
getDiagnostics(): [vscode.Uri, vscode.Diagnostic[]][];
getDiagnostics(resource?: vscode.Uri): vscode.Diagnostic[] | [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[]][] = [];
const index = new Map<string, number>();
const res: [vscode.Uri, vscode.Diagnostic[]][] = [];
this._collections.forEach(collection => {
collection.forEach((uri, diagnostics) => {
let idx = index.get(uri.toString());

View File

@@ -15,15 +15,15 @@ export class ExtHostDialogs {
this._proxy = mainContext.getProxy(MainContext.MainThreadDialogs);
}
showOpenDialog(options: vscode.OpenDialogOptions): Promise<URI[]> {
showOpenDialog(options: vscode.OpenDialogOptions): Promise<URI[] | undefined> {
return this._proxy.$showOpenDialog(options).then(filepaths => {
return filepaths && filepaths.map(URI.revive);
return filepaths ? filepaths.map(URI.revive) : undefined;
});
}
showSaveDialog(options: vscode.SaveDialogOptions): Promise<URI> {
showSaveDialog(options: vscode.SaveDialogOptions): Promise<URI | undefined> {
return this._proxy.$showSaveDialog(options).then(filepath => {
return filepath && URI.revive(filepath);
return filepath ? URI.revive(filepath) : undefined;
});
}
}

View File

@@ -45,17 +45,20 @@ export class ExtHostDocumentContentProvider implements ExtHostDocumentContentPro
this._documentContentProviders.set(handle, provider);
this._proxy.$registerTextContentProvider(handle, scheme);
let subscription: IDisposable;
let subscription: IDisposable | undefined;
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())) {
if (this._documentsAndEditors.getDocument(uri)) {
this.$provideTextDocumentContent(handle, uri).then(value => {
if (!value) {
return;
}
const document = this._documentsAndEditors.getDocument(uri.toString());
const document = this._documentsAndEditors.getDocument(uri);
if (!document) {
// disposed in the meantime
return;
@@ -84,7 +87,7 @@ export class ExtHostDocumentContentProvider implements ExtHostDocumentContentPro
});
}
$provideTextDocumentContent(handle: number, uri: UriComponents): Promise<string> {
$provideTextDocumentContent(handle: number, uri: UriComponents): Promise<string | null | undefined> {
const provider = this._documentContentProviders.get(handle);
if (!provider) {
return Promise.reject(new Error(`unsupported uri-scheme: ${uri.scheme}`));

View File

@@ -14,10 +14,10 @@ import { EndOfLine, Position, Range } from 'vs/workbench/api/node/extHostTypes';
import * as vscode from 'vscode';
const _modeId2WordDefinition = new Map<string, RegExp>();
export function setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void {
export function setWordDefinitionFor(modeId: string, wordDefinition: RegExp | undefined): void {
_modeId2WordDefinition.set(modeId, wordDefinition);
}
export function getWordDefinitionFor(modeId: string): RegExp {
export function getWordDefinitionFor(modeId: string): RegExp | undefined {
return _modeId2WordDefinition.get(modeId);
}
@@ -105,7 +105,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
}
private _getTextInRange(_range: vscode.Range): string {
let range = this._validateRange(_range);
const range = this._validateRange(_range);
if (range.isEmpty) {
return '';
@@ -115,7 +115,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
return this._lines[range.start.line].substring(range.start.character, range.end.character);
}
let lineEnding = this._eol,
const lineEnding = this._eol,
startLineIndex = range.start.line,
endLineIndex = range.end.line,
resultLines: string[] = [];
@@ -131,14 +131,14 @@ export class ExtHostDocumentData extends MirrorTextModel {
private _lineAt(lineOrPosition: number | vscode.Position): vscode.TextLine {
let line: number;
let line: number | undefined;
if (lineOrPosition instanceof Position) {
line = lineOrPosition.line;
} else if (typeof lineOrPosition === 'number') {
line = lineOrPosition;
}
if (line < 0 || line >= this._lines.length) {
if (typeof line !== 'number' || line < 0 || line >= this._lines.length) {
throw new Error('Illegal value for `line`');
}
@@ -146,7 +146,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
if (!result || result.lineNumber !== line || result.text !== this._lines[line]) {
const text = this._lines[line];
const firstNonWhitespaceCharacterIndex = /^(\s*)/.exec(text)[1].length;
const firstNonWhitespaceCharacterIndex = /^(\s*)/.exec(text)![1].length;
const range = new Range(line, 0, line, text.length);
const rangeIncludingLineBreak = line < this._lines.length - 1
? new Range(line, 0, line + 1, 0)
@@ -170,7 +170,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
private _offsetAt(position: vscode.Position): number {
position = this._validatePosition(position);
this._ensureLineStarts();
return this._lineStarts.getAccumulatedValue(position.line - 1) + position.character;
return this._lineStarts!.getAccumulatedValue(position.line - 1) + position.character;
}
private _positionAt(offset: number): vscode.Position {
@@ -178,9 +178,9 @@ export class ExtHostDocumentData extends MirrorTextModel {
offset = Math.max(0, offset);
this._ensureLineStarts();
let out = this._lineStarts.getIndexOf(offset);
const out = this._lineStarts!.getIndexOf(offset);
let lineLength = this._lines[out.index].length;
const lineLength = this._lines[out.index].length;
// Ensure we return a valid position
return new Position(out.index, Math.min(out.remainder, lineLength));
@@ -193,8 +193,8 @@ export class ExtHostDocumentData extends MirrorTextModel {
throw new Error('Invalid argument');
}
let start = this._validatePosition(range.start);
let end = this._validatePosition(range.end);
const start = this._validatePosition(range.start);
const end = this._validatePosition(range.end);
if (start === range.start && end === range.end) {
return range;
@@ -221,7 +221,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
hasChanged = true;
}
else {
let maxCharacter = this._lines[line].length;
const maxCharacter = this._lines[line].length;
if (character < 0) {
character = 0;
hasChanged = true;
@@ -238,8 +238,8 @@ export class ExtHostDocumentData extends MirrorTextModel {
return new Position(line, character);
}
private _getWordRangeAtPosition(_position: vscode.Position, regexp?: RegExp): vscode.Range {
let position = this._validatePosition(_position);
private _getWordRangeAtPosition(_position: vscode.Position, regexp?: RegExp): vscode.Range | undefined {
const position = this._validatePosition(_position);
if (!regexp) {
// use default when custom-regexp isn't provided
@@ -251,7 +251,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
regexp = getWordDefinitionFor(this._languageId);
}
let wordAtText = getWordAtText(
const wordAtText = getWordAtText(
position.character + 1,
ensureValidWordDefinition(regexp),
this._lines[position.line],

View File

@@ -14,8 +14,8 @@ import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
import { LinkedList } from 'vs/base/common/linkedList';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
type Listener = [Function, any, IExtensionDescription];
@@ -53,17 +53,17 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
const entries = this._callbacks.toArray();
let didTimeout = false;
let didTimeoutHandle = setTimeout(() => didTimeout = true, this._thresholds.timeout);
const didTimeoutHandle = setTimeout(() => didTimeout = true, this._thresholds.timeout);
const promise = sequence(entries.map(listener => {
return () => {
if (didTimeout) {
// timeout - no more listeners
return undefined;
return Promise.resolve();
}
const document = this._documents.getDocumentData(resource).document;
const document = this._documents.getDocument(resource);
return this._deliverEventAsyncAndBlameBadListeners(listener, <any>{ document, reason: TextDocumentSaveReason.to(reason) });
};
}));
@@ -72,7 +72,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
private _deliverEventAsyncAndBlameBadListeners([listener, thisArg, extension]: Listener, stubEvent: vscode.TextDocumentWillSaveEvent): Promise<any> {
const errors = this._badListeners.get(listener);
if (errors > this._thresholds.errors) {
if (typeof errors === 'number' && errors > this._thresholds.errors) {
// bad listener - ignore
return Promise.resolve(false);
}
@@ -90,7 +90,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
const errors = this._badListeners.get(listener);
this._badListeners.set(listener, !errors ? 1 : errors + 1);
if (errors > this._thresholds.errors) {
if (typeof errors === 'number' && errors > this._thresholds.errors) {
this._logService.info(`onWillSaveTextDocument-listener from extension '${extension.identifier.value}' will now be IGNORED because of timeouts and/or errors`);
}
}
@@ -169,7 +169,6 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
return this._mainThreadEditors.$tryApplyWorkspaceEdit({ edits: [resourceEdit] });
}
// TODO@joh bubble this to listener?
return Promise.reject(new Error('concurrent_edits'));
});
}

View File

@@ -56,20 +56,28 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
return this._documentsAndEditors.allDocuments();
}
public getDocumentData(resource: vscode.Uri): ExtHostDocumentData {
public getDocumentData(resource: vscode.Uri): ExtHostDocumentData | undefined {
if (!resource) {
return undefined;
}
const data = this._documentsAndEditors.getDocument(resource.toString());
const data = this._documentsAndEditors.getDocument(resource);
if (data) {
return data;
}
return undefined;
}
public getDocument(resource: vscode.Uri): vscode.TextDocument {
const data = this.getDocumentData(resource);
if (!data || !data.document) {
throw new Error('Unable to retrieve document from URI');
}
return data.document;
}
public ensureDocumentData(uri: URI): Promise<ExtHostDocumentData> {
let cached = this._documentsAndEditors.getDocument(uri.toString());
const cached = this._documentsAndEditors.getDocument(uri);
if (cached) {
return Promise.resolve(cached);
}
@@ -78,7 +86,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
if (!promise) {
promise = this._proxy.$tryOpenDocument(uri).then(() => {
this._documentLoader.delete(uri.toString());
return this._documentsAndEditors.getDocument(uri.toString());
return this._documentsAndEditors.getDocument(uri);
}, err => {
this._documentLoader.delete(uri.toString());
return Promise.reject(err);
@@ -95,9 +103,10 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
public $acceptModelModeChanged(uriComponents: UriComponents, oldModeId: string, newModeId: string): void {
const uri = URI.revive(uriComponents);
const strURL = uri.toString();
let data = this._documentsAndEditors.getDocument(strURL);
const data = this._documentsAndEditors.getDocument(uri);
if (!data) {
throw new Error('unknown document');
}
// Treat a mode change as a remove + add
this._onDidRemoveDocument.fire(data.document);
@@ -107,16 +116,20 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
public $acceptModelSaved(uriComponents: UriComponents): void {
const uri = URI.revive(uriComponents);
const strURL = uri.toString();
let data = this._documentsAndEditors.getDocument(strURL);
const data = this._documentsAndEditors.getDocument(uri);
if (!data) {
throw new Error('unknown document');
}
this.$acceptDirtyStateChanged(uriComponents, false);
this._onDidSaveDocument.fire(data.document);
}
public $acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void {
const uri = URI.revive(uriComponents);
const strURL = uri.toString();
let data = this._documentsAndEditors.getDocument(strURL);
const data = this._documentsAndEditors.getDocument(uri);
if (!data) {
throw new Error('unknown document');
}
data._acceptIsDirty(isDirty);
this._onDidChangeDocument.fire({
document: data.document,
@@ -126,8 +139,10 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
public $acceptModelChanged(uriComponents: UriComponents, events: IModelChangedEvent, isDirty: boolean): void {
const uri = URI.revive(uriComponents);
const strURL = uri.toString();
let data = this._documentsAndEditors.getDocument(strURL);
const data = this._documentsAndEditors.getDocument(uri);
if (!data) {
throw new Error('unknown document');
}
data._acceptIsDirty(isDirty);
data.onEvents(events);
this._onDidChangeDocument.fire({
@@ -143,7 +158,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
});
}
public setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void {
public setWordDefinitionFor(modeId: string, wordDefinition: RegExp | undefined): void {
setWordDefinitionFor(modeId, wordDefinition);
}
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as assert from 'vs/base/common/assert';
import { Emitter, Event } from 'vs/base/common/event';
import { dispose } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
@@ -17,7 +17,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
private _disposables: Disposable[] = [];
private _activeEditorId: string;
private _activeEditorId: string | null;
private readonly _editors = new Map<string, ExtHostTextEditor>();
private readonly _documents = new Map<string, ExtHostDocumentData>();
@@ -25,12 +25,12 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
private readonly _onDidAddDocuments = new Emitter<ExtHostDocumentData[]>();
private readonly _onDidRemoveDocuments = new Emitter<ExtHostDocumentData[]>();
private readonly _onDidChangeVisibleTextEditors = new Emitter<ExtHostTextEditor[]>();
private readonly _onDidChangeActiveTextEditor = new Emitter<ExtHostTextEditor>();
private readonly _onDidChangeActiveTextEditor = new Emitter<ExtHostTextEditor | undefined>();
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 onDidChangeActiveTextEditor: Event<ExtHostTextEditor | undefined> = this._onDidChangeActiveTextEditor.event;
constructor(
private readonly _mainContext: IMainContext,
@@ -52,7 +52,9 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
const id = uri.toString();
const data = this._documents.get(id);
this._documents.delete(id);
removedDocuments.push(data);
if (data) {
removedDocuments.push(data);
}
}
}
@@ -79,7 +81,9 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
for (const id of delta.removedEditors) {
const editor = this._editors.get(id);
this._editors.delete(id);
removedEditors.push(editor);
if (editor) {
removedEditors.push(editor);
}
}
}
@@ -89,15 +93,15 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
assert.ok(this._documents.has(resource.toString()), `document '${resource}' does not exist`);
assert.ok(!this._editors.has(data.id), `editor '${data.id}' already exists!`);
const documentData = this._documents.get(resource.toString());
const documentData = this._documents.get(resource.toString())!;
const editor = new ExtHostTextEditor(
this._mainContext.getProxy(MainContext.MainThreadTextEditors),
data.id,
documentData,
data.selections.map(typeConverters.Selection.to),
data.options,
data.visibleRanges.map(typeConverters.Range.to),
typeConverters.ViewColumn.to(data.editorPosition)
data.visibleRanges.map(range => typeConverters.Range.to(range)),
typeof data.editorPosition === 'number' ? typeConverters.ViewColumn.to(data.editorPosition) : undefined
);
this._editors.set(data.id, editor);
}
@@ -127,8 +131,8 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
}
}
getDocument(strUrl: string): ExtHostDocumentData {
return this._documents.get(strUrl);
getDocument(uri: URI): ExtHostDocumentData | undefined {
return this._documents.get(uri.toString());
}
allDocuments(): ExtHostDocumentData[] {
@@ -137,7 +141,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
return result;
}
getEditor(id: string): ExtHostTextEditor {
getEditor(id: string): ExtHostTextEditor | undefined {
return this._editors.get(id);
}

View File

@@ -5,10 +5,9 @@
import * as nls from 'vs/nls';
import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionActivationError, MissingDependencyError } from 'vs/workbench/services/extensions/common/extensions';
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
@@ -161,6 +160,12 @@ export class EmptyExtension extends ActivatedExtension {
}
}
export class HostExtension extends ActivatedExtension {
constructor() {
super(false, null, ExtensionActivationTimes.NONE, { activate: undefined, deactivate: undefined }, undefined, []);
}
}
export class FailedExtension extends ActivatedExtension {
constructor(activationError: Error) {
super(true, activationError, ExtensionActivationTimes.NONE, { activate: undefined, deactivate: undefined }, undefined, []);
@@ -168,9 +173,8 @@ export class FailedExtension extends ActivatedExtension {
}
export interface IExtensionsActivatorHost {
showMessage(severity: Severity, message: string): void;
actualActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension>;
onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): void;
actualActivateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension>;
}
export class ExtensionActivatedByEvent {
@@ -192,6 +196,7 @@ export class ExtensionsActivator {
private readonly _registry: ExtensionDescriptionRegistry;
private readonly _resolvedExtensionsSet: Set<string>;
private readonly _hostExtensionsMap: Map<string, ExtensionIdentifier>;
private readonly _host: IExtensionsActivatorHost;
private readonly _activatingExtensions: Map<string, Promise<void>>;
private readonly _activatedExtensions: Map<string, ActivatedExtension>;
@@ -200,10 +205,12 @@ export class ExtensionsActivator {
*/
private readonly _alreadyActivatedEvents: { [activationEvent: string]: boolean; };
constructor(registry: ExtensionDescriptionRegistry, resolvedExtensions: ExtensionIdentifier[], host: IExtensionsActivatorHost) {
constructor(registry: ExtensionDescriptionRegistry, resolvedExtensions: ExtensionIdentifier[], hostExtensions: ExtensionIdentifier[], host: IExtensionsActivatorHost) {
this._registry = registry;
this._resolvedExtensionsSet = new Set<string>();
resolvedExtensions.forEach((extensionId) => this._resolvedExtensionsSet.add(ExtensionIdentifier.toKey(extensionId)));
this._hostExtensionsMap = new Map<string, ExtensionIdentifier>();
hostExtensions.forEach((extensionId) => this._hostExtensionsMap.set(ExtensionIdentifier.toKey(extensionId), extensionId));
this._host = host;
this._activatingExtensions = new Map<string, Promise<void>>();
this._activatedExtensions = new Map<string, ActivatedExtension>();
@@ -230,27 +237,33 @@ export class ExtensionsActivator {
if (this._alreadyActivatedEvents[activationEvent]) {
return NO_OP_VOID_PROMISE;
}
let activateExtensions = this._registry.getExtensionDescriptionsForActivationEvent(activationEvent);
return this._activateExtensions(activateExtensions, reason, 0).then(() => {
const activateExtensions = this._registry.getExtensionDescriptionsForActivationEvent(activationEvent);
return this._activateExtensions(activateExtensions.map(e => e.identifier), reason).then(() => {
this._alreadyActivatedEvents[activationEvent] = true;
});
}
public activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
let desc = this._registry.getExtensionDescription(extensionId);
const desc = this._registry.getExtensionDescription(extensionId);
if (!desc) {
throw new Error('Extension `' + extensionId + '` is not known');
}
return this._activateExtensions([desc], reason, 0);
return this._activateExtensions([desc.identifier], reason);
}
/**
* Handle semantics related to dependencies for `currentExtension`.
* semantics: `redExtensions` must wait for `greenExtensions`.
*/
private _handleActivateRequest(currentExtension: IExtensionDescription, greenExtensions: { [id: string]: IExtensionDescription; }, redExtensions: IExtensionDescription[]): void {
let depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies);
private _handleActivateRequest(currentExtensionId: ExtensionIdentifier, greenExtensions: { [id: string]: ExtensionIdentifier; }, redExtensions: ExtensionIdentifier[]): void {
if (this._hostExtensionsMap.has(ExtensionIdentifier.toKey(currentExtensionId))) {
greenExtensions[ExtensionIdentifier.toKey(currentExtensionId)] = currentExtensionId;
return;
}
const currentExtension = this._registry.getExtensionDescription(currentExtensionId)!;
const depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies);
let currentExtensionGetsGreenLight = true;
for (let j = 0, lenJ = depIds.length; j < lenJ; j++) {
@@ -261,78 +274,77 @@ export class ExtensionsActivator {
continue;
}
const depDesc = this._registry.getExtensionDescription(depId);
const dep = this._activatedExtensions.get(ExtensionIdentifier.toKey(depId));
if (dep && !dep.activationFailed) {
// the dependency is already activated OK
continue;
}
if (!depDesc) {
// Error condition 1: unknown dependency
this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Cannot activate extension '{0}' because it depends on extension '{1}', which is not installed or disabled. Please install or enable '{1}' and reload the window.", currentExtension.displayName || currentExtension.identifier.value, depId));
const error = new Error(`Unknown dependency '${depId}'`);
if (dep && dep.activationFailed) {
// Error condition 2: a dependency has already failed activation
this._host.onExtensionActivationError(currentExtension.identifier, nls.localize('failedDep1', "Cannot activate extension '{0}' because it depends on extension '{1}', which failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId));
const error = new Error(`Dependency ${depId} failed to activate`);
(<any>error).detail = dep.activationFailedError;
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
return;
}
const dep = this._activatedExtensions.get(ExtensionIdentifier.toKey(depId));
if (dep) {
if (dep.activationFailed) {
// Error condition 2: a dependency has already failed activation
this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Cannot activate extension '{0}' because it depends on extension '{1}', which failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId));
const error = new Error(`Dependency ${depId} failed to activate`);
(<any>error).detail = dep.activationFailedError;
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
return;
}
} else {
if (this._hostExtensionsMap.has(ExtensionIdentifier.toKey(depId))) {
// must first wait for the dependency to activate
currentExtensionGetsGreenLight = false;
greenExtensions[ExtensionIdentifier.toKey(depId)] = depDesc;
greenExtensions[ExtensionIdentifier.toKey(depId)] = this._hostExtensionsMap.get(ExtensionIdentifier.toKey(depId))!;
continue;
}
const depDesc = this._registry.getExtensionDescription(depId);
if (depDesc) {
// must first wait for the dependency to activate
currentExtensionGetsGreenLight = false;
greenExtensions[ExtensionIdentifier.toKey(depId)] = depDesc.identifier;
continue;
}
// Error condition 1: unknown dependency
this._host.onExtensionActivationError(currentExtension.identifier, new MissingDependencyError(depId));
const error = new Error(`Unknown dependency '${depId}'`);
this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error));
return;
}
if (currentExtensionGetsGreenLight) {
greenExtensions[ExtensionIdentifier.toKey(currentExtension.identifier)] = currentExtension;
greenExtensions[ExtensionIdentifier.toKey(currentExtension.identifier)] = currentExtensionId;
} else {
redExtensions.push(currentExtension);
redExtensions.push(currentExtensionId);
}
}
private _activateExtensions(extensionDescriptions: IExtensionDescription[], reason: ExtensionActivationReason, recursionLevel: number): Promise<void> {
// console.log(recursionLevel, '_activateExtensions: ', extensionDescriptions.map(p => p.id));
if (extensionDescriptions.length === 0) {
private _activateExtensions(extensionIds: ExtensionIdentifier[], reason: ExtensionActivationReason): Promise<void> {
// console.log('_activateExtensions: ', extensionIds.map(p => p.value));
if (extensionIds.length === 0) {
return Promise.resolve(undefined);
}
extensionDescriptions = extensionDescriptions.filter((p) => !this._activatedExtensions.has(ExtensionIdentifier.toKey(p.identifier)));
if (extensionDescriptions.length === 0) {
extensionIds = extensionIds.filter((p) => !this._activatedExtensions.has(ExtensionIdentifier.toKey(p)));
if (extensionIds.length === 0) {
return Promise.resolve(undefined);
}
if (recursionLevel > 10) {
// More than 10 dependencies deep => most likely a dependency loop
for (let i = 0, len = extensionDescriptions.length; i < len; i++) {
// Error condition 3: dependency loop
this._host.showMessage(Severity.Error, nls.localize('failedDep2', "Extension '{0}' failed to activate. Reason: more than 10 levels of dependencies (most likely a dependency loop).", extensionDescriptions[i].identifier.value));
const error = new Error('More than 10 levels of dependencies (most likely a dependency loop)');
this._activatedExtensions.set(ExtensionIdentifier.toKey(extensionDescriptions[i].identifier), new FailedExtension(error));
}
return Promise.resolve(undefined);
}
const greenMap: { [id: string]: ExtensionIdentifier; } = Object.create(null),
red: ExtensionIdentifier[] = [];
let greenMap: { [id: string]: IExtensionDescription; } = Object.create(null),
red: IExtensionDescription[] = [];
for (let i = 0, len = extensionDescriptions.length; i < len; i++) {
this._handleActivateRequest(extensionDescriptions[i], greenMap, red);
for (let i = 0, len = extensionIds.length; i < len; i++) {
this._handleActivateRequest(extensionIds[i], greenMap, red);
}
// Make sure no red is also green
for (let i = 0, len = red.length; i < len; i++) {
const redExtensionKey = ExtensionIdentifier.toKey(red[i].identifier);
const redExtensionKey = ExtensionIdentifier.toKey(red[i]);
if (greenMap[redExtensionKey]) {
delete greenMap[redExtensionKey];
}
}
let green = Object.keys(greenMap).map(id => greenMap[id]);
const green = Object.keys(greenMap).map(id => greenMap[id]);
// console.log('greenExtensions: ', green.map(p => p.id));
// console.log('redExtensions: ', red.map(p => p.id));
@@ -342,13 +354,13 @@ export class ExtensionsActivator {
return Promise.all(green.map((p) => this._activateExtension(p, reason))).then(_ => undefined);
}
return this._activateExtensions(green, reason, recursionLevel + 1).then(_ => {
return this._activateExtensions(red, reason, recursionLevel + 1);
return this._activateExtensions(green, reason).then(_ => {
return this._activateExtensions(red, reason);
});
}
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<void> {
const extensionKey = ExtensionIdentifier.toKey(extensionDescription.identifier);
private _activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
const extensionKey = ExtensionIdentifier.toKey(extensionId);
if (this._activatedExtensions.has(extensionKey)) {
return Promise.resolve(undefined);
@@ -359,9 +371,9 @@ export class ExtensionsActivator {
return currentlyActivatingExtension;
}
const newlyActivatingExtension = this._host.actualActivateExtension(extensionDescription, reason).then(undefined, (err) => {
this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionDescription.identifier.value, err.message));
console.error('Activating extension `' + extensionDescription.identifier.value + '` failed: ', err.message);
const newlyActivatingExtension = this._host.actualActivateExtension(extensionId, reason).then(undefined, (err) => {
this._host.onExtensionActivationError(extensionId, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionId.value, err.message));
console.error('Activating extension `' + extensionId.value + '` failed: ', err.message);
console.log('Here is the error stack: ', err.stack);
// Treat the extension as being empty
return new FailedExtension(err);

View File

@@ -4,30 +4,33 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as path from 'path';
import * as path from 'vs/base/common/path';
// {{SQL CARBON EDIT}}
import { createApiFactory, initializeExtensionApi, ISqlExtensionApiFactory } from 'sql/workbench/api/node/sqlExtHost.api.impl';
import { originalFSPath } from 'vs/base/common/resources';
import { Barrier } from 'vs/base/common/async';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { TernarySearchTree } from 'vs/base/common/map';
import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
import * as pfs from 'vs/base/node/pfs';
import { ILogService } from 'vs/platform/log/common/log';
// {{SQL CARBON EDIT}} - Remove createApiFactory initializeExtensionApi, and IExtensionApiFactory imports
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, IWorkspaceData, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IStaticWorkspaceData } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionMemento, IExtensionModule } from 'vs/workbench/api/node/extHostExtensionActivator';
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionMemento, IExtensionModule, HostExtension } from 'vs/workbench/api/node/extHostExtensionActivator';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { connectProxyResolver } from 'vs/workbench/services/extensions/node/proxyResolver';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IWorkspace } from 'vs/platform/workspace/common/workspace';
import { Schemas } from 'vs/base/common/network';
class ExtensionMemento implements IExtensionMemento {
@@ -82,13 +85,13 @@ class ExtensionMemento implements IExtensionMemento {
class ExtensionStoragePath {
private readonly _workspace: IWorkspaceData;
private readonly _workspace?: IStaticWorkspaceData;
private readonly _environment: IEnvironment;
private readonly _ready: Promise<string>;
private _value: string;
private readonly _ready: Promise<string | undefined>;
private _value?: string;
constructor(workspace: IWorkspaceData, environment: IEnvironment) {
constructor(workspace: IStaticWorkspaceData | undefined, environment: IEnvironment) {
this._workspace = workspace;
this._environment = environment;
this._ready = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
@@ -98,7 +101,7 @@ class ExtensionStoragePath {
return this._ready;
}
workspaceValue(extension: IExtensionDescription): string {
workspaceValue(extension: IExtensionDescription): string | undefined {
if (this._value) {
return path.join(this._value, extension.identifier.value);
}
@@ -109,11 +112,14 @@ class ExtensionStoragePath {
return path.join(this._environment.globalStorageHome.fsPath, extension.identifier.value.toLowerCase());
}
private async _getOrCreateWorkspaceStoragePath(): Promise<string> {
private async _getOrCreateWorkspaceStoragePath(): Promise<string | undefined> {
if (!this._workspace) {
return Promise.resolve(undefined);
}
if (!this._environment.appSettingsHome) {
return undefined;
}
const storageName = this._workspace.id;
const storagePath = path.join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName);
@@ -161,15 +167,17 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape;
private readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape;
private readonly _barrier: Barrier;
private readonly _almostReadyToRunExtensions: Barrier;
private readonly _readyToRunExtensions: Barrier;
private readonly _registry: ExtensionDescriptionRegistry;
private readonly _storage: ExtHostStorage;
private readonly _storagePath: ExtensionStoragePath;
private readonly _activator: ExtensionsActivator;
private _extensionPathIndex: Promise<TernarySearchTree<IExtensionDescription>>;
// {{SQL CARBON EDIT}}
private _extensionPathIndex: Promise<TernarySearchTree<IExtensionDescription>> | null;
private readonly _extensionApiFactory: ISqlExtensionApiFactory;
private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver; };
private _started: boolean;
constructor(
@@ -191,27 +199,27 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry);
this._mainThreadExtensionsProxy = this._extHostContext.getProxy(MainContext.MainThreadExtensionService);
this._barrier = new Barrier();
this._almostReadyToRunExtensions = new Barrier();
this._readyToRunExtensions = new Barrier();
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
this._storage = new ExtHostStorage(this._extHostContext);
this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment);
this._activator = new ExtensionsActivator(this._registry, initData.resolvedExtensions, {
showMessage: (severity: Severity, message: string): void => {
this._mainThreadExtensionsProxy.$localShowMessage(severity, message);
switch (severity) {
case Severity.Error:
console.error(message);
break;
case Severity.Warning:
console.warn(message);
break;
default:
console.log(message);
}
const hostExtensions = new Set<string>();
initData.hostExtensions.forEach((extensionId) => hostExtensions.add(ExtensionIdentifier.toKey(extensionId)));
this._activator = new ExtensionsActivator(this._registry, initData.resolvedExtensions, initData.hostExtensions, {
onExtensionActivationError: (extensionId: ExtensionIdentifier, error: ExtensionActivationError): void => {
this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, error);
},
actualActivateExtension: (extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) {
const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
await this._mainThreadExtensionsProxy.$activateExtension(extensionId, activationEvent);
return new HostExtension();
}
const extensionDescription = this._registry.getExtensionDescription(extensionId)!;
return this._activateExtension(extensionDescription, reason);
}
});
@@ -220,6 +228,8 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
// initialize API first (i.e. do not release barrier until the API is initialized)
this._extensionApiFactory = createApiFactory(this._initData, this._extHostContext, this._extHostWorkspace, this._extHostConfiguration, this, this._extHostLogService, this._storage);
this._resolvers = Object.create(null);
this._started = false;
this._initialize();
@@ -235,7 +245,10 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
await initializeExtensionApi(this, this._extensionApiFactory, this._registry, configProvider);
// Do this when extension service exists, but extensions are not being activated yet.
await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._extHostLogService, this._mainThreadTelemetryProxy);
this._barrier.open();
this._almostReadyToRunExtensions.open();
await this._extHostWorkspace.waitForInitializeCall();
this._readyToRunExtensions.open();
} catch (err) {
errors.onUnexpectedError(err);
}
@@ -258,7 +271,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
public isActivated(extensionId: ExtensionIdentifier): boolean {
if (this._barrier.isOpen()) {
if (this._readyToRunExtensions.isOpen()) {
return this._activator.isActivated(extensionId);
}
return false;
@@ -285,11 +298,11 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {
return this._barrier.wait().then(_ => this._registry);
return this._readyToRunExtensions.wait().then(_ => this._registry);
}
public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI {
if (this._barrier.isOpen()) {
public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined {
if (this._readyToRunExtensions.isOpen()) {
return this._activator.getActivatedExtension(extensionId).exports;
} else {
return null;
@@ -314,7 +327,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private _deactivate(extensionId: ExtensionIdentifier): Promise<void> {
let result = Promise.resolve(undefined);
if (!this._barrier.isOpen()) {
if (!this._readyToRunExtensions.isOpen()) {
return result;
}
@@ -322,7 +335,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return result;
}
let extension = this._activator.getActivatedExtension(extensionId);
const extension = this._activator.getActivatedExtension(extensionId);
if (!extension) {
return result;
}
@@ -349,29 +362,24 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return result;
}
public addMessage(extensionId: ExtensionIdentifier, severity: Severity, message: string): void {
this._mainThreadExtensionsProxy.$addMessage(extensionId, severity, message);
}
// --- impl
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.identifier);
return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => {
const activationTimes = activatedExtension.activationTimes;
let activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
const activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.identifier, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes);
return activatedExtension;
}, (err) => {
this._mainThreadExtensionsProxy.$onExtensionActivationFailed(extensionDescription.identifier);
this._logExtensionActivationTimes(extensionDescription, reason, 'failure');
throw err;
});
}
private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) {
let event = getTelemetryActivationEvent(extensionDescription, reason);
const event = getTelemetryActivationEvent(extensionDescription, reason);
/* __GDPR__
"extensionActivationTimes" : {
"${include}": [
@@ -389,7 +397,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
let event = getTelemetryActivationEvent(extensionDescription, reason);
const event = getTelemetryActivationEvent(extensionDescription, reason);
/* __GDPR__
"activatePlugin" : {
"${include}": [
@@ -416,8 +424,8 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<IExtensionContext> {
let globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage);
let workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage);
const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage);
const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage);
this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
return Promise.all([
@@ -481,10 +489,10 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
console.error(err);
});
return this._handleWorkspaceContainsEagerExtensions(this._initData.workspace);
return this._handleWorkspaceContainsEagerExtensions(this._extHostWorkspace.workspace);
}
private _handleWorkspaceContainsEagerExtensions(workspace: IWorkspaceData): Promise<void> {
private _handleWorkspaceContainsEagerExtensions(workspace: IWorkspace | undefined): Promise<void> {
if (!workspace || workspace.folders.length === 0) {
return Promise.resolve(undefined);
}
@@ -496,7 +504,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
).then(() => { });
}
private _handleWorkspaceContainsEagerExtension(workspace: IWorkspaceData, desc: IExtensionDescription): Promise<void> {
private _handleWorkspaceContainsEagerExtension(workspace: IWorkspace, desc: IExtensionDescription): Promise<void> {
const activationEvents = desc.activationEvents;
if (!activationEvents) {
return Promise.resolve(undefined);
@@ -526,7 +534,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return Promise.all([fileNamePromise, globPatternPromise]).then(() => { });
}
private async _activateIfFileName(workspace: IWorkspaceData, extensionId: ExtensionIdentifier, fileName: string): Promise<void> {
private async _activateIfFileName(workspace: IWorkspace, extensionId: ExtensionIdentifier, fileName: string): Promise<void> {
// find exact path
for (const { uri } of workspace.folders) {
@@ -558,7 +566,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
.then(undefined, err => console.error(err));
}, ExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT);
let exists: boolean;
let exists: boolean = false;
try {
exists = await searchP;
} catch (err) {
@@ -590,19 +598,18 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
private _doHandleExtensionTests(): Promise<void> {
if (!this._initData.environment.extensionTestsPath || !this._initData.environment.extensionDevelopmentLocationURI) {
const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) {
return Promise.resolve(undefined);
}
if (this._initData.autoStart) {
return Promise.resolve(undefined); // https://github.com/Microsoft/vscode/issues/66936
}
const extensionTestsPath = originalFSPath(extensionTestsLocationURI);
// Require the test runner via node require from the provided path
let testRunner: ITestRunner;
let requireError: Error;
let testRunner: ITestRunner | undefined;
let requireError: Error | undefined;
try {
testRunner = <any>require.__$__nodeRequire(this._initData.environment.extensionTestsPath);
testRunner = <any>require.__$__nodeRequire(extensionTestsPath);
} catch (error) {
requireError = error;
}
@@ -610,7 +617,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
// Execute the runner if it follows our spec
if (testRunner && typeof testRunner.run === 'function') {
return new Promise<void>((c, e) => {
testRunner.run(this._initData.environment.extensionTestsPath, (error, failures) => {
testRunner!.run(extensionTestsPath, (error, failures) => {
if (error) {
e(error.toString());
} else {
@@ -628,7 +635,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
this._gracefulExit(1 /* ERROR */);
}
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", this._initData.environment.extensionTestsPath)));
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath)));
}
private _gracefulExit(code: number): void {
@@ -643,7 +650,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
this._started = true;
return this._barrier.wait()
return this._readyToRunExtensions.wait()
.then(() => this._handleEagerExtensions())
.then(() => this._handleExtensionTests())
.then(() => {
@@ -651,10 +658,40 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
});
}
// -- called by extensions
public registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable {
this._resolvers[authorityPrefix] = resolver;
return toDisposable(() => {
delete this._resolvers[authorityPrefix];
});
}
// -- called by main thread
public async $resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> {
throw new Error(`Not implemented`);
const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) {
throw new Error(`Not an authority that can be resolved!`);
}
const authorityPrefix = remoteAuthority.substr(0, authorityPlusIndex);
await this._almostReadyToRunExtensions.wait();
await this._activateByEvent(`onResolveRemoteAuthority:${authorityPrefix}`, false);
const resolver = this._resolvers[authorityPrefix];
if (!resolver) {
throw new Error(`No resolver available for ${authorityPrefix}`);
}
const result = await resolver.resolve(remoteAuthority);
return {
authority: remoteAuthority,
host: result.host,
port: result.port,
debugListenPort: result.debugListenPort,
debugConnectPort: result.debugConnectPort,
};
}
public $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
@@ -664,16 +701,19 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
public $activateByEvent(activationEvent: string): Promise<void> {
return (
this._barrier.wait()
this._readyToRunExtensions.wait()
.then(_ => this._activateByEvent(activationEvent, false))
);
}
public $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise<void> {
return (
this._barrier.wait()
.then(_ => this._activateById(extensionId, new ExtensionActivatedByEvent(false, activationEvent)))
);
public async $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise<boolean> {
await this._readyToRunExtensions.wait();
if (!this._registry.getExtensionDescription(extensionId)) {
// unknown extension => ignore
return false;
}
await this._activateById(extensionId, new ExtensionActivatedByEvent(false, activationEvent));
return true;
}
public async $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void> {
@@ -708,7 +748,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
public async $test_down(size: number): Promise<Buffer> {
let b = Buffer.alloc(size, Math.random() % 256);
const b = Buffer.alloc(size, Math.random() % 256);
return b;
}
@@ -744,7 +784,7 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription
"reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
let event = {
const event = {
id: extensionDescription.identifier.value,
name: extensionDescription.name,
extensionVersion: extensionDescription.version,
@@ -755,4 +795,4 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription
};
return event;
}
}

View File

@@ -8,19 +8,19 @@ import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystem
import * as vscode from 'vscode';
import * as files from 'vs/platform/files/common/files';
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
import { FileChangeType, DocumentLink } from 'vs/workbench/api/node/extHostTypes';
import { FileChangeType } from 'vs/workbench/api/node/extHostTypes';
import * as typeConverter from 'vs/workbench/api/node/extHostTypeConverters';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { Schemas } from 'vs/base/common/network';
import { ResourceLabelFormatter } from 'vs/platform/label/common/label';
import { State, StateMachine, LinkComputer } from 'vs/editor/common/modes/linkComputer';
import { State, StateMachine, LinkComputer, Edge } from 'vs/editor/common/modes/linkComputer';
import { commonPrefixLength } from 'vs/base/common/strings';
import { CharCode } from 'vs/base/common/charCode';
class FsLinkProvider {
private _schemes: string[] = [];
private _stateMachine: StateMachine;
private _stateMachine?: StateMachine;
add(scheme: string): void {
this._stateMachine = undefined;
@@ -28,7 +28,7 @@ class FsLinkProvider {
}
delete(scheme: string): void {
let idx = this._schemes.indexOf(scheme);
const idx = this._schemes.indexOf(scheme);
if (idx >= 0) {
this._schemes.splice(idx, 1);
this._stateMachine = undefined;
@@ -41,8 +41,8 @@ class FsLinkProvider {
// sort and compute common prefix with previous scheme
// then build state transitions based on the data
const schemes = this._schemes.sort();
const edges = [];
let prevScheme: string;
const edges: Edge[] = [];
let prevScheme: string | undefined;
let prevState: State;
let nextState = State.LastKnownState;
for (const scheme of schemes) {
@@ -94,11 +94,9 @@ class FsLinkProvider {
}, this._stateMachine);
for (const link of links) {
try {
let uri = URI.parse(link.url, true);
result.push(new DocumentLink(typeConverter.Range.to(link.range), uri));
} catch (err) {
// ignore
const docLink = typeConverter.DocumentLink.to(link);
if (docLink.target) {
result.push(docLink);
}
}
return result;
@@ -114,6 +112,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
private readonly _watches = new Map<number, IDisposable>();
private _linkProviderRegistration: IDisposable;
// Used as a handle both for file system providers and resource label formatters (being lazy)
private _handlePool: number = 0;
constructor(mainContext: IMainContext, private _extHostLanguageFeatures: ExtHostLanguageFeatures) {
@@ -173,14 +172,14 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
this._proxy.$registerFileSystemProvider(handle, scheme, capabilites);
const subscription = provider.onDidChangeFile(event => {
let mapped: IFileChangeDto[] = [];
const 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;
let newType: files.FileChangeType | undefined;
switch (type) {
case FileChangeType.Changed:
newType = files.FileChangeType.UPDATED;
@@ -191,6 +190,8 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
case FileChangeType.Deleted:
newType = files.FileChangeType.DELETED;
break;
default:
throw new Error('Unknown FileChangeType');
}
mapped.push({ resource, type: newType });
}
@@ -206,8 +207,13 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
});
}
setUriFormatter(formatter: ResourceLabelFormatter): void {
this._proxy.$setUriFormatter(formatter);
registerResourceLabelFormatter(formatter: ResourceLabelFormatter): IDisposable {
const handle = this._handlePool++;
this._proxy.$registerResourceLabelFormatter(handle, formatter);
return toDisposable(() => {
this._proxy.$unregisterResourceLabelFormatter(handle);
});
}
private static _asIStat(stat: vscode.FileStat): files.IStat {
@@ -215,65 +221,51 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
return { type, ctime, mtime, size };
}
private _checkProviderExists(handle: number): void {
if (!this._fsProvider.has(handle)) {
const err = new Error();
err.name = 'ENOPRO';
err.message = `no provider`;
throw err;
}
}
$stat(handle: number, resource: UriComponents): Promise<files.IStat> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat);
return Promise.resolve(this.getProvider(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat);
}
$readdir(handle: number, resource: UriComponents): Promise<[string, files.FileType][]> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).readDirectory(URI.revive(resource)));
return Promise.resolve(this.getProvider(handle).readDirectory(URI.revive(resource)));
}
$readFile(handle: number, resource: UriComponents): Promise<Buffer> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).readFile(URI.revive(resource))).then(data => {
return Promise.resolve(this.getProvider(handle).readFile(URI.revive(resource))).then(data => {
return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
});
}
$writeFile(handle: number, resource: UriComponents, content: Buffer, opts: files.FileWriteOptions): Promise<void> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).writeFile(URI.revive(resource), content, opts));
return Promise.resolve(this.getProvider(handle).writeFile(URI.revive(resource), content, opts));
}
$delete(handle: number, resource: UriComponents, opts: files.FileDeleteOptions): Promise<void> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).delete(URI.revive(resource), opts));
return Promise.resolve(this.getProvider(handle).delete(URI.revive(resource), opts));
}
$rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): Promise<void> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts));
return Promise.resolve(this.getProvider(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts));
}
$copy(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): Promise<void> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).copy(URI.revive(oldUri), URI.revive(newUri), opts));
const provider = this.getProvider(handle);
if (!provider.copy) {
throw new Error('FileSystemProvider does not implement "copy"');
}
return Promise.resolve(provider.copy(URI.revive(oldUri), URI.revive(newUri), opts));
}
$mkdir(handle: number, resource: UriComponents): Promise<void> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).createDirectory(URI.revive(resource)));
return Promise.resolve(this.getProvider(handle).createDirectory(URI.revive(resource)));
}
$watch(handle: number, session: number, resource: UriComponents, opts: files.IWatchOptions): void {
this._checkProviderExists(handle);
let subscription = this._fsProvider.get(handle).watch(URI.revive(resource), opts);
const subscription = this.getProvider(handle).watch(URI.revive(resource), opts);
this._watches.set(session, subscription);
}
$unwatch(session: number): void {
let subscription = this._watches.get(session);
$unwatch(_handle: number, session: number): void {
const subscription = this._watches.get(session);
if (subscription) {
subscription.dispose();
this._watches.delete(session);
@@ -281,26 +273,48 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
}
$open(handle: number, resource: UriComponents, opts: files.FileOpenOptions): Promise<number> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).open(URI.revive(resource), opts));
const provider = this.getProvider(handle);
if (!provider.open) {
throw new Error('FileSystemProvider does not implement "open"');
}
return Promise.resolve(provider.open(URI.revive(resource), opts));
}
$close(handle: number, fd: number): Promise<void> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).close(fd));
const provider = this.getProvider(handle);
if (!provider.close) {
throw new Error('FileSystemProvider does not implement "close"');
}
return Promise.resolve(provider.close(fd));
}
$read(handle: number, fd: number, pos: number, length: number): Promise<Buffer> {
this._checkProviderExists(handle);
const provider = this.getProvider(handle);
if (!provider.read) {
throw new Error('FileSystemProvider does not implement "read"');
}
const data = Buffer.allocUnsafe(length);
return Promise.resolve(this._fsProvider.get(handle).read(fd, pos, data, 0, length)).then(read => {
return Promise.resolve(provider.read(fd, pos, data, 0, length)).then(read => {
return data.slice(0, read); // don't send zeros
});
}
$write(handle: number, fd: number, pos: number, data: Buffer): Promise<number> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).write(fd, pos, data, 0, data.length));
const provider = this.getProvider(handle);
if (!provider.write) {
throw new Error('FileSystemProvider does not implement "write"');
}
return Promise.resolve(provider.write(fd, pos, data, 0, data.length));
}
private getProvider(handle: number): vscode.FileSystemProvider {
const provider = this._fsProvider.get(handle);
if (!provider) {
const err = new Error();
err.name = 'ENOPRO';
err.message = `no provider`;
throw err;
}
return provider;
}
}

View File

@@ -8,11 +8,11 @@ import { AsyncEmitter, Emitter, Event } from 'vs/base/common/event';
import { IRelativePattern, parse } from 'vs/base/common/glob';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as vscode from 'vscode';
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, ResourceFileEditDto, ResourceTextEditDto, MainThreadTextEditorsShape } from './extHost.protocol';
import * as typeConverter from './extHostTypeConverters';
import { Disposable, WorkspaceEdit } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
class FileSystemWatcher implements vscode.FileSystemWatcher {
@@ -49,10 +49,10 @@ class FileSystemWatcher implements vscode.FileSystemWatcher {
const parsedPattern = parse(globPattern);
let subscription = dispatcher(events => {
const subscription = dispatcher(events => {
if (!ignoreCreateEvents) {
for (let created of events.created) {
let uri = URI.revive(created);
const uri = URI.revive(created);
if (parsedPattern(uri.fsPath)) {
this._onDidCreate.fire(uri);
}
@@ -60,7 +60,7 @@ class FileSystemWatcher implements vscode.FileSystemWatcher {
}
if (!ignoreChangeEvents) {
for (let changed of events.changed) {
let uri = URI.revive(changed);
const uri = URI.revive(changed);
if (parsedPattern(uri.fsPath)) {
this._onDidChange.fire(uri);
}
@@ -68,7 +68,7 @@ class FileSystemWatcher implements vscode.FileSystemWatcher {
}
if (!ignoreDeleteEvents) {
for (let deleted of events.deleted) {
let uri = URI.revive(deleted);
const uri = URI.revive(deleted);
if (parsedPattern(uri.fsPath)) {
this._onDidDelete.fire(uri);
}
@@ -163,13 +163,13 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
bucket.push(wrappedThenable);
}
};
}).then(() => {
}).then((): any => {
if (edits.length === 0) {
return undefined;
}
// flatten all WorkspaceEdits collected via waitUntil-call
// and apply them in one go.
let allEdits = new Array<Array<ResourceFileEditDto | ResourceTextEditDto>>();
const allEdits = new Array<Array<ResourceFileEditDto | ResourceTextEditDto>>();
for (let edit of edits) {
if (edit) { // sparse array
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);

File diff suppressed because it is too large Load Diff

View File

@@ -24,9 +24,10 @@ export class ExtHostLanguages {
return this._proxy.$getLanguages();
}
changeLanguage(uri: vscode.Uri, languageId: string): Promise<vscode.TextDocument> {
changeLanguage(uri: vscode.Uri, languageId: string): Promise<vscode.TextDocument | undefined> {
return this._proxy.$changeLanguage(uri, languageId).then(() => {
return this._documents.getDocumentData(uri).document;
const data = this._documents.getDocumentData(uri);
return data ? data.document : undefined;
});
}
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { join } from 'vs/base/common/paths';
import { join } from 'vs/base/common/path';
import { ILogService, DelegatedLogService, LogLevel } from 'vs/platform/log/common/log';
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
import { ExtHostLogServiceShape } from 'vs/workbench/api/node/extHost.protocol';
@@ -11,7 +11,6 @@ import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/commo
import { URI } from 'vs/base/common/uri';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
private _logsPath: string;

View File

@@ -6,7 +6,7 @@
import Severity from 'vs/base/common/severity';
import * as vscode from 'vscode';
import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
function isMessageItem(item: any): item is vscode.MessageItem {
return item && item.title;
@@ -24,7 +24,7 @@ export class ExtHostMessageService {
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Promise<vscode.MessageItem | undefined>;
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Promise<string | vscode.MessageItem | undefined> {
let options: MainThreadMessageOptions = { extension };
const options: MainThreadMessageOptions = { extension };
let items: (string | vscode.MessageItem)[];
if (typeof optionsOrFirstItem === 'string' || isMessageItem(optionsOrFirstItem)) {
@@ -37,12 +37,12 @@ export class ExtHostMessageService {
const commands: { title: string; isCloseAffordance: boolean; handle: number; }[] = [];
for (let handle = 0; handle < items.length; handle++) {
let command = items[handle];
const command = items[handle];
if (typeof command === 'string') {
commands.push({ title: command, handle, isCloseAffordance: false });
} else if (typeof command === 'object') {
let { title, isCloseAffordance } = command;
commands.push({ title, isCloseAffordance, handle });
commands.push({ title, isCloseAffordance: !!isCloseAffordance, handle });
} else {
console.warn('Invalid message item:', command);
}

View File

@@ -6,8 +6,8 @@
import { MainContext, MainThreadOutputServiceShape, IMainContext, ExtHostOutputServiceShape } from './extHost.protocol';
import * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { posix } from 'path';
import { OutputAppender } from 'vs/platform/output/node/outputAppender';
import { join } from 'vs/base/common/path';
import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender';
import { toLocalISOString } from 'vs/base/common/date';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
@@ -23,7 +23,7 @@ export abstract class AbstractExtHostOutputChannel extends Disposable implements
protected readonly _onDidAppend: Emitter<void> = this._register(new Emitter<void>());
readonly onDidAppend: Event<void> = this._onDidAppend.event;
constructor(name: string, log: boolean, file: URI, proxy: MainThreadOutputServiceShape) {
constructor(name: string, log: boolean, file: URI | undefined, proxy: MainThreadOutputServiceShape) {
super();
this._name = name;
@@ -58,7 +58,7 @@ export abstract class AbstractExtHostOutputChannel extends Disposable implements
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
this.validate();
this._id.then(id => this._proxy.$reveal(id, typeof columnOrPreserveFocus === 'boolean' ? columnOrPreserveFocus : preserveFocus));
this._id.then(id => this._proxy.$reveal(id, !!(typeof columnOrPreserveFocus === 'boolean' ? columnOrPreserveFocus : preserveFocus)));
}
hide(): void {
@@ -86,7 +86,7 @@ export abstract class AbstractExtHostOutputChannel extends Disposable implements
export class ExtHostPushOutputChannel extends AbstractExtHostOutputChannel {
constructor(name: string, proxy: MainThreadOutputServiceShape) {
super(name, false, null, proxy);
super(name, false, undefined, proxy);
}
append(value: string): void {
@@ -103,7 +103,7 @@ export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChann
constructor(name: string, outputDir: string, proxy: MainThreadOutputServiceShape) {
const fileName = `${ExtHostOutputChannelBackedByFile._namePool++}-${name}`;
const file = URI.file(posix.join(outputDir, `${fileName}.log`));
const file = URI.file(join(outputDir, `${fileName}.log`));
super(name, false, file, proxy);
this._appender = new OutputAppender(fileName, file.fsPath);
@@ -150,7 +150,7 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape {
private _visibleChannelDisposable: IDisposable;
constructor(logsLocation: URI, mainContext: IMainContext) {
this._outputDir = posix.join(logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
this._outputDir = join(logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
this._proxy = mainContext.getProxy(MainContext.MainThreadOutputService);
}

View File

@@ -6,11 +6,11 @@
import { ProgressOptions } from 'vscode';
import { MainThreadProgressShape, ExtHostProgressShape } from './extHost.protocol';
import { ProgressLocation } from './extHostTypeConverters';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { Progress, IProgressStep } 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';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
export class ExtHostProgress implements ExtHostProgressShape {
@@ -27,11 +27,11 @@ export class ExtHostProgress implements ExtHostProgressShape {
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);
return this._withProgress(handle, task, !!cancellable);
}
private _withProgress<R>(handle: number, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>, cancellable: boolean): Thenable<R> {
let source: CancellationTokenSource;
let source: CancellationTokenSource | undefined;
if (cancellable) {
source = new CancellationTokenSource();
this._mapHandleToCancellationSource.set(handle, source);
@@ -48,7 +48,7 @@ export class ExtHostProgress implements ExtHostProgressShape {
let p: Thenable<R>;
try {
p = task(new ProgressCallback(this._proxy, handle), cancellable ? source.token : CancellationToken.None);
p = task(new ProgressCallback(this._proxy, handle), cancellable && source ? source.token : CancellationToken.None);
} catch (err) {
progressEnd(handle);
throw err;

View File

@@ -8,30 +8,31 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { IExtHostWorkspaceProvider } from 'vs/workbench/api/node/extHostWorkspace';
import { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode';
import { ExtHostQuickOpenShape, IMainContext, MainContext, MainThreadQuickOpenShape, TransferQuickPickItems, TransferQuickInput, TransferQuickInputButton } from './extHost.protocol';
import { URI } from 'vs/base/common/uri';
import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/node/extHostTypes';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { coalesce } from 'vs/base/common/arrays';
export type Item = string | QuickPickItem;
export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
private _proxy: MainThreadQuickOpenShape;
private _workspace: ExtHostWorkspace;
private _workspace: IExtHostWorkspaceProvider;
private _commands: ExtHostCommands;
private _onDidSelectItem: (handle: number) => void;
private _validateInput: (input: string) => string | Thenable<string>;
private _onDidSelectItem?: (handle: number) => void;
private _validateInput?: (input: string) => string | undefined | null | Thenable<string | undefined | null>;
private _sessions = new Map<number, ExtHostQuickInput>();
private _instances = 0;
constructor(mainContext: IMainContext, workspace: ExtHostWorkspace, commands: ExtHostCommands) {
constructor(mainContext: IMainContext, workspace: IExtHostWorkspaceProvider, commands: ExtHostCommands) {
this._proxy = mainContext.getProxy(MainContext.MainThreadQuickOpen);
this._workspace = workspace;
this._commands = commands;
@@ -67,15 +68,15 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
return itemsPromise.then(items => {
let pickItems: TransferQuickPickItems[] = [];
const pickItems: TransferQuickPickItems[] = [];
for (let handle = 0; handle < items.length; handle++) {
let item = items[handle];
const item = items[handle];
let label: string;
let description: string;
let detail: string;
let picked: boolean;
let alwaysShow: boolean;
let description: string | undefined;
let detail: string | undefined;
let picked: boolean | undefined;
let alwaysShow: boolean | undefined;
if (typeof item === 'string') {
label = item;
@@ -99,7 +100,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
// handle selection changes
if (options && typeof options.onDidSelectItem === 'function') {
this._onDidSelectItem = (handle) => {
options.onDidSelectItem(items[handle]);
options.onDidSelectItem!(items[handle]);
};
}
@@ -137,7 +138,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Promise<string> {
// global validate fn used in callback below
this._validateInput = options && options.validateInput;
this._validateInput = options ? options.validateInput : undefined;
return this._proxy.$input(options, typeof this._validateInput === 'function', token)
.then(undefined, err => {
@@ -149,22 +150,25 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
});
}
$validateInput(input: string): Promise<string> {
$validateInput(input: string): Promise<string | null | undefined> {
if (this._validateInput) {
return asPromise(() => this._validateInput(input));
return asPromise(() => this._validateInput!(input));
}
return undefined;
return Promise.resolve(undefined);
}
// ---- workspace folder picker
showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token = CancellationToken.None): Promise<WorkspaceFolder> {
return this._commands.executeCommand('_workbench.pickWorkspaceFolder', [options]).then((selectedFolder: WorkspaceFolder) => {
showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token = CancellationToken.None): Promise<WorkspaceFolder | undefined> {
return this._commands.executeCommand('_workbench.pickWorkspaceFolder', [options]).then(async (selectedFolder: WorkspaceFolder) => {
if (!selectedFolder) {
return undefined;
}
return this._workspace.getWorkspaceFolders().filter(folder => folder.uri.toString() === selectedFolder.uri.toString())[0];
const workspaceFolders = await this._workspace.getWorkspaceFolders2();
if (!workspaceFolders) {
return undefined;
}
return workspaceFolders.filter(folder => folder.uri.toString() === selectedFolder.uri.toString())[0];
});
}
@@ -382,7 +386,9 @@ class ExtHostQuickInput implements QuickInput {
_fireDidTriggerButton(handle: number) {
const button = this._handlesToButtons.get(handle);
this._onDidTriggerButtonEmitter.fire(button);
if (button) {
this._onDidTriggerButtonEmitter.fire(button);
}
}
_fireDidHide() {
@@ -437,9 +443,13 @@ class ExtHostQuickInput implements QuickInput {
}
}
function getIconUris(iconPath: QuickInputButton['iconPath']) {
function getIconUris(iconPath: QuickInputButton['iconPath']): { dark: URI, light?: URI } | undefined {
const dark = getDarkIconUri(iconPath);
const light = getLightIconUri(iconPath);
return { dark: getDarkIconUri(iconPath) || light, light };
if (!light && !dark) {
return undefined;
}
return { dark: (dark || light)!, light };
}
function getLightIconUri(iconPath: QuickInputButton['iconPath']) {
@@ -563,13 +573,13 @@ class ExtHostQuickPick<T extends QuickPickItem> extends ExtHostQuickInput implem
onDidChangeSelection = this._onDidChangeSelectionEmitter.event;
_fireDidChangeActive(handles: number[]) {
const items = handles.map(handle => this._handlesToItems.get(handle));
const items = coalesce(handles.map(handle => this._handlesToItems.get(handle)));
this._activeItems = items;
this._onDidChangeActiveEmitter.fire(items);
}
_fireDidChangeSelection(handles: number[]) {
const items = handles.map(handle => this._handlesToItems.get(handle));
const items = coalesce(handles.map(handle => this._handlesToItems.get(handle)));
this._selectedItems = items;
this._onDidChangeSelectionEmitter.fire(items);
}

View File

@@ -8,22 +8,21 @@ import { Event, Emitter } from 'vs/base/common/event';
import { debounce } from 'vs/base/common/decorators';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { asPromise } from 'vs/base/common/async';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape } from './extHost.protocol';
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, CommandDto } from './extHost.protocol';
import { sortedDiff } from 'vs/base/common/arrays';
import { comparePaths } from 'vs/base/common/comparers';
import * as vscode from 'vscode';
import { ISplice } from 'vs/base/common/sequence';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
type ProviderHandle = number;
type GroupHandle = number;
type ResourceStateHandle = number;
function getIconPath(decorations: vscode.SourceControlResourceThemableDecorations) {
function getIconPath(decorations?: vscode.SourceControlResourceThemableDecorations): string | undefined {
if (!decorations) {
return undefined;
} else if (typeof decorations.iconPath === 'string') {
@@ -60,7 +59,7 @@ function compareResourceStatesDecorations(a: vscode.SourceControlResourceDecorat
}
if (a.tooltip !== b.tooltip) {
return (a.tooltip || '').localeCompare(b.tooltip);
return (a.tooltip || '').localeCompare(b.tooltip || '');
}
result = compareResourceThemableDecorations(a, b);
@@ -205,7 +204,7 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox {
return this._visible;
}
set visible(visible: boolean | undefined) {
set visible(visible: boolean) {
visible = !!visible;
this._visible = visible;
this._proxy.$setInputBoxVisibility(this._sourceControlHandle, visible);
@@ -287,7 +286,7 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
return Promise.resolve(undefined);
}
return asPromise(() => this._commands.executeCommand(command.command, ...command.arguments));
return asPromise(() => this._commands.executeCommand(command.command, ...(command.arguments || [])));
}
_takeResourceStateSnapshot(): SCMRawResourceSplice[] {
@@ -309,11 +308,11 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
this._resourceStatesCommandsMap.set(handle, r.command);
}
if (lightIconPath || darkIconPath) {
if (lightIconPath) {
icons.push(lightIconPath);
}
if (darkIconPath !== lightIconPath) {
if (darkIconPath && (darkIconPath !== lightIconPath)) {
icons.push(darkIconPath);
}
@@ -442,7 +441,7 @@ class ExtHostSourceControl implements vscode.SourceControl {
this._statusBarCommands = statusBarCommands;
const internal = (statusBarCommands || []).map(c => this._commands.converter.toInternal(c));
const internal = (statusBarCommands || []).map(c => this._commands.converter.toInternal(c)) as CommandDto[];
this._proxy.$updateSourceControl(this.handle, { statusBarCommands: internal });
}
@@ -599,14 +598,12 @@ export class ExtHostSCM implements ExtHostSCMShape {
}
// Deprecated
getLastInputBox(extension: IExtensionDescription): ExtHostSCMInputBox {
getLastInputBox(extension: IExtensionDescription): ExtHostSCMInputBox | undefined {
this.logService.trace('ExtHostSCM#getLastInputBox', extension.identifier.value);
const sourceControls = this._sourceControlsByExtension.get(ExtensionIdentifier.toKey(extension.identifier));
const sourceControl = sourceControls && sourceControls[sourceControls.length - 1];
const inputBox = sourceControl && sourceControl.inputBox;
return inputBox;
return sourceControl && sourceControl.inputBox;
}
$provideOriginalResource(sourceControlHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise<UriComponents | null> {
@@ -615,11 +612,12 @@ export class ExtHostSCM implements ExtHostSCMShape {
const sourceControl = this._sourceControls.get(sourceControlHandle);
if (!sourceControl || !sourceControl.quickDiffProvider) {
if (!sourceControl || !sourceControl.quickDiffProvider || !sourceControl.quickDiffProvider.provideOriginalResource) {
return Promise.resolve(null);
}
return asPromise(() => sourceControl.quickDiffProvider.provideOriginalResource(uri, token));
return asPromise(() => sourceControl.quickDiffProvider!.provideOriginalResource!(uri, token))
.then<UriComponents | null>(r => r || null);
}
$onInputBoxValueChange(sourceControlHandle: number, value: string): Promise<void> {

View File

@@ -1,611 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import * as arrays from 'vs/base/common/arrays';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { canceled } from 'vs/base/common/errors';
import * as glob from 'vs/base/common/glob';
import * as resources from 'vs/base/common/resources';
import { StopWatch } from 'vs/base/common/stopwatch';
import * as strings from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer';
import { ICachedSearchStats, IFileIndexProviderStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, ISearchCompleteStats } from 'vs/platform/search/common/search';
import { IDirectoryEntry, IDirectoryTree, IInternalFileMatch } from 'vs/workbench/services/search/node/fileSearchManager';
import { QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/services/search/node/search';
import * as vscode from 'vscode';
interface IInternalSearchComplete<T = IFileSearchStats> {
limitHit: boolean;
results: IInternalFileMatch[];
stats: T;
}
export class FileIndexSearchEngine {
private filePattern?: string;
private normalizedFilePatternLowercase: string;
private includePattern?: glob.ParsedExpression;
private maxResults: number | null;
private exists: boolean;
private isLimitHit: boolean;
private resultCount: number;
private isCanceled: boolean;
private filesWalked = 0;
private dirsWalked = 0;
private activeCancellationTokens: Set<CancellationTokenSource>;
private globalExcludePattern?: glob.ParsedExpression;
constructor(private config: IFileQuery, private provider: vscode.FileIndexProvider) {
this.filePattern = config.filePattern;
this.includePattern = config.includePattern && glob.parse(config.includePattern);
this.maxResults = config.maxResults || null;
this.exists = !!config.exists;
this.resultCount = 0;
this.isLimitHit = false;
this.activeCancellationTokens = new Set<CancellationTokenSource>();
if (this.filePattern) {
this.normalizedFilePatternLowercase = strings.stripWildcards(this.filePattern).toLowerCase();
}
this.globalExcludePattern = config.excludePattern && glob.parse(config.excludePattern);
}
public cancel(): void {
this.isCanceled = true;
this.activeCancellationTokens.forEach(t => t.cancel());
this.activeCancellationTokens = new Set();
}
public search(_onResult: (match: IInternalFileMatch) => void): Promise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }> {
// Searches a single folder
const folderQuery = this.config.folderQueries[0];
return new Promise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }>((resolve, reject) => {
const onResult = (match: IInternalFileMatch) => {
this.resultCount++;
_onResult(match);
};
if (this.isCanceled) {
throw canceled();
}
// For each extra file
if (this.config.extraFileResources) {
this.config.extraFileResources
.forEach(extraFile => {
const extraFileStr = extraFile.toString(); // ?
const basename = path.basename(extraFileStr);
if (this.globalExcludePattern && this.globalExcludePattern(extraFileStr, basename)) {
return; // excluded
}
// File: Check for match on file pattern and include pattern
this.matchFile(onResult, { base: extraFile, basename });
});
}
return Promise.all(this.config.folderQueries.map(fq => this.searchInFolder(folderQuery, onResult))).then(stats => {
resolve({
isLimitHit: this.isLimitHit,
stats: {
directoriesWalked: this.dirsWalked,
filesWalked: this.filesWalked,
fileWalkTime: stats.map(s => s.fileWalkTime).reduce((s, c) => s + c, 0),
providerTime: stats.map(s => s.providerTime).reduce((s, c) => s + c, 0),
providerResultCount: stats.map(s => s.providerResultCount).reduce((s, c) => s + c, 0)
}
});
}, (errs: Error[]) => {
if (!Array.isArray(errs)) {
errs = [errs];
}
errs = arrays.coalesce(errs);
return Promise.reject(errs[0]);
});
});
}
private searchInFolder(fq: IFolderQuery<URI>, onResult: (match: IInternalFileMatch) => void): Promise<IFileIndexProviderStats> {
let cancellation = new CancellationTokenSource();
return new Promise((resolve, reject) => {
const options = this.getSearchOptionsForFolder(fq);
const tree = this.initDirectoryTree();
const queryTester = new QueryGlobTester(this.config, fq);
const noSiblingsClauses = !queryTester.hasSiblingExcludeClauses();
const onProviderResult = (uri: URI) => {
if (this.isCanceled) {
return;
}
// TODO@rob - ???
const relativePath = path.relative(fq.folder.path, uri.path);
if (noSiblingsClauses) {
const basename = path.basename(uri.path);
this.matchFile(onResult, { base: fq.folder, relativePath, basename, original: uri });
return;
}
// TODO: Optimize siblings clauses with ripgrep here.
this.addDirectoryEntries(tree, fq.folder, relativePath, onResult);
};
let providerSW: StopWatch;
let providerTime: number;
let fileWalkTime: number;
new Promise(resolve => process.nextTick(resolve))
.then(() => {
this.activeCancellationTokens.add(cancellation);
providerSW = StopWatch.create();
return this.provider.provideFileIndex(options, cancellation.token);
})
.then(results => {
providerTime = providerSW.elapsed();
const postProcessSW = StopWatch.create();
this.activeCancellationTokens.delete(cancellation);
if (this.isCanceled) {
return null;
}
results!.forEach(onProviderResult);
this.matchDirectoryTree(tree, queryTester, onResult);
fileWalkTime = postProcessSW.elapsed();
return null;
}).then(
() => {
cancellation.dispose();
resolve(<IFileIndexProviderStats>{
providerTime,
fileWalkTime,
directoriesWalked: this.dirsWalked,
filesWalked: this.filesWalked
});
},
err => {
cancellation.dispose();
reject(err);
});
});
}
private getSearchOptionsForFolder(fq: IFolderQuery<URI>): vscode.FileIndexOptions {
const includes = resolvePatternsForProvider(this.config.includePattern, fq.includePattern);
const excludes = resolvePatternsForProvider(this.config.excludePattern, fq.excludePattern);
return {
folder: fq.folder,
excludes,
includes,
useIgnoreFiles: !fq.disregardIgnoreFiles,
useGlobalIgnoreFiles: !fq.disregardGlobalIgnoreFiles,
followSymlinks: !fq.ignoreSymlinks
};
}
private initDirectoryTree(): IDirectoryTree {
const tree: IDirectoryTree = {
rootEntries: [],
pathToEntries: Object.create(null)
};
tree.pathToEntries['.'] = tree.rootEntries;
return tree;
}
private addDirectoryEntries({ pathToEntries }: IDirectoryTree, base: URI, relativeFile: string, onResult: (result: IInternalFileMatch) => void) {
// Support relative paths to files from a root resource (ignores excludes)
if (relativeFile === this.filePattern) {
const basename = path.basename(this.filePattern);
this.matchFile(onResult, { base: base, relativePath: this.filePattern, basename });
}
function add(relativePath: string) {
const basename = path.basename(relativePath);
const dirname = path.dirname(relativePath);
let entries = pathToEntries[dirname];
if (!entries) {
entries = pathToEntries[dirname] = [];
add(dirname);
}
entries.push({
base,
relativePath,
basename
});
}
add(relativeFile);
}
private matchDirectoryTree({ rootEntries, pathToEntries }: IDirectoryTree, queryTester: QueryGlobTester, onResult: (result: IInternalFileMatch) => void) {
const self = this;
const filePattern = this.filePattern;
function matchDirectory(entries: IDirectoryEntry[]) {
self.dirsWalked++;
for (let i = 0, n = entries.length; i < n; i++) {
const entry = entries[i];
const { relativePath, basename } = entry;
// Check exclude pattern
// If the user searches for the exact file name, we adjust the glob matching
// to ignore filtering by siblings because the user seems to know what she
// is searching for and we want to include the result in that case anyway
const hasSibling = glob.hasSiblingFn(() => entries.map(entry => entry.basename));
if (!queryTester.includedInQuerySync(relativePath, basename, filePattern !== basename ? hasSibling : undefined)) {
continue;
}
const sub = pathToEntries[relativePath];
if (sub) {
matchDirectory(sub);
} else {
self.filesWalked++;
if (relativePath === filePattern) {
continue; // ignore file if its path matches with the file pattern because that is already matched above
}
self.matchFile(onResult, entry);
}
if (self.isLimitHit) {
break;
}
}
}
matchDirectory(rootEntries);
}
private matchFile(onResult: (result: IInternalFileMatch) => void, candidate: IInternalFileMatch): void {
if (this.isFilePatternMatch(candidate.relativePath!) && (!this.includePattern || this.includePattern(candidate.relativePath!, candidate.basename))) {
if (this.exists || (this.maxResults && this.resultCount >= this.maxResults)) {
this.isLimitHit = true;
this.cancel();
}
if (!this.isLimitHit) {
onResult(candidate);
}
}
}
private isFilePatternMatch(path: string): boolean {
// Check for search pattern
if (this.filePattern) {
if (this.filePattern === '*') {
return true; // support the all-matching wildcard
}
return strings.fuzzyContains(path, this.normalizedFilePatternLowercase);
}
// No patterns means we match all
return true;
}
}
export class FileIndexSearchManager {
private static readonly BATCH_SIZE = 512;
private caches: { [cacheKey: string]: Cache; } = Object.create(null);
private readonly folderCacheKeys = new Map<string, Set<string>>();
public fileSearch(config: IFileQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): Promise<ISearchCompleteStats> {
if (config.sortByScore) {
let sortedSearch = this.trySortedSearchFromCache(config, token);
if (!sortedSearch) {
const engineConfig = config.maxResults ?
{
...config,
...{ maxResults: null }
} :
config;
const engine = new FileIndexSearchEngine(<any>engineConfig, provider);
sortedSearch = this.doSortedSearch(engine, config, token);
}
return sortedSearch.then(complete => {
this.sendAsBatches(complete.results, onBatch, FileIndexSearchManager.BATCH_SIZE);
return complete;
});
}
const engine = new FileIndexSearchEngine(config, provider);
return this.doSearch(engine, token)
.then(complete => {
this.sendAsBatches(complete.results, onBatch, FileIndexSearchManager.BATCH_SIZE);
return <ISearchCompleteStats>{
limitHit: complete.limitHit,
stats: {
type: 'fileIndexProvider',
detailStats: complete.stats,
fromCache: false,
resultCount: complete.results.length
}
};
});
}
private getFolderCacheKey(config: IFileQuery): string {
const uri = config.folderQueries[0].folder.toString();
const folderCacheKey = config.cacheKey && `${uri}_${config.cacheKey}`;
if (!this.folderCacheKeys.get(config.cacheKey!)) {
this.folderCacheKeys.set(config.cacheKey!, new Set());
}
this.folderCacheKeys.get(config.cacheKey!)!.add(folderCacheKey!);
return folderCacheKey!;
}
private rawMatchToSearchItem(match: IInternalFileMatch): IFileMatch {
return {
resource: match.original || resources.joinPath(match.base, match.relativePath!)
};
}
private doSortedSearch(engine: FileIndexSearchEngine, config: IFileQuery, token: CancellationToken): Promise<IInternalSearchComplete> {
let allResultsPromise = createCancelablePromise<IInternalSearchComplete<IFileIndexProviderStats>>(token => {
return this.doSearch(engine, token);
});
const folderCacheKey = this.getFolderCacheKey(config);
let cache: Cache;
if (folderCacheKey) {
cache = this.getOrCreateCache(folderCacheKey);
const cacheRow: ICacheRow = {
promise: allResultsPromise,
resolved: false
};
cache.resultsToSearchCache[config.filePattern!] = cacheRow;
allResultsPromise.then(() => {
cacheRow.resolved = true;
}, err => {
delete cache.resultsToSearchCache[config.filePattern!];
});
allResultsPromise = this.preventCancellation(allResultsPromise);
}
return Promise.resolve<IInternalSearchComplete>(
allResultsPromise.then(complete => {
const scorerCache: ScorerCache = cache ? cache.scorerCache : Object.create(null);
const sortSW = (typeof config.maxResults !== 'number' || config.maxResults > 0) && StopWatch.create();
return this.sortResults(config, complete.results, scorerCache, token)
.then(sortedResults => {
// sortingTime: -1 indicates a "sorted" search that was not sorted, i.e. populating the cache when quickopen is opened.
// Contrasting with findFiles which is not sorted and will have sortingTime: undefined
const sortingTime = sortSW ? sortSW.elapsed() : -1;
return <IInternalSearchComplete>{
limitHit: complete.limitHit || typeof config.maxResults === 'number' && complete.results.length > config.maxResults, // ??
results: sortedResults,
stats: {
detailStats: complete.stats,
fromCache: false,
resultCount: sortedResults.length,
sortingTime,
type: 'fileIndexProvider'
}
};
});
}));
}
private getOrCreateCache(cacheKey: string): Cache {
const existing = this.caches[cacheKey];
if (existing) {
return existing;
}
return this.caches[cacheKey] = new Cache();
}
private trySortedSearchFromCache(config: IFileQuery, token: CancellationToken): Promise<IInternalSearchComplete> | undefined {
const folderCacheKey = this.getFolderCacheKey(config);
const cache = folderCacheKey && this.caches[folderCacheKey];
if (!cache) {
return undefined;
}
const cached = this.getResultsFromCache(cache, config.filePattern!, token);
if (cached) {
return cached.then(complete => {
const sortSW = StopWatch.create();
return this.sortResults(config, complete.results, cache.scorerCache, token)
.then(sortedResults => {
if (token && token.isCancellationRequested) {
throw canceled();
}
return <IInternalSearchComplete<IFileSearchStats>>{
limitHit: complete.limitHit || typeof config.maxResults === 'number' && complete.results.length > config.maxResults,
results: sortedResults,
stats: {
fromCache: true,
detailStats: complete.stats,
type: 'fileIndexProvider',
resultCount: sortedResults.length,
sortingTime: sortSW.elapsed()
}
};
});
});
}
return undefined;
}
private sortResults(config: IFileQuery, results: IInternalFileMatch[], scorerCache: ScorerCache, token: CancellationToken): Promise<IInternalFileMatch[]> {
// we use the same compare function that is used later when showing the results using fuzzy scoring
// this is very important because we are also limiting the number of results by config.maxResults
// and as such we want the top items to be included in this result set if the number of items
// exceeds config.maxResults.
const query = prepareQuery(config.filePattern!);
const compare = (matchA: IInternalFileMatch, matchB: IInternalFileMatch) => compareItemsByScore(matchA, matchB, query, true, FileMatchItemAccessor, scorerCache);
return arrays.topAsync(results, compare, config.maxResults!, 10000, token);
}
private sendAsBatches(rawMatches: IInternalFileMatch[], onBatch: (batch: IFileMatch[]) => void, batchSize: number) {
const serializedMatches = rawMatches.map(rawMatch => this.rawMatchToSearchItem(rawMatch));
if (batchSize && batchSize > 0) {
for (let i = 0; i < serializedMatches.length; i += batchSize) {
onBatch(serializedMatches.slice(i, i + batchSize));
}
} else {
onBatch(serializedMatches);
}
}
private getResultsFromCache(cache: Cache, searchValue: string, token: CancellationToken): Promise<IInternalSearchComplete<ICachedSearchStats>> | null {
const cacheLookupSW = StopWatch.create();
if (path.isAbsolute(searchValue)) {
return null; // bypass cache if user looks up an absolute path where matching goes directly on disk
}
// Find cache entries by prefix of search value
const hasPathSep = searchValue.indexOf(path.sep) >= 0;
let cacheRow: ICacheRow | undefined;
for (let previousSearch in cache.resultsToSearchCache) {
// If we narrow down, we might be able to reuse the cached results
if (strings.startsWith(searchValue, previousSearch)) {
if (hasPathSep && previousSearch.indexOf(path.sep) < 0) {
continue; // since a path character widens the search for potential more matches, require it in previous search too
}
const row = cache.resultsToSearchCache[previousSearch];
cacheRow = {
promise: this.preventCancellation(row.promise),
resolved: row.resolved
};
break;
}
}
if (!cacheRow) {
return null;
}
const cacheLookupTime = cacheLookupSW.elapsed();
const cacheFilterSW = StopWatch.create();
return new Promise<IInternalSearchComplete<ICachedSearchStats>>((c, e) => {
token.onCancellationRequested(() => e(canceled()));
cacheRow!.promise.then(complete => {
if (token && token.isCancellationRequested) {
e(canceled());
}
// Pattern match on results
let results: IInternalFileMatch[] = [];
const normalizedSearchValueLowercase = strings.stripWildcards(searchValue).toLowerCase();
for (let i = 0; i < complete.results.length; i++) {
let entry = complete.results[i];
// Check if this entry is a match for the search value
if (!strings.fuzzyContains(entry.relativePath!, normalizedSearchValueLowercase)) {
continue;
}
results.push(entry);
}
c(<IInternalSearchComplete<ICachedSearchStats>>{
limitHit: complete.limitHit,
results,
stats: {
cacheWasResolved: cacheRow!.resolved,
cacheLookupTime,
cacheFilterTime: cacheFilterSW.elapsed(),
cacheEntryCount: complete.results.length
}
});
}, e);
});
}
private doSearch(engine: FileIndexSearchEngine, token: CancellationToken): Promise<IInternalSearchComplete<IFileIndexProviderStats>> {
token.onCancellationRequested(() => engine.cancel());
const results: IInternalFileMatch[] = [];
const onResult = match => results.push(match);
return engine.search(onResult).then(result => {
return <IInternalSearchComplete<IFileIndexProviderStats>>{
limitHit: result.isLimitHit,
results,
stats: result.stats
};
});
}
public clearCache(cacheKey: string): void {
const expandedKeys = this.folderCacheKeys.get(cacheKey);
if (!expandedKeys) {
return undefined;
}
expandedKeys.forEach(key => delete this.caches[key]);
this.folderCacheKeys.delete(cacheKey);
return undefined;
}
private preventCancellation<C>(promise: CancelablePromise<C>): CancelablePromise<C> {
return new class implements CancelablePromise<C> {
cancel() {
// Do nothing
}
then(resolve, reject) {
return promise.then(resolve, reject);
}
catch(reject?) {
return this.then(undefined, reject);
}
finally(onFinally) {
return promise.finally(onFinally);
}
};
}
}
interface ICacheRow {
promise: CancelablePromise<IInternalSearchComplete<IFileIndexProviderStats>>;
resolved: boolean;
}
class Cache {
public resultsToSearchCache: { [searchValue: string]: ICacheRow; } = Object.create(null);
public scorerCache: ScorerCache = Object.create(null);
}
const FileMatchItemAccessor = new class implements IItemAccessor<IInternalFileMatch> {
public getItemLabel(match: IInternalFileMatch): string {
return match.basename; // e.g. myFile.txt
}
public getItemDescription(match: IInternalFileMatch): string {
return match.relativePath!.substr(0, match.relativePath!.length - match.basename.length - 1); // e.g. some/path/to/file
}
public getItemPath(match: IInternalFileMatch): string {
return match.relativePath!; // e.g. some/path/to/file/myFile.txt
}
};

View File

@@ -8,13 +8,11 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as extfs from 'vs/base/node/extfs';
import { ILogService } from 'vs/platform/log/common/log';
import { IFileQuery, IFolderQuery, IRawFileQuery, IRawQuery, IRawTextQuery, ISearchCompleteStats, ITextQuery } from 'vs/platform/search/common/search';
import { FileIndexSearchManager } from 'vs/workbench/api/node/extHostSearch.fileIndex';
import { IFileQuery, IFolderQuery, IRawFileQuery, IRawQuery, IRawTextQuery, ISearchCompleteStats, ITextQuery, isSerializedFileMatch } from 'vs/workbench/services/search/common/search';
import { FileSearchManager } from 'vs/workbench/services/search/node/fileSearchManager';
import { SearchService } from 'vs/workbench/services/search/node/rawSearchService';
import { RipgrepSearchProvider } from 'vs/workbench/services/search/node/ripgrepSearchProvider';
import { OutputChannel } from 'vs/workbench/services/search/node/ripgrepSearchUtils';
import { isSerializedFileMatch } from 'vs/workbench/services/search/node/search';
import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager';
import * as vscode from 'vscode';
import { ExtHostSearchShape, IMainContext, MainContext, MainThreadSearchShape } from './extHost.protocol';
@@ -30,20 +28,16 @@ export class ExtHostSearch implements ExtHostSearchShape {
private readonly _textSearchUsedSchemes = new Set<string>();
private readonly _fileSearchProvider = new Map<number, vscode.FileSearchProvider>();
private readonly _fileSearchUsedSchemes = new Set<string>();
private readonly _fileIndexProvider = new Map<number, vscode.FileIndexProvider>();
private readonly _fileIndexUsedSchemes = new Set<string>();
private _handlePool: number = 0;
private _internalFileSearchHandle: number;
private _internalFileSearchProvider: SearchService;
private _internalFileSearchProvider: SearchService | null;
private _fileSearchManager: FileSearchManager;
private _fileIndexSearchManager: FileIndexSearchManager;
constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer, private _logService: ILogService, private _extfs = extfs) {
constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer | null, private _logService: ILogService, private _extfs = extfs) {
this._proxy = mainContext.getProxy(MainContext.MainThreadSearch);
this._fileSearchManager = new FileSearchManager();
this._fileIndexSearchManager = new FileIndexSearchManager();
}
private _transformScheme(scheme: string): string {
@@ -96,22 +90,6 @@ export class ExtHostSearch implements ExtHostSearchShape {
});
}
registerFileIndexProvider(scheme: string, provider: vscode.FileIndexProvider): IDisposable {
if (this._fileIndexUsedSchemes.has(scheme)) {
throw new Error(`a provider for the scheme '${scheme}' is already registered`);
}
this._fileIndexUsedSchemes.add(scheme);
const handle = this._handlePool++;
this._fileIndexProvider.set(handle, provider);
this._proxy.$registerFileIndexProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._fileIndexUsedSchemes.delete(scheme);
this._fileSearchProvider.delete(handle);
this._proxy.$unregisterProvider(handle); // TODO@roblou - unregisterFileIndexProvider
});
}
$provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: CancellationToken): Promise<ISearchCompleteStats> {
const query = reviveQuery(rawQuery);
if (handle === this._internalFileSearchHandle) {
@@ -123,10 +101,7 @@ export class ExtHostSearch implements ExtHostSearchShape {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
}, token);
} else {
const indexProvider = this._fileIndexProvider.get(handle);
return this._fileIndexSearchManager.fileSearch(query, indexProvider, batch => {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
}, token);
throw new Error('unknown provider: ' + handle);
}
}
}
@@ -147,7 +122,11 @@ export class ExtHostSearch implements ExtHostSearchShape {
}
};
return this._internalFileSearchProvider.doFileSearch(rawQuery, onResult, token);
if (!this._internalFileSearchProvider) {
throw new Error('No internal file search handler');
}
return <Promise<ISearchCompleteStats>>this._internalFileSearchProvider.doFileSearch(rawQuery, onResult, token);
}
$clearCache(cacheKey: string): Promise<void> {
@@ -156,15 +135,14 @@ export class ExtHostSearch implements ExtHostSearchShape {
}
this._fileSearchManager.clearCache(cacheKey);
this._fileIndexSearchManager.clearCache(cacheKey);
return Promise.resolve(undefined);
}
$provideTextSearchResults(handle: number, session: number, rawQuery: IRawTextQuery, token: CancellationToken): Promise<ISearchCompleteStats> {
const provider = this._textSearchProvider.get(handle);
if (!provider.provideTextSearchResults) {
return Promise.resolve(undefined);
if (!provider || !provider.provideTextSearchResults) {
throw new Error(`Unknown provider ${handle}`);
}
const query = reviveQuery(rawQuery);

View File

@@ -14,7 +14,7 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
private _id: number;
private _alignment: number;
private _priority: number;
private _priority?: number;
private _disposed: boolean;
private _visible: boolean;
@@ -26,9 +26,9 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
private _timeoutHandle: any;
private _proxy: MainThreadStatusBarShape;
private _extensionId: ExtensionIdentifier;
private _extensionId?: ExtensionIdentifier;
constructor(proxy: MainThreadStatusBarShape, extensionId: ExtensionIdentifier, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
constructor(proxy: MainThreadStatusBarShape, extensionId: ExtensionIdentifier | undefined, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
this._id = ExtHostStatusBarEntry.ID_GEN++;
this._proxy = proxy;
this._alignment = alignment;
@@ -44,7 +44,7 @@ export class ExtHostStatusBarEntry implements StatusBarItem {
return this._alignment;
}
public get priority(): number {
public get priority(): number | undefined {
return this._priority;
}
@@ -139,7 +139,7 @@ class StatusBarMessage {
this._update();
return new Disposable(() => {
let idx = this._messages.indexOf(data);
const idx = this._messages.indexOf(data);
if (idx >= 0) {
this._messages.splice(idx, 1);
this._update();
@@ -167,13 +167,13 @@ export class ExtHostStatusBar {
this._statusMessage = new StatusBarMessage(this);
}
createStatusBarEntry(extensionId: ExtensionIdentifier, alignment?: ExtHostStatusBarAlignment, priority?: number): StatusBarItem {
createStatusBarEntry(extensionId: ExtensionIdentifier | undefined, alignment?: ExtHostStatusBarAlignment, priority?: number): StatusBarItem {
return new ExtHostStatusBarEntry(this._proxy, extensionId, alignment, priority);
}
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): Disposable {
let d = this._statusMessage.setMessage(text);
const d = this._statusMessage.setMessage(text);
let handle: any;
if (typeof timeoutOrThenable === 'number') {

View File

@@ -23,7 +23,7 @@ export class ExtHostStorage implements ExtHostStorageShape {
this._proxy = mainContext.getProxy(MainContext.MainThreadStorage);
}
getValue<T>(shared: boolean, key: string, defaultValue?: T): Promise<T> {
getValue<T>(shared: boolean, key: string, defaultValue?: T): Promise<T | undefined> {
return this._proxy.$getValue<T>(shared, key).then(value => value || defaultValue);
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import * as path from 'vs/base/common/path';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as Objects from 'vs/base/common/objects';
@@ -11,16 +11,18 @@ import { asPromise } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import { win32 } from 'vs/base/node/processes';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { MainContext, MainThreadTaskShape, ExtHostTaskShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
import * as types from 'vs/workbench/api/node/extHostTypes';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostWorkspace, IExtHostWorkspaceProvider } from 'vs/workbench/api/node/extHostWorkspace';
import * as vscode from 'vscode';
import {
TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO, ProcessExecutionOptionsDTO, ProcessExecutionDTO,
ShellExecutionOptionsDTO, ShellExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO
TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO,
ProcessExecutionOptionsDTO, ProcessExecutionDTO,
ShellExecutionOptionsDTO, ShellExecutionDTO,
CustomExecutionDTO,
TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO
} from '../shared/tasks';
// {{SQL CARBON EDIT}}
@@ -28,17 +30,20 @@ import {
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/node/extHostTerminalService';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { CancellationToken } from 'vs/base/common/cancellation';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
namespace TaskDefinitionDTO {
export function from(value: vscode.TaskDefinition): TaskDefinitionDTO {
export function from(value: vscode.TaskDefinition): TaskDefinitionDTO | undefined {
if (value === undefined || value === null) {
return undefined;
}
return value;
}
export function to(value: TaskDefinitionDTO): vscode.TaskDefinition {
export function to(value: TaskDefinitionDTO): vscode.TaskDefinition | undefined {
if (value === undefined || value === null) {
return undefined;
}
@@ -47,13 +52,13 @@ namespace TaskDefinitionDTO {
}
namespace TaskPresentationOptionsDTO {
export function from(value: vscode.TaskPresentationOptions): TaskPresentationOptionsDTO {
export function from(value: vscode.TaskPresentationOptions): TaskPresentationOptionsDTO | undefined {
if (value === undefined || value === null) {
return undefined;
}
return value;
}
export function to(value: TaskPresentationOptionsDTO): vscode.TaskPresentationOptions {
export function to(value: TaskPresentationOptionsDTO): vscode.TaskPresentationOptions | undefined {
if (value === undefined || value === null) {
return undefined;
}
@@ -62,13 +67,13 @@ namespace TaskPresentationOptionsDTO {
}
namespace ProcessExecutionOptionsDTO {
export function from(value: vscode.ProcessExecutionOptions): ProcessExecutionOptionsDTO {
export function from(value: vscode.ProcessExecutionOptions): ProcessExecutionOptionsDTO | undefined {
if (value === undefined || value === null) {
return undefined;
}
return value;
}
export function to(value: ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions {
export function to(value: ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions | undefined {
if (value === undefined || value === null) {
return undefined;
}
@@ -77,15 +82,19 @@ namespace ProcessExecutionOptionsDTO {
}
namespace ProcessExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ProcessExecutionDTO {
let candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is ProcessExecutionDTO {
if (value) {
const candidate = value as ProcessExecutionDTO;
return candidate && !!candidate.process;
} else {
return false;
}
}
export function from(value: vscode.ProcessExecution): ProcessExecutionDTO {
export function from(value: vscode.ProcessExecution): ProcessExecutionDTO | undefined {
if (value === undefined || value === null) {
return undefined;
}
let result: ProcessExecutionDTO = {
const result: ProcessExecutionDTO = {
process: value.process,
args: value.args
};
@@ -94,7 +103,7 @@ namespace ProcessExecutionDTO {
}
return result;
}
export function to(value: ProcessExecutionDTO): types.ProcessExecution {
export function to(value: ProcessExecutionDTO): types.ProcessExecution | undefined {
if (value === undefined || value === null) {
return undefined;
}
@@ -103,13 +112,13 @@ namespace ProcessExecutionDTO {
}
namespace ShellExecutionOptionsDTO {
export function from(value: vscode.ShellExecutionOptions): ShellExecutionOptionsDTO {
export function from(value: vscode.ShellExecutionOptions): ShellExecutionOptionsDTO | undefined {
if (value === undefined || value === null) {
return undefined;
}
return value;
}
export function to(value: ShellExecutionOptionsDTO): vscode.ShellExecutionOptions {
export function to(value: ShellExecutionOptionsDTO): vscode.ShellExecutionOptions | undefined {
if (value === undefined || value === null) {
return undefined;
}
@@ -118,15 +127,19 @@ namespace ShellExecutionOptionsDTO {
}
namespace ShellExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO): value is ShellExecutionDTO {
let candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is ShellExecutionDTO {
if (value) {
const candidate = value as ShellExecutionDTO;
return candidate && (!!candidate.commandLine || !!candidate.command);
} else {
return false;
}
}
export function from(value: vscode.ShellExecution): ShellExecutionDTO {
export function from(value: vscode.ShellExecution): ShellExecutionDTO | undefined {
if (value === undefined || value === null) {
return undefined;
}
let result: ShellExecutionDTO = {
const result: ShellExecutionDTO = {
};
if (value.commandLine !== undefined) {
result.commandLine = value.commandLine;
@@ -139,27 +152,44 @@ namespace ShellExecutionDTO {
}
return result;
}
export function to(value: ShellExecutionDTO): types.ShellExecution {
if (value === undefined || value === null) {
export function to(value: ShellExecutionDTO): types.ShellExecution | undefined {
if (value === undefined || value === null || (value.command === undefined && value.commandLine === undefined)) {
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);
return new types.ShellExecution(value.command!, value.args ? value.args : [], value.options);
}
}
}
namespace CustomExecutionDTO {
export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined): value is CustomExecutionDTO {
if (value) {
let candidate = value as CustomExecutionDTO;
return candidate && candidate.customExecution === 'customExecution';
} else {
return false;
}
}
export function from(value: vscode.CustomExecution): CustomExecutionDTO {
return {
customExecution: 'customExecution'
};
}
}
namespace TaskHandleDTO {
export function from(value: types.Task): TaskHandleDTO {
let folder: UriComponents;
let folder: UriComponents | undefined;
if (value.scope !== undefined && typeof value.scope !== 'number') {
folder = value.scope.uri;
}
return {
id: value._id,
workspaceFolder: folder
id: value._id!,
workspaceFolder: folder!
};
}
}
@@ -170,9 +200,9 @@ namespace TaskDTO {
if (tasks === undefined || tasks === null) {
return [];
}
let result: TaskDTO[] = [];
const result: TaskDTO[] = [];
for (let task of tasks) {
let converted = from(task, extension);
const converted = from(task, extension);
if (converted) {
result.push(converted);
}
@@ -180,17 +210,19 @@ namespace TaskDTO {
return result;
}
export function from(value: vscode.Task, extension: IExtensionDescription): TaskDTO {
export function from(value: vscode.Task, extension: IExtensionDescription): TaskDTO | undefined {
if (value === undefined || value === null) {
return undefined;
}
let execution: ShellExecutionDTO | ProcessExecutionDTO;
let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecutionDTO | undefined;
if (value.execution instanceof types.ProcessExecution) {
execution = ProcessExecutionDTO.from(value.execution);
} else if (value.execution instanceof types.ShellExecution) {
execution = ShellExecutionDTO.from(value.execution);
} else if ((<vscode.Task2>value).execution2 && (<vscode.Task2>value).execution2 instanceof types.CustomExecution) {
execution = CustomExecutionDTO.from(<types.CustomExecution>(<vscode.Task2>value).execution2);
}
let definition: TaskDefinitionDTO = TaskDefinitionDTO.from(value.definition);
const definition: TaskDefinitionDTO | undefined = TaskDefinitionDTO.from(value.definition);
let scope: number | UriComponents;
if (value.scope) {
if (typeof value.scope === 'number') {
@@ -205,9 +237,9 @@ namespace TaskDTO {
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,
const group = (value.group as types.TaskGroup) ? (value.group as types.TaskGroup).id : undefined;
const result: TaskDTO = {
_id: (value as types.Task)._id!,
definition,
name: value.name,
source: {
@@ -215,7 +247,7 @@ namespace TaskDTO {
label: value.source,
scope: scope
},
execution,
execution: execution!,
isBackground: value.isBackground,
group: group,
presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions),
@@ -225,24 +257,24 @@ namespace TaskDTO {
};
return result;
}
export function to(value: TaskDTO, workspace: ExtHostWorkspace): types.Task {
export async function to(value: TaskDTO | undefined, workspace: IExtHostWorkspaceProvider): Promise<types.Task | undefined> {
if (value === undefined || value === null) {
return undefined;
}
let execution: types.ShellExecution | types.ProcessExecution;
let execution: types.ShellExecution | types.ProcessExecution | undefined;
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;
const definition: vscode.TaskDefinition | undefined = TaskDefinitionDTO.to(value.definition);
let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
if (value.source) {
if (value.source.scope !== undefined) {
if (typeof value.source.scope === 'number') {
scope = value.source.scope;
} else {
scope = workspace.resolveWorkspaceFolder(URI.revive(value.source.scope));
scope = await workspace.resolveWorkspaceFolder(URI.revive(value.source.scope));
}
} else {
scope = types.TaskScope.Workspace;
@@ -251,7 +283,7 @@ namespace TaskDTO {
if (!definition || !scope) {
return undefined;
}
let result = new types.Task(definition, scope, value.name, value.source.label, execution, value.problemMatchers);
const result = new types.Task(definition, scope, value.name!, value.source.label, execution, value.problemMatchers);
if (value.isBackground !== undefined) {
result.isBackground = value.isBackground;
}
@@ -259,7 +291,7 @@ namespace TaskDTO {
result.group = types.TaskGroup.from(value.group);
}
if (value.presentationOptions) {
result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions);
result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions)!;
}
if (value._id) {
result._id = value._id;
@@ -269,11 +301,11 @@ namespace TaskDTO {
}
namespace TaskFilterDTO {
export function from(value: vscode.TaskFilter): TaskFilterDTO {
export function from(value: vscode.TaskFilter | undefined): TaskFilterDTO | undefined {
return value;
}
export function to(value: TaskFilterDTO): vscode.TaskFilter {
export function to(value: TaskFilterDTO): vscode.TaskFilter | undefined {
if (!value) {
return undefined;
}
@@ -302,8 +334,12 @@ class TaskExecutionImpl implements vscode.TaskExecution {
}
namespace TaskExecutionDTO {
export function to(value: TaskExecutionDTO, tasks: ExtHostTask): vscode.TaskExecution {
return new TaskExecutionImpl(tasks, value.id, TaskDTO.to(value.task, tasks.extHostWorkspace));
export async function to(value: TaskExecutionDTO, tasks: ExtHostTask, workspaceProvider: IExtHostWorkspaceProvider): Promise<vscode.TaskExecution> {
const task = await TaskDTO.to(value.task, workspaceProvider);
if (!task) {
throw new Error('Unexpected: Task cannot be created.');
}
return new TaskExecutionImpl(tasks, value.id, task);
}
export function from(value: vscode.TaskExecution): TaskExecutionDTO {
return {
@@ -318,15 +354,121 @@ interface HandlerData {
extension: IExtensionDescription;
}
class CustomExecutionData implements IDisposable {
private static waitForDimensionsTimeoutInMs: number = 5000;
private _cancellationSource?: CancellationTokenSource;
private readonly _onTaskExecutionComplete: Emitter<CustomExecutionData> = new Emitter<CustomExecutionData>();
private readonly _disposables: IDisposable[] = [];
private terminal?: vscode.Terminal;
private terminalId?: number;
public result: number | undefined;
constructor(
private readonly customExecution: vscode.CustomExecution,
private readonly terminalService: ExtHostTerminalService) {
}
public dispose(): void {
dispose(this._disposables);
}
public get onTaskExecutionComplete(): Event<CustomExecutionData> {
return this._onTaskExecutionComplete.event;
}
private onDidCloseTerminal(terminal: vscode.Terminal): void {
if ((this.terminal === terminal) && this._cancellationSource) {
this._cancellationSource.cancel();
}
}
private onDidOpenTerminal(terminal: vscode.Terminal): void {
if (!(terminal instanceof ExtHostTerminal)) {
throw new Error('How could this not be a extension host terminal?');
}
if (this.terminalId && terminal._id === this.terminalId) {
this.startCallback(this.terminalId);
}
}
public async startCallback(terminalId: number): Promise<void> {
this.terminalId = terminalId;
// If we have already started the extension task callback, then
// do not start it again.
// It is completely valid for multiple terminals to be opened
// before the one for our task.
if (this._cancellationSource) {
return undefined;
}
const callbackTerminals: vscode.Terminal[] = this.terminalService.terminals.filter((terminal) => terminal._id === terminalId);
if (!callbackTerminals || callbackTerminals.length === 0) {
this._disposables.push(this.terminalService.onDidOpenTerminal(this.onDidOpenTerminal.bind(this)));
return;
}
if (callbackTerminals.length !== 1) {
throw new Error(`Expected to only have one terminal at this point`);
}
this.terminal = callbackTerminals[0];
const terminalRenderer: vscode.TerminalRenderer = await this.terminalService.resolveTerminalRenderer(terminalId);
// If we don't have the maximum dimensions yet, then we need to wait for them (but not indefinitely).
// Custom executions will expect the dimensions to be set properly before they are launched.
// BUT, due to the API contract VSCode has for terminals and dimensions, they are still responsible for
// handling cases where they are not set.
if (!terminalRenderer.maximumDimensions) {
const dimensionTimeout: Promise<void> = new Promise((resolve) => {
setTimeout(() => {
resolve();
}, CustomExecutionData.waitForDimensionsTimeoutInMs);
});
let dimensionsRegistration: IDisposable | undefined;
const dimensionsPromise: Promise<void> = new Promise((resolve) => {
dimensionsRegistration = terminalRenderer.onDidChangeMaximumDimensions((newDimensions) => {
resolve();
});
});
await Promise.race([dimensionTimeout, dimensionsPromise]);
if (dimensionsRegistration) {
dimensionsRegistration.dispose();
}
}
this._cancellationSource = new CancellationTokenSource();
this._disposables.push(this._cancellationSource);
this._disposables.push(this.terminalService.onDidCloseTerminal(this.onDidCloseTerminal.bind(this)));
// Regardless of how the task completes, we are done with this custom execution task.
this.customExecution.callback(terminalRenderer, this._cancellationSource.token).then(
(success) => {
this.result = success;
this._onTaskExecutionComplete.fire(this);
}, (rejected) => {
this._onTaskExecutionComplete.fire(this);
});
}
}
export class ExtHostTask implements ExtHostTaskShape {
private _proxy: MainThreadTaskShape;
private _workspaceService: ExtHostWorkspace;
private _workspaceProvider: IExtHostWorkspaceProvider;
private _editorService: ExtHostDocumentsAndEditors;
private _configurationService: ExtHostConfiguration;
private _terminalService: ExtHostTerminalService;
private _handleCounter: number;
private _handlers: Map<number, HandlerData>;
private _taskExecutions: Map<string, TaskExecutionImpl>;
private _providedCustomExecutions: Map<string, CustomExecutionData>;
private _activeCustomExecutions: Map<string, CustomExecutionData>;
private readonly _onDidExecuteTask: Emitter<vscode.TaskStartEvent> = new Emitter<vscode.TaskStartEvent>();
private readonly _onDidTerminateTask: Emitter<vscode.TaskEndEvent> = new Emitter<vscode.TaskEndEvent>();
@@ -334,25 +476,29 @@ export class ExtHostTask implements ExtHostTaskShape {
private readonly _onDidTaskProcessStarted: Emitter<vscode.TaskProcessStartEvent> = new Emitter<vscode.TaskProcessStartEvent>();
private readonly _onDidTaskProcessEnded: Emitter<vscode.TaskProcessEndEvent> = new Emitter<vscode.TaskProcessEndEvent>();
constructor(mainContext: IMainContext, workspaceService: ExtHostWorkspace, editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfiguration) {
constructor(
mainContext: IMainContext,
workspaceService: ExtHostWorkspace,
editorService: ExtHostDocumentsAndEditors,
configurationService: ExtHostConfiguration,
extHostTerminalService: ExtHostTerminalService) {
this._proxy = mainContext.getProxy(MainContext.MainThreadTask);
this._workspaceService = workspaceService;
this._workspaceProvider = workspaceService;
this._editorService = editorService;
this._configurationService = configurationService;
this._terminalService = extHostTerminalService;
this._handleCounter = 0;
this._handlers = new Map<number, HandlerData>();
this._taskExecutions = new Map<string, TaskExecutionImpl>();
}
public get extHostWorkspace(): ExtHostWorkspace {
return this._workspaceService;
this._providedCustomExecutions = new Map<string, CustomExecutionData>();
this._activeCustomExecutions = new Map<string, CustomExecutionData>();
}
public registerTaskProvider(extension: IExtensionDescription, provider: vscode.TaskProvider): vscode.Disposable {
if (!provider) {
return new types.Disposable(() => { });
}
let handle = this.nextHandle();
const handle = this.nextHandle();
this._handlers.set(handle, { provider, extension });
this._proxy.$registerTaskProvider(handle);
return new types.Disposable(() => {
@@ -366,10 +512,10 @@ export class ExtHostTask implements ExtHostTaskShape {
}
public fetchTasks(filter?: vscode.TaskFilter): Promise<vscode.Task[]> {
return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then((values) => {
let result: vscode.Task[] = [];
return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then(async (values) => {
const result: vscode.Task[] = [];
for (let value of values) {
let task = TaskDTO.to(value, this._workspaceService);
const task = await TaskDTO.to(value, this._workspaceProvider);
if (task) {
result.push(task);
}
@@ -378,13 +524,13 @@ export class ExtHostTask implements ExtHostTaskShape {
});
}
public executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
let tTask = (task as types.Task);
public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
const tTask = (task as types.Task);
// We have a preserved ID. So the task didn't change.
if (tTask._id !== undefined) {
return this._proxy.$executeTask(TaskHandleDTO.from(tTask)).then(value => this.getTaskExecution(value, task));
} else {
let dto = TaskDTO.from(task, extension);
const dto = TaskDTO.from(task, extension);
if (dto === undefined) {
return Promise.reject(new Error('Task is not valid'));
}
@@ -393,7 +539,7 @@ export class ExtHostTask implements ExtHostTaskShape {
}
public get taskExecutions(): vscode.TaskExecution[] {
let result: vscode.TaskExecution[] = [];
const result: vscode.TaskExecution[] = [];
this._taskExecutions.forEach(value => result.push(value));
return result;
}
@@ -409,9 +555,28 @@ export class ExtHostTask implements ExtHostTaskShape {
return this._onDidExecuteTask.event;
}
public $onDidStartTask(execution: TaskExecutionDTO): void {
public async $onDidStartTask(execution: TaskExecutionDTO, terminalId: number): Promise<void> {
// Once a terminal is spun up for the custom execution task this event will be fired.
// At that point, we need to actually start the callback, but
// only if it hasn't already begun.
const extensionCallback: CustomExecutionData | undefined = this._providedCustomExecutions.get(execution.id);
if (extensionCallback) {
if (this._activeCustomExecutions.get(execution.id) !== undefined) {
throw new Error('We should not be trying to start the same custom task executions twice.');
}
this._activeCustomExecutions.set(execution.id, extensionCallback);
const taskExecutionComplete: IDisposable = extensionCallback.onTaskExecutionComplete(() => {
this.customExecutionComplete(execution);
taskExecutionComplete.dispose();
});
extensionCallback.startCallback(terminalId);
}
this._onDidExecuteTask.fire({
execution: this.getTaskExecution(execution)
execution: await this.getTaskExecution(execution)
});
}
@@ -419,9 +584,10 @@ export class ExtHostTask implements ExtHostTaskShape {
return this._onDidTerminateTask.event;
}
public $OnDidEndTask(execution: TaskExecutionDTO): void {
const _execution = this.getTaskExecution(execution);
public async $OnDidEndTask(execution: TaskExecutionDTO): Promise<void> {
const _execution = await this.getTaskExecution(execution);
this._taskExecutions.delete(execution.id);
this.customExecutionComplete(execution);
this._onDidTerminateTask.fire({
execution: _execution
});
@@ -431,8 +597,8 @@ export class ExtHostTask implements ExtHostTaskShape {
return this._onDidTaskProcessStarted.event;
}
public $onDidStartTaskProcess(value: TaskProcessStartedDTO): void {
const execution = this.getTaskExecution(value.id);
public async $onDidStartTaskProcess(value: TaskProcessStartedDTO): Promise<void> {
const execution = await this.getTaskExecution(value.id);
if (execution) {
this._onDidTaskProcessStarted.fire({
execution: execution,
@@ -445,8 +611,8 @@ export class ExtHostTask implements ExtHostTaskShape {
return this._onDidTaskProcessEnded.event;
}
public $onDidEndTaskProcess(value: TaskProcessEndedDTO): void {
const execution = this.getTaskExecution(value.id);
public async $onDidEndTaskProcess(value: TaskProcessEndedDTO): Promise<void> {
const execution = await this.getTaskExecution(value.id);
if (execution) {
this._onDidTaskProcessEnded.fire({
execution: execution,
@@ -456,63 +622,106 @@ export class ExtHostTask implements ExtHostTaskShape {
}
public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<TaskSetDTO> {
let handler = this._handlers.get(handle);
const handler = this._handlers.get(handle);
if (!handler) {
return Promise.reject(new Error('no handler found'));
}
return asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => {
let sanitized: vscode.Task[] = [];
for (let task of value) {
if (task.definition && validTypes[task.definition.type] === true) {
sanitized.push(task);
} else {
sanitized.push(task);
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
// For custom execution tasks, we need to store the execution objects locally
// since we obviously cannot send callback functions through the proxy.
// So, clear out any existing ones.
this._providedCustomExecutions.clear();
// Set up a list of task ID promises that we can wait on
// before returning the provided tasks. The ensures that
// our task IDs are calculated for any custom execution tasks.
// Knowing this ID ahead of time is needed because when a task
// start event is fired this is when the custom execution is called.
// The task start event is also the first time we see the ID from the main
// thread, which is too late for us because we need to save an map
// from an ID to the custom execution function. (Kind of a cart before the horse problem).
const taskIdPromises: Promise<void>[] = [];
const fetchPromise = asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => {
const taskDTOs: TaskDTO[] = [];
if (value) {
for (let task of value) {
if (!task.definition || !validTypes[task.definition.type]) {
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
}
const taskDTO: TaskDTO | undefined = TaskDTO.from(task, handler.extension);
if (taskDTO) {
taskDTOs.push(taskDTO);
if (CustomExecutionDTO.is(taskDTO.execution)) {
taskIdPromises.push(new Promise((resolve) => {
// The ID is calculated on the main thread task side, so, let's call into it here.
// We need the task id's pre-computed for custom task executions because when OnDidStartTask
// is invoked, we have to be able to map it back to our data.
this._proxy.$createTaskId(taskDTO).then((taskId) => {
this._providedCustomExecutions.set(taskId, new CustomExecutionData(<vscode.CustomExecution>(<vscode.Task2>task).execution2, this._terminalService));
resolve();
});
}));
}
}
}
}
return {
tasks: TaskDTO.fromMany(sanitized, handler.extension),
tasks: taskDTOs,
extension: handler.extension
};
});
return new Promise((resolve) => {
fetchPromise.then((result) => {
Promise.all(taskIdPromises).then(() => {
resolve(result);
});
});
});
}
// {{SQL CARBON EDIT}} disable debug related method
public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> {
// const configProvider = await this._configurationService.getConfigProvider();
// let uri: URI = URI.revive(uriComponents);
// let result = {
// process: undefined as string,
// variables: Object.create(null)
// };
// let workspaceFolder = this._workspaceService.resolveWorkspaceFolder(uri);
// let resolver = new ExtHostVariableResolverService(this._workspaceService, this._editorService, configProvider);
// let ws: IWorkspaceFolder = {
// uri: workspaceFolder.uri,
// name: workspaceFolder.name,
// index: workspaceFolder.index,
// toResource: () => {
// throw new Error('Not implemented');
// }
// };
// for (let variable of toResolve.variables) {
// result.variables[variable] = resolver.resolve(ws, variable);
// }
// if (toResolve.process !== undefined) {
// let paths: string[] | undefined = undefined;
// if (toResolve.process.path !== undefined) {
// paths = toResolve.process.path.split(path.delimiter);
// for (let i = 0; i < paths.length; i++) {
// paths[i] = resolver.resolve(ws, paths[i]);
// }
// }
// result.process = win32.findExecutable(
// resolver.resolve(ws, toResolve.process.name),
// toResolve.process.cwd !== undefined ? resolver.resolve(ws, toResolve.process.cwd) : undefined,
// paths
// );
// }
// return result;
/*const configProvider = await this._configurationService.getConfigProvider();
const uri: URI = URI.revive(uriComponents);
const result = {
process: <unknown>undefined as string,
variables: Object.create(null)
};
const workspaceFolder = await this._workspaceProvider.resolveWorkspaceFolder(uri);
const workspaceFolders = await this._workspaceProvider.getWorkspaceFolders2();
if (!workspaceFolders || !workspaceFolder) {
throw new Error('Unexpected: Tasks can only be run in a workspace folder');
}
const resolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider);
const ws: IWorkspaceFolder = {
uri: workspaceFolder.uri,
name: workspaceFolder.name,
index: workspaceFolder.index,
toResource: () => {
throw new Error('Not implemented');
}
};
for (let variable of toResolve.variables) {
result.variables[variable] = resolver.resolve(ws, variable);
}
if (toResolve.process !== undefined) {
let paths: string[] | undefined = undefined;
if (toResolve.process.path !== undefined) {
paths = toResolve.process.path.split(path.delimiter);
for (let i = 0; i < paths.length; i++) {
paths[i] = resolver.resolve(ws, paths[i]);
}
}
result.process = win32.findExecutable(
resolver.resolve(ws, toResolve.process.name),
toResolve.process.cwd !== undefined ? resolver.resolve(ws, toResolve.process.cwd) : undefined,
paths
);
}
return result;*/
return undefined;
}
@@ -520,17 +729,34 @@ export class ExtHostTask implements ExtHostTaskShape {
return this._handleCounter++;
}
private getTaskExecution(execution: TaskExecutionDTO | string, task?: vscode.Task): TaskExecutionImpl {
private async getTaskExecution(execution: TaskExecutionDTO | string, task?: vscode.Task): Promise<TaskExecutionImpl> {
if (typeof execution === 'string') {
return this._taskExecutions.get(execution);
const taskExecution = this._taskExecutions.get(execution);
if (!taskExecution) {
throw new Error('Unexpected: The specified task is missing an execution');
}
return taskExecution;
}
let result: TaskExecutionImpl = this._taskExecutions.get(execution.id);
let result: TaskExecutionImpl | undefined = this._taskExecutions.get(execution.id);
if (result) {
return result;
}
result = new TaskExecutionImpl(this, execution.id, task ? task : TaskDTO.to(execution.task, this._workspaceService));
this._taskExecutions.set(execution.id, result);
return result;
const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider);
if (!taskToCreate) {
throw new Error('Unexpected: Task does not exist.');
}
const createdResult: TaskExecutionImpl = new TaskExecutionImpl(this, execution.id, taskToCreate);
this._taskExecutions.set(execution.id, createdResult);
return createdResult;
}
private customExecutionComplete(execution: TaskExecutionDTO): void {
const extensionCallback: CustomExecutionData | undefined = this._activeCustomExecutions.get(execution.id);
if (extensionCallback) {
this._activeCustomExecutions.delete(execution.id);
this._proxy.$customExecutionComplete(execution.id, extensionCallback.result);
extensionCallback.dispose();
}
}
}

View File

@@ -4,21 +4,19 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import pkg from 'vs/platform/product/node/package';
import * as os from 'os';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as platform from 'vs/base/common/platform';
import * as terminalEnvironment from 'vs/workbench/parts/terminal/node/terminalEnvironment';
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ILogService } from 'vs/platform/log/common/log';
import { EXT_HOST_CREATION_DELAY } from 'vs/workbench/parts/terminal/common/terminal';
import { TerminalProcess } from 'vs/workbench/parts/terminal/node/terminalProcess';
import { EXT_HOST_CREATION_DELAY, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal';
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
import { timeout } from 'vs/base/common/async';
import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net';
import * as http from 'http';
import * as fs from 'fs';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { sanitizeProcessEnvironment } from 'vs/base/node/processes';
import { sanitizeProcessEnvironment } from 'vs/base/common/processes';
const RENDERER_NO_PROCESS_ID = -1;
@@ -76,8 +74,10 @@ export class BaseExtHostTerminal {
}
export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Terminal {
private _pidPromise: Promise<number>;
private _pidPromiseComplete: (value: number) => any;
private _pidPromise: Promise<number | undefined>;
private _cols: number | undefined;
private _pidPromiseComplete: ((value: number | undefined) => any) | null;
private _rows: number | undefined;
private readonly _onData = new Emitter<string>();
public get onDidWriteData(): Event<string> {
@@ -90,7 +90,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
constructor(
proxy: MainThreadTerminalServiceShape,
private _name: string,
private _name?: string,
id?: number,
pid?: number
) {
@@ -108,7 +108,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
shellPath?: string,
shellArgs?: string[],
cwd?: string | URI,
env?: { [key: string]: string },
env?: { [key: string]: string | null },
waitOnExit?: boolean,
strictEnv?: boolean
): void {
@@ -119,14 +119,34 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
}
public get name(): string {
return this._name;
return this._name || '';
}
public set name(name: string) {
this._name = name;
}
public get processId(): Promise<number> {
public get dimensions(): vscode.TerminalDimensions | undefined {
if (this._cols === undefined || this._rows === undefined) {
return undefined;
}
return {
columns: this._cols,
rows: this._rows
};
}
public setDimensions(cols: number, rows: number): boolean {
if (cols === this._cols && rows === this._rows) {
// Nothing changed
return false;
}
this._cols = cols;
this._rows = rows;
return true;
}
public get processId(): Promise<number | undefined> {
return this._pidPromise;
}
@@ -145,7 +165,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
this._queueApiRequest(this._proxy.$hide, []);
}
public _setProcessId(processId: number): void {
public _setProcessId(processId: number | undefined): void {
// The event may fire 2 times when the panel is restored
if (this._pidPromiseComplete) {
this._pidPromiseComplete(processId);
@@ -183,15 +203,15 @@ export class ExtHostTerminalRenderer extends BaseExtHostTerminal implements vsco
}
private _dimensions: vscode.TerminalDimensions | undefined;
public get dimensions(): vscode.TerminalDimensions { return this._dimensions; }
public set dimensions(dimensions: vscode.TerminalDimensions) {
public get dimensions(): vscode.TerminalDimensions | undefined { return this._dimensions; }
public set dimensions(dimensions: vscode.TerminalDimensions | undefined) {
this._checkDisposed();
this._dimensions = dimensions;
this._queueApiRequest(this._proxy.$terminalRendererSetDimensions, [dimensions]);
}
private _maximumDimensions: vscode.TerminalDimensions;
public get maximumDimensions(): vscode.TerminalDimensions {
private _maximumDimensions: vscode.TerminalDimensions | undefined;
public get maximumDimensions(): vscode.TerminalDimensions | undefined {
if (!this._maximumDimensions) {
return undefined;
}
@@ -213,13 +233,17 @@ export class ExtHostTerminalRenderer extends BaseExtHostTerminal implements vsco
constructor(
proxy: MainThreadTerminalServiceShape,
private _name: string,
private _terminal: ExtHostTerminal
private _terminal: ExtHostTerminal,
id?: number
) {
super(proxy);
this._proxy.$createTerminalRenderer(this._name).then(id => {
this._runQueuedRequests(id);
(<any>this._terminal)._runQueuedRequests(id);
});
super(proxy, id);
if (!id) {
this._proxy.$createTerminalRenderer(this._name).then(id => {
this._runQueuedRequests(id);
(<any>this._terminal)._runQueuedRequests(id);
});
}
}
public write(data: string): void {
@@ -235,21 +259,21 @@ export class ExtHostTerminalRenderer extends BaseExtHostTerminal implements vsco
if (this._maximumDimensions && this._maximumDimensions.columns === columns && this._maximumDimensions.rows === rows) {
return;
}
this._maximumDimensions = { columns, rows };
this._onDidChangeMaximumDimensions.fire(this.maximumDimensions);
const newValue = { columns, rows };
this._maximumDimensions = newValue;
this._onDidChangeMaximumDimensions.fire(newValue);
}
}
export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _proxy: MainThreadTerminalServiceShape;
private _activeTerminal: ExtHostTerminal;
private _activeTerminal: ExtHostTerminal | undefined;
private _terminals: ExtHostTerminal[] = [];
private _terminalProcesses: { [id: number]: TerminalProcess } = {};
private _terminalRenderers: ExtHostTerminalRenderer[] = [];
private _getTerminalPromises: { [id: number]: Promise<ExtHostTerminal> } = {};
private _cliServer: CLIServer | undefined;
public get activeTerminal(): ExtHostTerminal { return this._activeTerminal; }
public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; }
public get terminals(): ExtHostTerminal[] { return this._terminals; }
private readonly _onDidCloseTerminal: Emitter<vscode.Terminal> = new Emitter<vscode.Terminal>();
@@ -258,12 +282,13 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
public get onDidOpenTerminal(): Event<vscode.Terminal> { return this._onDidOpenTerminal && this._onDidOpenTerminal.event; }
private readonly _onDidChangeActiveTerminal: Emitter<vscode.Terminal | undefined> = new Emitter<vscode.Terminal | undefined>();
public get onDidChangeActiveTerminal(): Event<vscode.Terminal | undefined> { return this._onDidChangeActiveTerminal && this._onDidChangeActiveTerminal.event; }
private readonly _onDidChangeTerminalDimensions: Emitter<vscode.TerminalDimensionsChangeEvent> = new Emitter<vscode.TerminalDimensionsChangeEvent>();
public get onDidChangeTerminalDimensions(): Event<vscode.TerminalDimensionsChangeEvent> { return this._onDidChangeTerminalDimensions && this._onDidChangeTerminalDimensions.event; }
constructor(
mainContext: IMainContext,
private _extHostConfiguration: ExtHostConfiguration,
private _logService: ILogService,
private _commands: ExtHostCommands
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService);
}
@@ -293,6 +318,24 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
return renderer;
}
public async resolveTerminalRenderer(id: number): Promise<vscode.TerminalRenderer> {
// Check to see if the extension host already knows about this terminal.
for (const terminalRenderer of this._terminalRenderers) {
if (terminalRenderer._id === id) {
return terminalRenderer;
}
}
const terminal = this._getTerminalById(id);
if (!terminal) {
throw new Error(`Cannot resolve terminal renderer for terminal id ${id}`);
}
const renderer = new ExtHostTerminalRenderer(this._proxy, terminal.name, terminal, terminal._id);
this._terminalRenderers.push(renderer);
return renderer;
}
public $acceptActiveTerminalChanged(id: number | null): void {
const original = this._activeTerminal;
if (id === null) {
@@ -300,6 +343,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
if (original !== this._activeTerminal) {
this._onDidChangeActiveTerminal.fire(this._activeTerminal);
}
return;
}
this._performTerminalIdAction(id, terminal => {
if (terminal) {
@@ -319,7 +363,17 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
});
}
public $acceptTerminalRendererDimensions(id: number, cols: number, rows: number): void {
public async $acceptTerminalDimensions(id: number, cols: number, rows: number): Promise<void> {
const terminal = this._getTerminalById(id);
if (terminal) {
if (terminal.setDimensions(cols, rows)) {
this._onDidChangeTerminalDimensions.fire({
terminal: terminal,
dimensions: terminal.dimensions as vscode.TerminalDimensions
});
}
}
// When a terminal's dimensions change, a renderer's _maximum_ dimensions change
const renderer = this._getTerminalRendererById(id);
if (renderer) {
renderer._setMaximumDimensions(cols, rows);
@@ -342,11 +396,10 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
public $acceptTerminalClosed(id: number): void {
const index = this._getTerminalObjectIndexById(this.terminals, id);
if (index === null) {
return;
if (index !== null) {
const terminal = this._terminals.splice(index, 1)[0];
this._onDidCloseTerminal.fire(terminal);
}
const terminal = this._terminals.splice(index, 1)[0];
this._onDidCloseTerminal.fire(terminal);
}
public $acceptTerminalOpened(id: number, name: string): void {
@@ -356,6 +409,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
this._onDidOpenTerminal.fire(this.terminals[index]);
return;
}
const renderer = this._getTerminalRendererById(id);
const terminal = new ExtHostTerminal(this._proxy, name, id, renderer ? RENDERER_NO_PROCESS_ID : undefined);
this._terminals.push(terminal);
@@ -381,7 +435,15 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
}
public async $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number): Promise<void> {
public async $createProcess(id: number, shellLaunchConfigDto: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number): Promise<void> {
const shellLaunchConfig: IShellLaunchConfig = {
name: shellLaunchConfigDto.name,
executable: shellLaunchConfigDto.executable,
args: shellLaunchConfigDto.args,
cwd: typeof shellLaunchConfigDto.cwd === 'string' ? shellLaunchConfigDto.cwd : URI.revive(shellLaunchConfigDto.cwd),
env: shellLaunchConfigDto.env
};
// TODO: This function duplicates a lot of TerminalProcessManager.createProcess, ideally
// they would be merged into a single implementation.
const configProvider = await this._extHostConfiguration.getConfigProvider();
@@ -392,8 +454,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
// 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}`);
const shellConfigValue: string | undefined = terminalConfig.get(`shell.${platformKey}`);
const shellArgsConfigValue: string | undefined = terminalConfig.get(`shellArgs.${platformKey}`);
shellLaunchConfig.executable = shellConfigValue;
shellLaunchConfig.args = shellArgsConfigValue;
@@ -401,7 +463,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
// TODO: @daniel
const activeWorkspaceRootUri = URI.revive(activeWorkspaceRootUriComponents);
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, activeWorkspaceRootUri, terminalConfig.cwd);
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), activeWorkspaceRootUri, terminalConfig.cwd);
// TODO: Pull in and resolve config settings
// // Resolve env vars from config and shell
@@ -413,25 +475,24 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
// Merge process env with the env from config
const env = { ...process.env };
terminalEnvironment.mergeEnvironments(env, envFromConfig);
terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env);
Object.keys(env).filter(k => env[k] === undefined).forEach(k => {
delete env[k];
});
const castedEnv = env as platform.IProcessEnvironment;
terminalEnvironment.mergeEnvironments(castedEnv, envFromConfig);
terminalEnvironment.mergeEnvironments(castedEnv, shellLaunchConfig.env);
// Sanitize the environment, removing any undesirable VS Code and Electron environment
// variables
sanitizeProcessEnvironment(env);
sanitizeProcessEnvironment(castedEnv, 'VSCODE_IPC_HOOK_CLI');
// Continue env initialization, merging in the env from the launch
// config and adding keys that are needed to create the process
terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, terminalConfig.get('setLocaleVariables'));
if (!this._cliServer) {
this._cliServer = new CLIServer(this._commands);
}
env['VSCODE_IPC_HOOK_CLI'] = this._cliServer.ipcHandlePath;
terminalEnvironment.addTerminalEnvironmentKeys(castedEnv, pkg.version, platform.locale, terminalConfig.get('setLocaleVariables') as boolean);
// Fork the process and listen for messages
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env);
const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty'));
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, castedEnv);
const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, castedEnv, terminalConfig.get('windowsEnableConpty') as boolean);
p.onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid));
p.onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title));
p.onProcessData(data => this._proxy.$sendProcessData(id, data));
@@ -477,11 +538,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
// Send exit event to main side
this._proxy.$sendProcessExit(id, exitCode);
if (this._cliServer && !Object.keys(this._terminalProcesses).length) {
this._cliServer.dispose();
this._cliServer = undefined;
}
}
private _getTerminalByIdEventually(id: number, retries: number = 5): Promise<ExtHostTerminal> {
@@ -513,20 +569,20 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
});
}
private _getTerminalById(id: number): ExtHostTerminal {
private _getTerminalById(id: number): ExtHostTerminal | null {
return this._getTerminalObjectById(this._terminals, id);
}
private _getTerminalRendererById(id: number): ExtHostTerminalRenderer {
private _getTerminalRendererById(id: number): ExtHostTerminalRenderer | null {
return this._getTerminalObjectById(this._terminalRenderers, id);
}
private _getTerminalObjectById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): T {
private _getTerminalObjectById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): T | null {
const index = this._getTerminalObjectIndexById(array, id);
return index !== null ? array[index] : null;
}
private _getTerminalObjectIndexById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): number {
private _getTerminalObjectIndexById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): number | null {
let index: number | null = null;
array.some((item, i) => {
const thisId = item._id;
@@ -553,73 +609,3 @@ class ApiRequest {
this._callback.apply(proxy, [id].concat(this._args));
}
}
class CLIServer {
private _server: http.Server;
private _ipcHandlePath: string | undefined;
constructor(private _commands: ExtHostCommands) {
this._server = http.createServer((req, res) => this.onRequest(req, res));
this.setup().catch(err => {
console.error(err);
return '';
});
}
public get ipcHandlePath() {
return this._ipcHandlePath;
}
private async setup(): Promise<string> {
this._ipcHandlePath = generateRandomPipeName();
try {
this._server.listen(this.ipcHandlePath);
this._server.on('error', err => console.error(err));
} catch (err) {
console.error('Could not start open from terminal server.');
}
return this.ipcHandlePath;
}
private toURIs(strs: string[]): URI[] {
const result: URI[] = [];
if (Array.isArray(strs)) {
for (const s of strs) {
try {
result.push(URI.parse(s));
} catch (e) {
// ignore
}
}
}
return result;
}
private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
const chunks: string[] = [];
req.setEncoding('utf8');
req.on('data', (d: string) => chunks.push(d));
req.on('end', () => {
let { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow } = JSON.parse(chunks.join(''));
if (folderURIs && folderURIs.length || fileURIs && fileURIs.length) {
if (folderURIs && folderURIs.length && !forceReuseWindow) {
forceNewWindow = true;
}
this._commands.executeCommand('_files.windowOpen', { folderURIs: this.toURIs(folderURIs), fileURIs: this.toURIs(fileURIs), forceNewWindow, diffMode, addMode, forceReuseWindow });
}
res.writeHead(200);
res.end();
});
}
dispose(): void {
this._server.close();
if (this._ipcHandlePath && process.platform !== 'win32' && fs.existsSync(this._ipcHandlePath)) {
fs.unlinkSync(this._ipcHandlePath);
}
}
}

View File

@@ -35,7 +35,7 @@ export class TextEditorDecorationType implements vscode.TextEditorDecorationType
export interface ITextEditOperation {
range: vscode.Range;
text: string;
text: string | null;
forceMoveMarkers: boolean;
}
@@ -105,8 +105,8 @@ export class TextEditorEdit {
this._pushEdit(range, null, true);
}
private _pushEdit(range: Range, text: string, forceMoveMarkers: boolean): void {
let validRange = this._document.validateRange(range);
private _pushEdit(range: Range, text: string | null, forceMoveMarkers: boolean): void {
const validRange = this._document.validateRange(range);
this._collectedEdits.push({
range: validRange,
text: text,
@@ -142,6 +142,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
private _id: string;
private _tabSize: number;
private _indentSize: number;
private _insertSpaces: boolean;
private _cursorStyle: TextEditorCursorStyle;
private _lineNumbers: TextEditorLineNumbersStyle;
@@ -154,6 +155,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
public _accept(source: IResolvedTextEditorConfiguration): void {
this._tabSize = source.tabSize;
this._indentSize = source.indentSize;
this._insertSpaces = source.insertSpaces;
this._cursorStyle = source.cursorStyle;
this._lineNumbers = source.lineNumbers;
@@ -168,11 +170,11 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
return 'auto';
}
if (typeof value === 'number') {
let r = Math.floor(value);
const r = Math.floor(value);
return (r > 0 ? r : null);
}
if (typeof value === 'string') {
let r = parseInt(value, 10);
const r = parseInt(value, 10);
if (isNaN(r)) {
return null;
}
@@ -182,7 +184,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
}
public set tabSize(value: number | string) {
let tabSize = this._validateTabSize(value);
const tabSize = this._validateTabSize(value);
if (tabSize === null) {
// ignore invalid call
return;
@@ -200,6 +202,47 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
}));
}
public get indentSize(): number | string {
return this._indentSize;
}
private _validateIndentSize(value: number | string): number | 'tabSize' | null {
if (value === 'tabSize') {
return 'tabSize';
}
if (typeof value === 'number') {
const r = Math.floor(value);
return (r > 0 ? r : null);
}
if (typeof value === 'string') {
const r = parseInt(value, 10);
if (isNaN(r)) {
return null;
}
return (r > 0 ? r : null);
}
return null;
}
public set indentSize(value: number | string) {
const indentSize = this._validateIndentSize(value);
if (indentSize === null) {
// ignore invalid call
return;
}
if (typeof indentSize === 'number') {
if (this._indentSize === indentSize) {
// nothing to do
return;
}
// reflect the new indentSize value immediately
this._indentSize = indentSize;
}
warnOnError(this._proxy.$trySetOptions(this._id, {
indentSize: indentSize
}));
}
public get insertSpaces(): boolean | string {
return this._insertSpaces;
}
@@ -212,7 +255,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
}
public set insertSpaces(value: boolean | string) {
let insertSpaces = this._validateInsertSpaces(value);
const insertSpaces = this._validateInsertSpaces(value);
if (typeof insertSpaces === 'boolean') {
if (this._insertSpaces === insertSpaces) {
// nothing to do
@@ -257,11 +300,11 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
}
public assign(newOptions: vscode.TextEditorOptions) {
let bulkConfigurationUpdate: ITextEditorConfigurationUpdate = {};
const bulkConfigurationUpdate: ITextEditorConfigurationUpdate = {};
let hasUpdate = false;
if (typeof newOptions.tabSize !== 'undefined') {
let tabSize = this._validateTabSize(newOptions.tabSize);
const tabSize = this._validateTabSize(newOptions.tabSize);
if (tabSize === 'auto') {
hasUpdate = true;
bulkConfigurationUpdate.tabSize = tabSize;
@@ -273,8 +316,21 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
}
}
// if (typeof newOptions.indentSize !== 'undefined') {
// const indentSize = this._validateIndentSize(newOptions.indentSize);
// if (indentSize === 'tabSize') {
// hasUpdate = true;
// bulkConfigurationUpdate.indentSize = indentSize;
// } else if (typeof indentSize === 'number' && this._indentSize !== indentSize) {
// // reflect the new indentSize value immediately
// this._indentSize = indentSize;
// hasUpdate = true;
// bulkConfigurationUpdate.indentSize = indentSize;
// }
// }
if (typeof newOptions.insertSpaces !== 'undefined') {
let insertSpaces = this._validateInsertSpaces(newOptions.insertSpaces);
const insertSpaces = this._validateInsertSpaces(newOptions.insertSpaces);
if (insertSpaces === 'auto') {
hasUpdate = true;
bulkConfigurationUpdate.insertSpaces = insertSpaces;
@@ -317,7 +373,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
private _selections: Selection[];
private _options: ExtHostTextEditorOptions;
private _visibleRanges: Range[];
private _viewColumn: vscode.ViewColumn;
private _viewColumn: vscode.ViewColumn | undefined;
private _disposed: boolean = false;
private _hasDecorationsForKey: { [key: string]: boolean; };
@@ -326,7 +382,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
constructor(
proxy: MainThreadTextEditorsShape, id: string, document: ExtHostDocumentData,
selections: Selection[], options: IResolvedTextEditorConfiguration,
visibleRanges: Range[], viewColumn: vscode.ViewColumn
visibleRanges: Range[], viewColumn: vscode.ViewColumn | undefined
) {
this._proxy = proxy;
this._id = id;
@@ -395,7 +451,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
// ---- view column
get viewColumn(): vscode.ViewColumn {
get viewColumn(): vscode.ViewColumn | undefined {
return this._viewColumn;
}
@@ -454,7 +510,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
TypeConverters.fromRangeOrRangeWithMessage(ranges)
);
} else {
let _ranges: number[] = new Array<number>(4 * ranges.length);
const _ranges: number[] = new Array<number>(4 * ranges.length);
for (let i = 0, len = ranges.length; i < len; i++) {
const range = ranges[i];
_ranges[4 * i] = range.start.line + 1;
@@ -482,8 +538,8 @@ export class ExtHostTextEditor implements vscode.TextEditor {
);
}
private _trySetSelection(): Promise<vscode.TextEditor> {
let selection = this._selections.map(TypeConverters.Selection.from);
private _trySetSelection(): Promise<vscode.TextEditor | null | undefined> {
const selection = this._selections.map(TypeConverters.Selection.from);
return this._runOnProxy(() => this._proxy.$trySetSelections(this._id, selection));
}
@@ -498,13 +554,13 @@ export class ExtHostTextEditor implements vscode.TextEditor {
if (this._disposed) {
return Promise.reject(new Error('TextEditor#edit not possible on closed editors'));
}
let edit = new TextEditorEdit(this._documentData.document, options);
const edit = new TextEditorEdit(this._documentData.document, options);
callback(edit);
return this._applyEdit(edit);
}
private _applyEdit(editBuilder: TextEditorEdit): Promise<boolean> {
let editData = editBuilder.finalize();
const editData = editBuilder.finalize();
// return when there is nothing to do
if (editData.edits.length === 0 && !editData.setEndOfLine) {
@@ -512,7 +568,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
}
// check that the edits are not overlapping (i.e. illegal)
let editRanges = editData.edits.map(edit => edit.range);
const editRanges = editData.edits.map(edit => edit.range);
// sort ascending (by end and then by start)
editRanges.sort((a, b) => {
@@ -542,7 +598,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
}
// prepare data for serialization
let edits: ISingleEditOperation[] = editData.edits.map((edit) => {
const edits = editData.edits.map((edit): ISingleEditOperation => {
return {
range: TypeConverters.Range.from(edit.range),
text: edit.text,
@@ -564,7 +620,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
let ranges: IRange[];
if (!where || (Array.isArray(where) && where.length === 0)) {
ranges = this._selections.map(TypeConverters.Range.from);
ranges = this._selections.map(range => TypeConverters.Range.from(range));
} else if (where instanceof Position) {
const { lineNumber, column } = TypeConverters.Position.from(where);
@@ -589,7 +645,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
// ---- util
private _runOnProxy(callback: () => Promise<any>): Promise<ExtHostTextEditor> {
private _runOnProxy(callback: () => Promise<any>): Promise<ExtHostTextEditor | undefined | null> {
if (this._disposed) {
console.warn('TextEditor is closed/disposed');
return Promise.resolve(undefined);

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import * as arrays from 'vs/base/common/arrays';
import { ExtHostEditorsShape, IEditorPropertiesChangeData, IMainContext, ITextDocumentShowOptions, ITextEditorPositionData, MainContext, MainThreadTextEditorsShape } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { ExtHostTextEditor, TextEditorDecorationType } from 'vs/workbench/api/node/extHostTextEditor';
@@ -42,7 +43,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(e => this._onDidChangeActiveTextEditor.fire(e));
}
getActiveTextEditor(): ExtHostTextEditor {
getActiveTextEditor(): ExtHostTextEditor | undefined {
return this._extHostDocumentsAndEditors.activeEditor();
}
@@ -52,8 +53,8 @@ export class ExtHostEditors implements ExtHostEditorsShape {
showTextDocument(document: vscode.TextDocument, column: vscode.ViewColumn, preserveFocus: boolean): Promise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, options: { column: vscode.ViewColumn, preserveFocus: boolean, pinned: boolean }): Promise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Promise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Promise<vscode.TextEditor> {
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions | undefined, preserveFocus?: boolean): Promise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions | undefined, preserveFocus?: boolean): Promise<vscode.TextEditor> {
let options: ITextDocumentShowOptions;
if (typeof columnOrOptions === 'number') {
options = {
@@ -74,7 +75,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
}
return this._proxy.$tryShowTextDocument(document.uri, options).then(id => {
let editor = this._extHostDocumentsAndEditors.getEditor(id);
const editor = id && this._extHostDocumentsAndEditors.getEditor(id);
if (editor) {
return editor;
} else {
@@ -96,6 +97,9 @@ export class ExtHostEditors implements ExtHostEditorsShape {
$acceptEditorPropertiesChanged(id: string, data: IEditorPropertiesChangeData): void {
const textEditor = this._extHostDocumentsAndEditors.getEditor(id);
if (!textEditor) {
throw new Error('unknown text editor');
}
// (1) set all properties
if (data.options) {
@@ -106,7 +110,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
textEditor._acceptSelections(selections);
}
if (data.visibleRanges) {
const visibleRanges = data.visibleRanges.map(TypeConverters.Range.to);
const visibleRanges = arrays.coalesce(data.visibleRanges.map(TypeConverters.Range.to));
textEditor._acceptVisibleRanges(visibleRanges);
}
@@ -127,7 +131,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
});
}
if (data.visibleRanges) {
const visibleRanges = data.visibleRanges.map(TypeConverters.Range.to);
const visibleRanges = arrays.coalesce(data.visibleRanges.map(TypeConverters.Range.to));
this._onDidChangeTextEditorVisibleRanges.fire({
textEditor,
visibleRanges
@@ -136,9 +140,12 @@ export class ExtHostEditors implements ExtHostEditorsShape {
}
$acceptEditorPositionData(data: ITextEditorPositionData): void {
for (let id in data) {
let textEditor = this._extHostDocumentsAndEditors.getEditor(id);
let viewColumn = TypeConverters.ViewColumn.to(data[id]);
for (const id in data) {
const textEditor = this._extHostDocumentsAndEditors.getEditor(id);
if (!textEditor) {
throw new Error('Unknown text editor');
}
const viewColumn = TypeConverters.ViewColumn.to(data[id]);
if (textEditor.viewColumn !== viewColumn) {
textEditor._acceptViewColumn(viewColumn);
this._onDidChangeTextEditorViewColumn.fire({ textEditor, viewColumn });

View File

@@ -5,7 +5,7 @@
import { localize } from 'vs/nls';
import * as vscode from 'vscode';
import { basename } from 'vs/base/common/paths';
import { basename } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -17,15 +17,15 @@ import { TreeItemCollapsibleState, ThemeIcon, MarkdownString } from 'vs/workbenc
import { isUndefinedOrNull, isString } from 'vs/base/common/types';
import { equals, coalesce } from 'vs/base/common/arrays';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtensionDescription, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import * as typeConvert from 'vs/workbench/api/node/extHostTypeConverters';
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
// {{SQL CARBON EDIT}}
import * as azdata from 'azdata';
import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
export type TreeItemHandle = string;
function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeItemLabel {
function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeItemLabel | undefined {
if (isString(label)) {
return { label };
}
@@ -34,7 +34,7 @@ function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeIte
&& typeof label === 'object'
&& typeof label.label === 'string') {
checkProposedApiEnabled(extension);
let highlights: [number, number][] = undefined;
let highlights: [number, number][] | undefined = undefined;
if (Array.isArray(label.highlights)) {
highlights = (<[number, number][]>label.highlights).filter((highlight => highlight.length === 2 && typeof highlight[0] === 'number' && typeof highlight[1] === 'number'));
highlights = highlights.length ? highlights : undefined;
@@ -139,11 +139,14 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
}
}
type Root = null | undefined;
type TreeData<T> = { message: boolean, element: T | Root | false };
// {{SQL CARBON EDIT}}
export interface TreeNode {
item: ITreeItem;
parent: TreeNode;
children: TreeNode[];
parent: TreeNode | Root;
children?: TreeNode[];
}
// {{SQL CARBON EDIT}}
@@ -163,7 +166,7 @@ export class ExtHostTreeView<T> extends Disposable {
get visible(): boolean { return this._visible; }
private _selectedHandles: TreeItemHandle[] = [];
get selectedElements(): T[] { return this._selectedHandles.map(handle => this.getExtensionElement(handle)).filter(element => !isUndefinedOrNull(element)); }
get selectedElements(): T[] { return <T[]>this._selectedHandles.map(handle => this.getExtensionElement(handle)).filter(element => !isUndefinedOrNull(element)); }
private _onDidExpandElement: Emitter<vscode.TreeViewExpansionEvent<T>> = this._register(new Emitter<vscode.TreeViewExpansionEvent<T>>());
readonly onDidExpandElement: Event<vscode.TreeViewExpansionEvent<T>> = this._onDidExpandElement.event;
@@ -177,7 +180,9 @@ export class ExtHostTreeView<T> extends Disposable {
private _onDidChangeVisibility: Emitter<vscode.TreeViewVisibilityChangeEvent> = this._register(new Emitter<vscode.TreeViewVisibilityChangeEvent>());
readonly onDidChangeVisibility: Event<vscode.TreeViewVisibilityChangeEvent> = this._onDidChangeVisibility.event;
private refreshPromise: Promise<void> = Promise.resolve(null);
private _onDidChangeData: Emitter<TreeData<T>> = this._register(new Emitter<TreeData<T>>());
private refreshPromise: Promise<void> = Promise.resolve();
constructor(private viewId: string, options: vscode.TreeViewOptions<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) {
super();
@@ -187,23 +192,39 @@ export class ExtHostTreeView<T> extends Disposable {
this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll });
}
if (this.dataProvider.onDidChangeTreeData) {
let refreshingPromise, promiseCallback;
this._register(Event.debounce<T, T[]>(this.dataProvider.onDidChangeTreeData, (last, current) => {
this._register(this.dataProvider.onDidChangeTreeData(element => this._onDidChangeData.fire({ message: false, element })));
}
let refreshingPromise, promiseCallback;
this._register(Event.debounce<TreeData<T>, { message: boolean, elements: (T | Root)[] }>(this._onDidChangeData.event, (result, current) => {
if (!result) {
result = { message: false, elements: [] };
}
if (current.element !== false) {
if (!refreshingPromise) {
// New refresh has started
refreshingPromise = new Promise(c => promiseCallback = c);
this.refreshPromise = this.refreshPromise.then(() => refreshingPromise);
}
return last ? [...last, current] : [current];
}, 200)(elements => {
result.elements.push(current.element);
}
if (current.message) {
result.message = true;
}
return result;
}, 200)(({ message, elements }) => {
if (elements.length) {
const _promiseCallback = promiseCallback;
refreshingPromise = null;
this.refresh(elements).then(() => _promiseCallback());
}));
}
}
if (message) {
this.proxy.$setMessage(this.viewId, this._message);
}
}));
}
getChildren(parentHandle?: TreeItemHandle): Promise<ITreeItem[]> {
getChildren(parentHandle: TreeItemHandle | Root): Promise<ITreeItem[]> {
const parentElement = parentHandle ? this.getExtensionElement(parentHandle) : undefined;
if (parentHandle && !parentElement) {
console.error(`No tree item with id \'${parentHandle}\' found.`);
@@ -215,7 +236,7 @@ export class ExtHostTreeView<T> extends Disposable {
.then(nodes => nodes.map(n => n.item));
}
getExtensionElement(treeItemHandle: TreeItemHandle): T {
getExtensionElement(treeItemHandle: TreeItemHandle): T | undefined {
return this.elements.get(treeItemHandle);
}
@@ -241,7 +262,7 @@ export class ExtHostTreeView<T> extends Disposable {
set message(message: string | MarkdownString) {
this._message = message;
this.proxy.$setMessage(this.viewId, typeConvert.MarkdownString.fromStrict(this._message));
this._onDidChangeData.fire({ message: true, element: false });
}
setExpanded(treeItemHandle: TreeItemHandle, expanded: boolean): void {
@@ -285,12 +306,12 @@ export class ExtHostTreeView<T> extends Disposable {
});
}
private resolveParent(element: T): Promise<T> {
private resolveParent(element: T): Promise<T | Root> {
const node = this.nodes.get(element);
if (node) {
return Promise.resolve(node.parent ? this.elements.get(node.parent.item.handle) : null);
return Promise.resolve(node.parent ? this.elements.get(node.parent.item.handle) : undefined);
}
return asPromise(() => this.dataProvider.getParent(element));
return asPromise(() => this.dataProvider.getParent!(element));
}
// {{SQL CARBON EDIT}}
@@ -301,7 +322,7 @@ export class ExtHostTreeView<T> extends Disposable {
}
return asPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => this.createHandle(element, extTreeItem, parent, true))
.then(handle => this.getChildren(parent ? parent.item.handle : null)
.then(handle => this.getChildren(parent ? parent.item.handle : undefined)
.then(() => {
const cachedElement = this.getExtensionElement(handle);
if (cachedElement) {
@@ -314,16 +335,16 @@ export class ExtHostTreeView<T> extends Disposable {
}));
}
private getChildrenNodes(parentNodeOrHandle?: TreeNode | TreeItemHandle): TreeNode[] {
private getChildrenNodes(parentNodeOrHandle: TreeNode | TreeItemHandle | Root): TreeNode[] | null {
if (parentNodeOrHandle) {
let parentNode: TreeNode;
let parentNode: TreeNode | undefined;
if (typeof parentNodeOrHandle === 'string') {
const parentElement = this.getExtensionElement(parentNodeOrHandle);
parentNode = parentElement ? this.nodes.get(parentElement) : null;
parentNode = parentElement ? this.nodes.get(parentElement) : undefined;
} else {
parentNode = parentNodeOrHandle;
}
return parentNode ? parentNode.children : null;
return parentNode ? parentNode.children || null : null;
}
return this.roots;
}
@@ -341,13 +362,13 @@ export class ExtHostTreeView<T> extends Disposable {
.then(coalesce);
}
private refresh(elements: T[]): Promise<void> {
private refresh(elements: (T | Root)[]): Promise<void> {
const hasRoot = elements.some(element => !element);
if (hasRoot) {
this.clearAll(); // clear cache
return this.proxy.$refresh(this.viewId);
} else {
const handlesToRefresh = this.getHandlesToRefresh(elements);
const handlesToRefresh = this.getHandlesToRefresh(<T[]>elements);
if (handlesToRefresh.length) {
return this.refreshHandles(handlesToRefresh);
}
@@ -359,15 +380,15 @@ export class ExtHostTreeView<T> extends Disposable {
protected getHandlesToRefresh(elements: T[]): TreeItemHandle[] {
const elementsToUpdate = new Set<TreeItemHandle>();
for (const element of elements) {
let elementNode = this.nodes.get(element);
const elementNode = this.nodes.get(element);
if (elementNode && !elementsToUpdate.has(elementNode.item.handle)) {
// check if an ancestor of extElement is already in the elements to update list
let currentNode = elementNode;
let currentNode: TreeNode | undefined = elementNode;
while (currentNode && currentNode.parent && !elementsToUpdate.has(currentNode.parent.item.handle)) {
const parentElement = this.elements.get(currentNode.parent.item.handle);
currentNode = this.nodes.get(parentElement);
currentNode = parentElement ? this.nodes.get(parentElement) : undefined;
}
if (!currentNode.parent) {
if (currentNode && !currentNode.parent) {
elementsToUpdate.add(elementNode.item.handle);
}
}
@@ -377,9 +398,11 @@ export class ExtHostTreeView<T> extends Disposable {
// Take only top level elements
elementsToUpdate.forEach((handle) => {
const element = this.elements.get(handle);
let node = this.nodes.get(element);
if (node && (!node.parent || !elementsToUpdate.has(node.parent.item.handle))) {
handlesToUpdate.push(handle);
if (element) {
const node = this.nodes.get(element);
if (node && (!node.parent || !elementsToUpdate.has(node.parent.item.handle))) {
handlesToUpdate.push(handle);
}
}
});
@@ -396,26 +419,31 @@ export class ExtHostTreeView<T> extends Disposable {
itemsToRefresh[treeItemHandle] = node.item;
}
})))
.then(() => Object.keys(itemsToRefresh).length ? this.proxy.$refresh(this.viewId, itemsToRefresh) : null);
.then(() => Object.keys(itemsToRefresh).length ? this.proxy.$refresh(this.viewId, itemsToRefresh) : undefined);
}
// {{SQL CARBON EDIT}}
protected refreshNode(treeItemHandle: TreeItemHandle): Promise<TreeNode> {
protected refreshNode(treeItemHandle: TreeItemHandle): Promise<TreeNode | null> {
const extElement = this.getExtensionElement(treeItemHandle);
const existing = this.nodes.get(extElement);
this.clearChildren(extElement); // clear children cache
return asPromise(() => this.dataProvider.getTreeItem(extElement))
.then(extTreeItem => {
if (extTreeItem) {
const newNode = this.createTreeNode(extElement, extTreeItem, existing.parent);
this.updateNodeCache(extElement, newNode, existing, existing.parent);
return newNode;
}
return null;
});
if (extElement) {
const existing = this.nodes.get(extElement);
if (existing) {
this.clearChildren(extElement); // clear children cache
return asPromise(() => this.dataProvider.getTreeItem(extElement))
.then(extTreeItem => {
if (extTreeItem) {
const newNode = this.createTreeNode(extElement, extTreeItem, existing.parent);
this.updateNodeCache(extElement, newNode, existing, existing.parent);
return newNode;
}
return null;
});
}
}
return Promise.resolve(null);
}
private createAndRegisterTreeNode(element: T, extTreeItem: vscode.TreeItem, parentNode: TreeNode): TreeNode {
private createAndRegisterTreeNode(element: T, extTreeItem: vscode.TreeItem, parentNode: TreeNode | Root): TreeNode {
const node = this.createTreeNode(element, extTreeItem, parentNode);
if (extTreeItem.id && this.elements.has(node.item.handle)) {
throw new Error(localize('treeView.duplicateElement', 'Element with id {0} is already registered', extTreeItem.id));
@@ -426,7 +454,7 @@ export class ExtHostTreeView<T> extends Disposable {
}
// {{SQL CARBON EDIT}}
protected createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode): TreeNode {
protected createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode | Root): TreeNode {
return {
item: this.createTreeItem(element, extensionTreeItem, parent),
parent,
@@ -435,7 +463,7 @@ export class ExtHostTreeView<T> extends Disposable {
}
// {{SQL CARBON EDIT}}
protected createTreeItem(element: T, extensionTreeItem: azdata.TreeItem, parent?: TreeNode): sqlITreeItem {
protected createTreeItem(element: T, extensionTreeItem: azdata.TreeItem, parent: TreeNode | Root): ITreeItem {
const handle = this.createHandle(element, extensionTreeItem, parent);
const icon = this.getLightIconPath(extensionTreeItem);
@@ -460,16 +488,16 @@ export class ExtHostTreeView<T> extends Disposable {
return item;
}
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent: TreeNode, returnFirst?: boolean): TreeItemHandle {
private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent: TreeNode | Root, returnFirst?: boolean): TreeItemHandle {
if (id) {
return `${ExtHostTreeView.ID_HANDLE_PREFIX}/${id}`;
}
const treeItemLabel = toTreeItemLabel(label, this.extension);
const prefix: string = parent ? parent.item.handle : ExtHostTreeView.LABEL_HANDLE_PREFIX;
let elementId = treeItemLabel ? treeItemLabel.label : resourceUri ? basename(resourceUri.path) : '';
let elementId = treeItemLabel ? treeItemLabel.label : resourceUri ? basename(resourceUri) : '';
elementId = elementId.indexOf('/') !== -1 ? elementId.replace('/', '//') : elementId;
const existingHandle = this.nodes.has(element) ? this.nodes.get(element).item.handle : undefined;
const existingHandle = this.nodes.has(element) ? this.nodes.get(element)!.item.handle : undefined;
const childrenNodes = (this.getChildrenNodes(parent) || []);
let handle: TreeItemHandle;
@@ -488,7 +516,7 @@ export class ExtHostTreeView<T> extends Disposable {
return handle;
}
private getLightIconPath(extensionTreeItem: vscode.TreeItem): URI {
private getLightIconPath(extensionTreeItem: vscode.TreeItem): URI | undefined {
if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon)) {
if (typeof extensionTreeItem.iconPath === 'string'
|| extensionTreeItem.iconPath instanceof URI) {
@@ -499,7 +527,7 @@ export class ExtHostTreeView<T> extends Disposable {
return undefined;
}
private getDarkIconPath(extensionTreeItem: vscode.TreeItem): URI {
private getDarkIconPath(extensionTreeItem: vscode.TreeItem): URI | undefined {
if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon) && extensionTreeItem.iconPath['dark']) {
return this.getIconPath(extensionTreeItem.iconPath['dark']);
}
@@ -519,7 +547,7 @@ export class ExtHostTreeView<T> extends Disposable {
}
// {{SQL CARBON EDIT}}
protected updateNodeCache(element: T, newNode: TreeNode, existing: TreeNode, parentNode: TreeNode): void {
protected updateNodeCache(element: T, newNode: TreeNode, existing: TreeNode, parentNode: TreeNode | Root): void {
// Remove from the cache
this.elements.delete(newNode.item.handle);
this.nodes.delete(element);
@@ -538,7 +566,7 @@ export class ExtHostTreeView<T> extends Disposable {
}
}
private addNodeToParentCache(node: TreeNode, parentNode: TreeNode): void {
private addNodeToParentCache(node: TreeNode, parentNode: TreeNode | Root): void {
if (parentNode) {
if (!parentNode.children) {
parentNode.children = [];
@@ -554,7 +582,26 @@ export class ExtHostTreeView<T> extends Disposable {
private clearChildren(parentElement?: T): void {
if (parentElement) {
let node = this.nodes.get(parentElement);
const node = this.nodes.get(parentElement);
if (node) {
if (node.children) {
for (const child of node.children) {
const childEleement = this.elements.get(child.item.handle);
if (childEleement) {
this.clear(childEleement);
}
}
}
node.children = undefined;
}
} else {
this.clearAll();
}
}
private clear(element: T): void {
const node = this.nodes.get(element);
if (node) {
if (node.children) {
for (const child of node.children) {
const childEleement = this.elements.get(child.item.handle);
@@ -563,26 +610,11 @@ export class ExtHostTreeView<T> extends Disposable {
}
}
}
node.children = undefined;
} else {
this.clearAll();
this.nodes.delete(element);
this.elements.delete(node.item.handle);
}
}
private clear(element: T): void {
let node = this.nodes.get(element);
if (node.children) {
for (const child of node.children) {
const childEleement = this.elements.get(child.item.handle);
if (childEleement) {
this.clear(childEleement);
}
}
}
this.nodes.delete(element);
this.elements.delete(node.item.handle);
}
// {{SQL CARBON EDIT}}
protected clearAll(): void {
this.roots = null;

View File

@@ -5,7 +5,7 @@
import * as modes from 'vs/editor/common/modes';
import * as types from './extHostTypes';
import * as search from 'vs/workbench/parts/search/common/search';
import * as search from 'vs/workbench/contrib/search/common/search';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon';
@@ -28,6 +28,7 @@ import * as marked from 'vs/base/common/marked/marked';
import { parse } from 'vs/base/common/marshalling';
import { cloneAndChange } from 'vs/base/common/objects';
import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log';
import { coalesce } from 'vs/base/common/arrays';
export interface PositionLike {
line: number;
@@ -64,7 +65,10 @@ export namespace Selection {
}
export namespace Range {
export function from(range: RangeLike): IRange {
export function from(range: undefined): undefined;
export function from(range: RangeLike): IRange;
export function from(range: RangeLike | undefined): IRange | undefined;
export function from(range: RangeLike | undefined): IRange | undefined {
if (!range) {
return undefined;
}
@@ -77,7 +81,10 @@ export namespace Range {
};
}
export function to(range: IRange): types.Range {
export function to(range: undefined): types.Range;
export function to(range: IRange): types.Range;
export function to(range: IRange | undefined): types.Range | undefined;
export function to(range: IRange | undefined): types.Range | undefined {
if (!range) {
return undefined;
}
@@ -96,7 +103,7 @@ export namespace Position {
}
export namespace DiagnosticTag {
export function from(value: vscode.DiagnosticTag): MarkerTag {
export function from(value: vscode.DiagnosticTag): MarkerTag | undefined {
switch (value) {
case types.DiagnosticTag.Unnecessary:
return MarkerTag.Unnecessary;
@@ -114,7 +121,7 @@ export namespace Diagnostic {
code: isString(value.code) || isNumber(value.code) ? String(value.code) : undefined,
severity: DiagnosticSeverity.from(value.severity),
relatedInformation: value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.from),
tags: Array.isArray(value.tags) ? value.tags.map(DiagnosticTag.from) : undefined,
tags: Array.isArray(value.tags) ? coalesce(value.tags.map(DiagnosticTag.from)) : undefined,
};
}
}
@@ -175,12 +182,12 @@ export namespace ViewColumn {
return ACTIVE_GROUP; // default is always the active group
}
export function to(position?: EditorViewColumn): vscode.ViewColumn {
export function to(position: EditorViewColumn): vscode.ViewColumn {
if (typeof position === 'number' && position >= 0) {
return position + 1; // adjust to index (ViewColumn.ONE => 1)
}
return undefined;
throw new Error(`invalid 'EditorViewColumn'`);
}
}
@@ -226,13 +233,15 @@ export namespace MarkdownString {
}
// extract uris into a separate object
res.uris = Object.create(null);
let renderer = new marked.Renderer();
const resUris: { [href: string]: UriComponents } = Object.create(null);
res.uris = resUris;
const renderer = new marked.Renderer();
renderer.image = renderer.link = (href: string): string => {
try {
let uri = URI.parse(href, true);
uri = uri.with({ query: _uriMassage(uri.query, res.uris) });
res.uris[href] = uri;
uri = uri.with({ query: _uriMassage(uri.query, resUris) });
resUris[href] = uri;
} catch (e) {
// ignore
}
@@ -258,7 +267,7 @@ export namespace MarkdownString {
}
data = cloneAndChange(data, value => {
if (value instanceof URI) {
let key = `__uri_${Math.random().toString(16).slice(2, 8)}`;
const key = `__uri_${Math.random().toString(16).slice(2, 8)}`;
bucket[key] = value;
return key;
} else {
@@ -284,10 +293,12 @@ export namespace MarkdownString {
export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.DecorationOptions[]): IDecorationOptions[] {
if (isDecorationOptionsArr(ranges)) {
return ranges.map(r => {
return ranges.map((r): IDecorationOptions => {
return {
range: Range.from(r.range),
hoverMessage: Array.isArray(r.hoverMessage) ? MarkdownString.fromMany(r.hoverMessage) : r.hoverMessage && MarkdownString.from(r.hoverMessage),
hoverMessage: Array.isArray(r.hoverMessage)
? MarkdownString.fromMany(r.hoverMessage)
: (r.hoverMessage ? MarkdownString.from(r.hoverMessage) : undefined),
renderOptions: <any> /* URI vs Uri */r.renderOptions
};
});
@@ -300,7 +311,7 @@ export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.Deco
}
}
function pathOrURIToURI(value: string | URI): URI {
export function pathOrURIToURI(value: string | URI): URI {
if (typeof value === 'undefined') {
return value;
}
@@ -318,7 +329,7 @@ export namespace ThemableDecorationAttachmentRenderOptions {
}
return {
contentText: options.contentText,
contentIconPath: pathOrURIToURI(options.contentIconPath),
contentIconPath: options.contentIconPath ? pathOrURIToURI(options.contentIconPath) : undefined,
border: options.border,
borderColor: <string | types.ThemeColor>options.borderColor,
fontStyle: options.fontStyle,
@@ -357,11 +368,11 @@ export namespace ThemableDecorationRenderOptions {
color: <string | types.ThemeColor>options.color,
opacity: options.opacity,
letterSpacing: options.letterSpacing,
gutterIconPath: pathOrURIToURI(options.gutterIconPath),
gutterIconPath: options.gutterIconPath ? pathOrURIToURI(options.gutterIconPath) : undefined,
gutterIconSize: options.gutterIconSize,
overviewRulerColor: <string | types.ThemeColor>options.overviewRulerColor,
before: ThemableDecorationAttachmentRenderOptions.from(options.before),
after: ThemableDecorationAttachmentRenderOptions.from(options.after),
before: options.before ? ThemableDecorationAttachmentRenderOptions.from(options.before) : undefined,
after: options.after ? ThemableDecorationAttachmentRenderOptions.from(options.after) : undefined,
};
}
}
@@ -388,10 +399,10 @@ export namespace DecorationRenderOptions {
export function from(options: vscode.DecorationRenderOptions): IDecorationRenderOptions {
return {
isWholeLine: options.isWholeLine,
rangeBehavior: DecorationRangeBehavior.from(options.rangeBehavior),
rangeBehavior: options.rangeBehavior ? DecorationRangeBehavior.from(options.rangeBehavior) : undefined,
overviewRulerLane: options.overviewRulerLane,
light: ThemableDecorationRenderOptions.from(options.light),
dark: ThemableDecorationRenderOptions.from(options.dark),
light: options.light ? ThemableDecorationRenderOptions.from(options.light) : undefined,
dark: options.dark ? ThemableDecorationRenderOptions.from(options.dark) : undefined,
backgroundColor: <string | types.ThemeColor>options.backgroundColor,
outline: options.outline,
@@ -411,11 +422,11 @@ export namespace DecorationRenderOptions {
color: <string | types.ThemeColor>options.color,
opacity: options.opacity,
letterSpacing: options.letterSpacing,
gutterIconPath: pathOrURIToURI(options.gutterIconPath),
gutterIconPath: options.gutterIconPath ? pathOrURIToURI(options.gutterIconPath) : undefined,
gutterIconSize: options.gutterIconSize,
overviewRulerColor: <string | types.ThemeColor>options.overviewRulerColor,
before: ThemableDecorationAttachmentRenderOptions.from(options.before),
after: ThemableDecorationAttachmentRenderOptions.from(options.after),
before: options.before ? ThemableDecorationAttachmentRenderOptions.from(options.before) : undefined,
after: options.after ? ThemableDecorationAttachmentRenderOptions.from(options.after) : undefined,
};
}
}
@@ -432,7 +443,7 @@ export namespace TextEdit {
export function to(edit: modes.TextEdit): types.TextEdit {
const result = new types.TextEdit(Range.to(edit.range), edit.text);
result.newEol = EndOfLine.to(edit.eol);
result.newEol = (typeof edit.eol === 'undefined' ? undefined : EndOfLine.to(edit.eol))!;
return result;
}
}
@@ -446,7 +457,7 @@ export namespace WorkspaceEdit {
const [uri, uriOrEdits] = entry;
if (Array.isArray(uriOrEdits)) {
// text edits
const doc = documents ? documents.getDocument(uri.toString()) : undefined;
const doc = documents && uri ? documents.getDocument(uri) : undefined;
result.edits.push(<ResourceTextEditDto>{ resource: uri, modelVersionId: doc && doc.version, edits: uriOrEdits.map(TextEdit.from) });
} else {
// resource edits
@@ -648,7 +659,7 @@ export namespace CompletionContext {
export namespace CompletionItemKind {
export function from(kind: types.CompletionItemKind): modes.CompletionItemKind {
export function from(kind: types.CompletionItemKind | undefined): modes.CompletionItemKind {
switch (kind) {
case types.CompletionItemKind.Method: return modes.CompletionItemKind.Method;
case types.CompletionItemKind.Function: return modes.CompletionItemKind.Function;
@@ -724,9 +735,9 @@ export namespace CompletionItem {
result.preselect = suggestion.preselect;
result.commitCharacters = suggestion.commitCharacters;
result.range = Range.to(suggestion.range);
result.keepWhitespace = Boolean(suggestion.insertTextRules & modes.CompletionItemInsertTextRule.KeepWhitespace);
result.keepWhitespace = typeof suggestion.insertTextRules === 'undefined' ? false : Boolean(suggestion.insertTextRules & modes.CompletionItemInsertTextRule.KeepWhitespace);
// 'inserText'-logic
if (suggestion.insertTextRules & modes.CompletionItemInsertTextRule.InsertAsSnippet) {
if (typeof suggestion.insertTextRules !== 'undefined' && suggestion.insertTextRules & modes.CompletionItemInsertTextRule.InsertAsSnippet) {
result.insertText = new types.SnippetString(suggestion.insertText);
} else {
result.insertText = suggestion.insertText;
@@ -742,7 +753,7 @@ export namespace ParameterInformation {
export function from(info: types.ParameterInformation): modes.ParameterInformation {
return {
label: info.label,
documentation: MarkdownString.fromStrict(info.documentation)
documentation: info.documentation ? MarkdownString.fromStrict(info.documentation) : undefined
};
}
export function to(info: modes.ParameterInformation): types.ParameterInformation {
@@ -758,7 +769,7 @@ export namespace SignatureInformation {
export function from(info: types.SignatureInformation): modes.SignatureInformation {
return {
label: info.label,
documentation: MarkdownString.fromStrict(info.documentation),
documentation: info.documentation ? MarkdownString.fromStrict(info.documentation) : undefined,
parameters: info.parameters && info.parameters.map(ParameterInformation.from)
};
}
@@ -796,12 +807,20 @@ export namespace DocumentLink {
export function from(link: vscode.DocumentLink): modes.ILink {
return {
range: Range.from(link.range),
url: link.target && link.target.toString()
url: link.target
};
}
export function to(link: modes.ILink): vscode.DocumentLink {
return new types.DocumentLink(Range.to(link.range), link.url && URI.parse(link.url));
let target: URI | undefined = undefined;
if (link.url) {
try {
target = typeof link.url === 'string' ? URI.parse(link.url, true) : URI.revive(link.url);
} catch (err) {
// ignore
}
}
return new types.DocumentLink(Range.to(link.range), target);
}
}
@@ -835,27 +854,17 @@ export namespace Color {
}
}
export namespace SelectionRangeKind {
export function from(kind: vscode.SelectionRangeKind): string {
return kind.value;
}
export function to(value: string): vscode.SelectionRangeKind {
return new types.SelectionRangeKind(value);
}
}
export namespace SelectionRange {
export function from(obj: vscode.SelectionRange): modes.SelectionRange {
return {
kind: SelectionRangeKind.from(obj.kind),
kind: '',
range: Range.from(obj.range)
};
}
export function to(obj: modes.SelectionRange): vscode.SelectionRange {
return new types.SelectionRange(Range.to(obj.range), SelectionRangeKind.to(obj.kind));
return new types.SelectionRange(Range.to(obj.range));
}
}
@@ -877,7 +886,7 @@ export namespace TextDocumentSaveReason {
export namespace EndOfLine {
export function from(eol: vscode.EndOfLine): EndOfLineSequence {
export function from(eol: vscode.EndOfLine): EndOfLineSequence | undefined {
if (eol === types.EndOfLine.CRLF) {
return EndOfLineSequence.CRLF;
} else if (eol === types.EndOfLine.LF) {
@@ -886,7 +895,7 @@ export namespace EndOfLine {
return undefined;
}
export function to(eol: EndOfLineSequence): vscode.EndOfLine {
export function to(eol: EndOfLineSequence): vscode.EndOfLine | undefined {
if (eol === EndOfLineSequence.CRLF) {
return types.EndOfLine.CRLF;
} else if (eol === EndOfLineSequence.LF) {
@@ -903,7 +912,7 @@ export namespace ProgressLocation {
case types.ProgressLocation.Window: return MainProgressLocation.Window;
case types.ProgressLocation.Notification: return MainProgressLocation.Notification;
}
return undefined;
throw new Error(`Unknown 'ProgressLocation'`);
}
}
@@ -935,7 +944,7 @@ export namespace FoldingRangeKind {
export namespace TextEditorOptions {
export function from(options?: vscode.TextDocumentShowOptions): ITextEditorOptions {
export function from(options?: vscode.TextDocumentShowOptions): ITextEditorOptions | undefined {
if (options) {
return {
pinned: typeof options.preview === 'boolean' ? !options.preview : undefined,
@@ -951,7 +960,10 @@ export namespace TextEditorOptions {
export namespace GlobPattern {
export function from(pattern: vscode.GlobPattern): string | types.RelativePattern {
export function from(pattern: vscode.GlobPattern): string | types.RelativePattern;
export function from(pattern: undefined | null): undefined | null;
export function from(pattern: vscode.GlobPattern | undefined | null): string | types.RelativePattern | undefined | null;
export function from(pattern: vscode.GlobPattern | undefined | null): string | types.RelativePattern | undefined | null {
if (pattern instanceof types.RelativePattern) {
return pattern;
}
@@ -975,7 +987,10 @@ export namespace GlobPattern {
export namespace LanguageSelector {
export function from(selector: vscode.DocumentSelector): languageSelector.LanguageSelector {
export function from(selector: undefined): undefined;
export function from(selector: vscode.DocumentSelector): languageSelector.LanguageSelector;
export function from(selector: vscode.DocumentSelector | undefined): languageSelector.LanguageSelector | undefined;
export function from(selector: vscode.DocumentSelector | undefined): languageSelector.LanguageSelector | undefined {
if (!selector) {
return undefined;
} else if (Array.isArray(selector)) {
@@ -986,7 +1001,7 @@ export namespace LanguageSelector {
return <languageSelector.LanguageFilter>{
language: selector.language,
scheme: selector.scheme,
pattern: GlobPattern.from(selector.pattern),
pattern: typeof selector.pattern === 'undefined' ? undefined : GlobPattern.from(selector.pattern),
exclusive: selector.exclusive
};
}

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import * as crypto from 'crypto';
import { relative } from 'path';
import { coalesce, equals } from 'vs/base/common/arrays';
import { illegalArgument } from 'vs/base/common/errors';
import { IRelativePattern } from 'vs/base/common/glob';
@@ -16,6 +15,18 @@ import { generateUuid } from 'vs/base/common/uuid';
import * as vscode from 'vscode';
function es5ClassCompat(target: Function): any {
///@ts-ignore
function _() { return Reflect.construct(target, arguments, this.constructor); }
Object.defineProperty(_, 'name', Object.getOwnPropertyDescriptor(target, 'name')!);
///@ts-ignore
Object.setPrototypeOf(_, target);
///@ts-ignore
Object.setPrototypeOf(_.prototype, target.prototype);
return _;
}
@es5ClassCompat
export class Disposable {
static from(...inDisposables: { dispose(): any }[]): Disposable {
@@ -46,6 +57,7 @@ export class Disposable {
}
}
@es5ClassCompat
export class Position {
static Min(...positions: Position[]): Position {
@@ -54,7 +66,7 @@ export class Position {
}
let result = positions[0];
for (let i = 1; i < positions.length; i++) {
let p = positions[i];
const p = positions[i];
if (p.isBefore(result!)) {
result = p;
}
@@ -68,7 +80,7 @@ export class Position {
}
let result = positions[0];
for (let i = 1; i < positions.length; i++) {
let p = positions[i];
const p = positions[i];
if (p.isAfter(result!)) {
result = p;
}
@@ -217,6 +229,7 @@ export class Position {
}
}
@es5ClassCompat
export class Range {
static isRange(thing: any): thing is vscode.Range {
@@ -290,8 +303,8 @@ export class Range {
}
intersection(other: Range): Range | undefined {
let start = Position.Max(other.start, this._start);
let end = Position.Min(other.end, this._end);
const start = Position.Max(other.start, this._start);
const end = Position.Min(other.end, this._end);
if (start.isAfter(end)) {
// this happens when there is no overlap:
// |-----|
@@ -307,8 +320,8 @@ export class Range {
} else if (other.contains(this)) {
return other;
}
let start = Position.Min(other.start, this._start);
let end = Position.Max(other.end, this.end);
const start = Position.Min(other.start, this._start);
const end = Position.Max(other.end, this.end);
return new Range(start, end);
}
@@ -351,6 +364,7 @@ export class Range {
}
}
@es5ClassCompat
export class Selection extends Range {
static isSelection(thing: any): thing is Selection {
@@ -416,11 +430,30 @@ export class Selection extends Range {
}
}
export class ResolvedAuthority {
readonly host: string;
readonly port: number;
debugListenPort?: number;
debugConnectPort?: number;
constructor(host: string, port: number) {
if (typeof host !== 'string' || host.length === 0) {
throw illegalArgument('host');
}
if (typeof port !== 'number' || port === 0 || Math.round(port) !== port) {
throw illegalArgument('port');
}
this.host = host;
this.port = Math.round(port);
}
}
export enum EndOfLine {
LF = 1,
CRLF = 2
}
@es5ClassCompat
export class TextEdit {
static isTextEdit(thing: any): thing is TextEdit {
@@ -447,13 +480,13 @@ export class TextEdit {
}
static setEndOfLine(eol: EndOfLine): TextEdit {
let ret = new TextEdit(new Range(new Position(0, 0), new Position(0, 0)), '');
const ret = new TextEdit(new Range(new Position(0, 0), new Position(0, 0)), '');
ret.newEol = eol;
return ret;
}
protected _range: Range;
protected _newText: string;
protected _newText: string | null;
protected _newEol: EndOfLine;
get range(): Range {
@@ -489,9 +522,9 @@ export class TextEdit {
this._newEol = value;
}
constructor(range: Range, newText: string) {
constructor(range: Range, newText: string | null) {
this.range = range;
this.newText = newText;
this._newText = newText;
}
toJSON(): any {
@@ -524,6 +557,7 @@ export interface IFileTextEdit {
edit: TextEdit;
}
@es5ClassCompat
export class WorkspaceEdit implements vscode.WorkspaceEdit {
private _edits = new Array<IFileOperation | IFileTextEdit>();
@@ -582,7 +616,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
}
get(uri: URI): TextEdit[] {
let res: TextEdit[] = [];
const res: TextEdit[] = [];
for (let candidate of this._edits) {
if (candidate._type === 2 && candidate.uri.toString() === uri.toString()) {
res.push(candidate.edit);
@@ -592,7 +626,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
}
entries(): [URI, TextEdit[]][] {
let textEdits = new Map<string, [URI, TextEdit[]]>();
const textEdits = new Map<string, [URI, TextEdit[]]>();
for (let candidate of this._edits) {
if (candidate._type === 2) {
let textEdit = textEdits.get(candidate.uri.toString());
@@ -607,7 +641,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
}
_allEntries(): ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] {
let res: ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] = [];
const res: ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] = [];
for (let edit of this._edits) {
if (edit._type === 1) {
res.push([edit.from, edit.to, edit.options]);
@@ -627,6 +661,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
}
}
@es5ClassCompat
export class SnippetString {
static isSnippetString(thing: any): thing is SnippetString {
@@ -720,6 +755,7 @@ export enum DiagnosticSeverity {
Error = 0
}
@es5ClassCompat
export class Location {
static isLocation(thing: any): thing is Location {
@@ -758,6 +794,7 @@ export class Location {
}
}
@es5ClassCompat
export class DiagnosticRelatedInformation {
static is(thing: any): thing is DiagnosticRelatedInformation {
@@ -791,6 +828,7 @@ export class DiagnosticRelatedInformation {
}
}
@es5ClassCompat
export class Diagnostic {
range: Range;
@@ -835,6 +873,7 @@ export class Diagnostic {
}
}
@es5ClassCompat
export class Hover {
public contents: vscode.MarkdownString[] | vscode.MarkedString[];
@@ -864,6 +903,7 @@ export enum DocumentHighlightKind {
Write = 2
}
@es5ClassCompat
export class DocumentHighlight {
range: Range;
@@ -911,6 +951,7 @@ export enum SymbolKind {
TypeParameter = 25
}
@es5ClassCompat
export class SymbolInformation {
static validate(candidate: SymbolInformation): void {
@@ -924,9 +965,9 @@ export class SymbolInformation {
kind: SymbolKind;
containerName: string | undefined;
constructor(name: string, kind: SymbolKind, containerName: string, location: Location);
constructor(name: string, kind: SymbolKind, containerName: string | undefined, location: Location);
constructor(name: string, kind: SymbolKind, range: Range, uri?: URI, containerName?: string);
constructor(name: string, kind: SymbolKind, rangeOrContainer: string | Range, locationOrUri?: Location | URI, containerName?: string) {
constructor(name: string, kind: SymbolKind, rangeOrContainer: string | undefined | Range, locationOrUri?: Location | URI, containerName?: string) {
this.name = name;
this.kind = kind;
this.containerName = containerName;
@@ -954,6 +995,7 @@ export class SymbolInformation {
}
}
@es5ClassCompat
export class DocumentSymbol {
static validate(candidate: DocumentSymbol): void {
@@ -993,6 +1035,7 @@ export enum CodeActionTrigger {
Manual = 2,
}
@es5ClassCompat
export class CodeAction {
title: string;
@@ -1011,18 +1054,19 @@ export class CodeAction {
}
@es5ClassCompat
export class CodeActionKind {
private static readonly sep = '.';
public static readonly Empty = new CodeActionKind('');
public static readonly QuickFix = CodeActionKind.Empty.append('quickfix');
public static readonly Refactor = CodeActionKind.Empty.append('refactor');
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');
public static readonly SourceFixAll = CodeActionKind.Source.append('fixAll');
public static Empty;
public static QuickFix;
public static Refactor;
public static RefactorExtract;
public static RefactorInline;
public static RefactorRewrite;
public static Source;
public static SourceOrganizeImports;
public static SourceFixAll;
constructor(
public readonly value: string
@@ -1040,38 +1084,34 @@ export class CodeActionKind {
return this.value === other.value || startsWith(other.value, this.value + CodeActionKind.sep);
}
}
CodeActionKind.Empty = new CodeActionKind('');
CodeActionKind.QuickFix = CodeActionKind.Empty.append('quickfix');
CodeActionKind.Refactor = CodeActionKind.Empty.append('refactor');
CodeActionKind.RefactorExtract = CodeActionKind.Refactor.append('extract');
CodeActionKind.RefactorInline = CodeActionKind.Refactor.append('inline');
CodeActionKind.RefactorRewrite = CodeActionKind.Refactor.append('rewrite');
CodeActionKind.Source = CodeActionKind.Empty.append('source');
CodeActionKind.SourceOrganizeImports = CodeActionKind.Source.append('organizeImports');
CodeActionKind.SourceFixAll = CodeActionKind.Source.append('fixAll');
export class SelectionRangeKind {
private static readonly _sep = '.';
static readonly Empty = new SelectionRangeKind('');
static readonly Statement = SelectionRangeKind.Empty.append('statement');
static readonly Declaration = SelectionRangeKind.Empty.append('declaration');
readonly value: string;
constructor(value: string) {
this.value = value;
}
append(value: string): SelectionRangeKind {
return new SelectionRangeKind(this.value ? this.value + SelectionRangeKind._sep + value : value);
}
}
@es5ClassCompat
export class SelectionRange {
kind: SelectionRangeKind;
range: Range;
parent?: SelectionRange;
constructor(range: Range, kind: SelectionRangeKind, ) {
constructor(range: Range, parent?: SelectionRange) {
this.range = range;
this.kind = kind;
this.parent = parent;
if (parent && !parent.range.contains(this.range)) {
throw new Error('Invalid argument: parent must contain this range');
}
}
}
@es5ClassCompat
export class CodeLens {
range: Range;
@@ -1088,6 +1128,20 @@ export class CodeLens {
}
}
export class CodeInset {
range: Range;
height?: number;
constructor(range: Range, height?: number) {
this.range = range;
this.height = height;
}
}
@es5ClassCompat
export class MarkdownString {
value: string;
@@ -1118,6 +1172,7 @@ export class MarkdownString {
}
}
@es5ClassCompat
export class ParameterInformation {
label: string | [number, number];
@@ -1129,6 +1184,7 @@ export class ParameterInformation {
}
}
@es5ClassCompat
export class SignatureInformation {
label: string;
@@ -1142,6 +1198,7 @@ export class SignatureInformation {
}
}
@es5ClassCompat
export class SignatureHelp {
signatures: SignatureInformation[];
@@ -1166,8 +1223,8 @@ export enum CompletionTriggerKind {
}
export interface CompletionContext {
triggerKind: CompletionTriggerKind;
triggerCharacter: string;
readonly triggerKind: CompletionTriggerKind;
readonly triggerCharacter?: string;
}
export enum CompletionItemKind {
@@ -1198,19 +1255,20 @@ export enum CompletionItemKind {
TypeParameter = 24
}
@es5ClassCompat
export class CompletionItem implements vscode.CompletionItem {
label: string;
kind: CompletionItemKind | undefined;
detail: string;
documentation: string | MarkdownString;
sortText: string;
filterText: string;
preselect: boolean;
detail?: string;
documentation?: string | MarkdownString;
sortText?: string;
filterText?: string;
preselect?: boolean;
insertText: string | SnippetString;
keepWhitespace?: boolean;
range: Range;
commitCharacters: string[];
commitCharacters?: string[];
textEdit: TextEdit;
additionalTextEdits: TextEdit[];
command: vscode.Command;
@@ -1235,6 +1293,7 @@ export class CompletionItem implements vscode.CompletionItem {
}
}
@es5ClassCompat
export class CompletionList {
isIncomplete?: boolean;
@@ -1314,7 +1373,7 @@ export enum DecorationRangeBehavior {
}
export namespace TextEditorSelectionChangeKind {
export function fromValue(s: string) {
export function fromValue(s: string | undefined) {
switch (s) {
case 'keyboard': return TextEditorSelectionChangeKind.Keyboard;
case 'mouse': return TextEditorSelectionChangeKind.Mouse;
@@ -1324,13 +1383,14 @@ export namespace TextEditorSelectionChangeKind {
}
}
@es5ClassCompat
export class DocumentLink {
range: Range;
target: URI;
target?: URI;
constructor(range: Range, target: URI) {
constructor(range: Range, target: URI | undefined) {
if (target && !(target instanceof URI)) {
throw illegalArgument('target');
}
@@ -1342,6 +1402,7 @@ export class DocumentLink {
}
}
@es5ClassCompat
export class Color {
readonly red: number;
readonly green: number;
@@ -1358,6 +1419,7 @@ export class Color {
export type IColorFormat = string | { opaque: string, transparent: string };
@es5ClassCompat
export class ColorInformation {
range: Range;
@@ -1375,6 +1437,7 @@ export class ColorInformation {
}
}
@es5ClassCompat
export class ColorPresentation {
label: string;
textEdit?: TextEdit;
@@ -1416,6 +1479,7 @@ export enum TaskPanelKind {
New = 3
}
@es5ClassCompat
export class TaskGroup implements vscode.TaskGroup {
private _id: string;
@@ -1458,6 +1522,7 @@ export class TaskGroup implements vscode.TaskGroup {
}
}
@es5ClassCompat
export class ProcessExecution implements vscode.ProcessExecution {
private _process: string;
@@ -1530,6 +1595,7 @@ export class ProcessExecution implements vscode.ProcessExecution {
}
}
@es5ClassCompat
export class ShellExecution implements vscode.ShellExecution {
private _commandLine: string;
@@ -1626,8 +1692,33 @@ export enum TaskScope {
Workspace = 2
}
export class Task implements vscode.Task {
export class CustomExecution implements vscode.CustomExecution {
private _callback: (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number>;
constructor(callback: (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number>) {
this._callback = callback;
}
public computeId(): string {
const hash = crypto.createHash('md5');
hash.update('customExecution');
hash.update(generateUuid());
return hash.digest('hex');
}
public set callback(value: (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number>) {
this._callback = value;
}
public get callback(): (args: vscode.TerminalRenderer, cancellationToken: vscode.CancellationToken) => Thenable<number> {
return this._callback;
}
}
@es5ClassCompat
export class Task implements vscode.Task2 {
private static ExtensionCallbackType: string = 'customExecution';
private static ProcessType: string = 'process';
private static ShellType: string = 'shell';
private static EmptyType: string = '$empty';
@@ -1637,7 +1728,7 @@ export class Task implements vscode.Task {
private _definition: vscode.TaskDefinition;
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
private _name: string;
private _execution: ProcessExecution | ShellExecution | undefined;
private _execution: ProcessExecution | ShellExecution | CustomExecution | undefined;
private _problemMatchers: string[];
private _hasDefinedMatchers: boolean;
private _isBackground: boolean;
@@ -1646,8 +1737,8 @@ export class Task implements vscode.Task {
private _presentationOptions: vscode.TaskPresentationOptions;
private _runOptions: vscode.RunOptions;
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder, name: string, source: string, execution?: ProcessExecution | ShellExecution | CustomExecution, problemMatchers?: string | string[]);
constructor(definition: vscode.TaskDefinition, arg2: string | (vscode.TaskScope.Global | vscode.TaskScope.Workspace) | vscode.WorkspaceFolder, arg3: any, arg4?: any, arg5?: any, arg6?: any) {
this.definition = definition;
let problemMatchers: string | string[];
@@ -1712,6 +1803,11 @@ export class Task implements vscode.Task {
type: Task.ShellType,
id: this._execution.computeId()
};
} else if (this._execution instanceof CustomExecution) {
this._definition = {
type: Task.ExtensionCallbackType,
id: this._execution.computeId()
};
} else {
this._definition = {
type: Task.EmptyType,
@@ -1754,17 +1850,25 @@ export class Task implements vscode.Task {
}
get execution(): ProcessExecution | ShellExecution | undefined {
return this._execution;
return (this._execution instanceof CustomExecution) ? undefined : this._execution;
}
set execution(value: ProcessExecution | ShellExecution | undefined) {
this.execution2 = value;
}
get execution2(): ProcessExecution | ShellExecution | CustomExecution | undefined {
return this._execution;
}
set execution2(value: ProcessExecution | ShellExecution | CustomExecution | undefined) {
if (value === null) {
value = undefined;
}
this.clear();
this._execution = value;
let type = this._definition.type;
if (Task.EmptyType === type || Task.ProcessType === type || Task.ShellType === type) {
const type = this._definition.type;
if (Task.EmptyType === type || Task.ProcessType === type || Task.ShellType === type || Task.ExtensionCallbackType === type) {
this.computeDefinitionBasedOnExecution();
}
}
@@ -1858,6 +1962,7 @@ export enum ProgressLocation {
Notification = 15
}
@es5ClassCompat
export class TreeItem {
label?: string | vscode.TreeItemLabel;
@@ -1885,18 +1990,23 @@ export enum TreeItemCollapsibleState {
Expanded = 2
}
@es5ClassCompat
export class ThemeIcon {
static readonly File = new ThemeIcon('file');
static readonly Folder = new ThemeIcon('folder');
static File: ThemeIcon;
static Folder: ThemeIcon;
readonly id: string;
private constructor(id: string) {
constructor(id: string) {
this.id = id;
}
}
ThemeIcon.File = new ThemeIcon('file');
ThemeIcon.Folder = new ThemeIcon('folder');
@es5ClassCompat
export class ThemeColor {
id: string;
constructor(id: string) {
@@ -1912,6 +2022,7 @@ export enum ConfigurationTarget {
WorkspaceFolder = 3
}
@es5ClassCompat
export class RelativePattern implements IRelativePattern {
base: string;
baseFolder?: URI;
@@ -1938,12 +2049,9 @@ export class RelativePattern implements IRelativePattern {
this.pattern = pattern;
}
public pathToRelative(from: string, to: string): string {
return relative(from, to);
}
}
@es5ClassCompat
export class Breakpoint {
private _id: string | undefined;
@@ -1974,6 +2082,7 @@ export class Breakpoint {
}
}
@es5ClassCompat
export class SourceBreakpoint extends Breakpoint {
readonly location: Location;
@@ -1986,6 +2095,7 @@ export class SourceBreakpoint extends Breakpoint {
}
}
@es5ClassCompat
export class FunctionBreakpoint extends Breakpoint {
readonly functionName: string;
@@ -1998,7 +2108,7 @@ export class FunctionBreakpoint extends Breakpoint {
}
}
// {{SQL CARBON EDIT}}
@es5ClassCompat
export class DebugAdapterExecutable implements vscode.DebugAdapterExecutable {
readonly command: string;
readonly args: string[];
@@ -2011,6 +2121,7 @@ export class DebugAdapterExecutable implements vscode.DebugAdapterExecutable {
}
}
@es5ClassCompat
export class DebugAdapterServer implements vscode.DebugAdapterServer {
readonly port: number;
readonly host?: string;
@@ -2023,6 +2134,7 @@ export class DebugAdapterServer implements vscode.DebugAdapterServer {
// {{SQL CARBON EDIT}}
/*
@es5ClassCompat
export class DebugAdapterImplementation implements vscode.DebugAdapterImplementation {
readonly implementation: any;
@@ -2050,6 +2162,7 @@ export enum FileChangeType {
Deleted = 3,
}
@es5ClassCompat
export class FileSystemError extends Error {
static FileExists(messageOrUri?: string | URI): FileSystemError {
@@ -2092,6 +2205,7 @@ export class FileSystemError extends Error {
//#region folding api
@es5ClassCompat
export class FoldingRange {
start: number;
@@ -2127,6 +2241,7 @@ export enum CommentThreadCollapsibleState {
Expanded = 1
}
@es5ClassCompat
export class QuickInputButtons {
static readonly Back: vscode.QuickInputButton = { iconPath: 'back.svg' };

View File

@@ -8,14 +8,14 @@ import { URI } from 'vs/base/common/uri';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import * as vscode from 'vscode';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState, WebviewInsetHandle } from './extHost.protocol';
import { Disposable } from './extHostTypes';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
type IconPath = URI | { light: URI, dark: URI };
export class ExtHostWebview implements vscode.Webview {
private readonly _handle: WebviewPanelHandle;
private readonly _handle: WebviewPanelHandle | WebviewInsetHandle;
private readonly _proxy: MainThreadWebviewsShape;
private _html: string;
private _options: vscode.WebviewOptions;
@@ -25,7 +25,7 @@ export class ExtHostWebview implements vscode.Webview {
public readonly onDidReceiveMessage: Event<any> = this._onMessageEmitter.event;
constructor(
handle: WebviewPanelHandle,
handle: WebviewPanelHandle | WebviewInsetHandle,
proxy: MainThreadWebviewsShape,
options: vscode.WebviewOptions
) {
@@ -80,12 +80,12 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
private readonly _proxy: MainThreadWebviewsShape;
private readonly _viewType: string;
private _title: string;
private _iconPath: IconPath;
private _iconPath?: IconPath;
private readonly _options: vscode.WebviewPanelOptions;
private readonly _webview: ExtHostWebview;
private _isDisposed: boolean = false;
private _viewColumn: vscode.ViewColumn;
private _viewColumn: vscode.ViewColumn | undefined;
private _visible: boolean = true;
private _active: boolean = true;
@@ -101,7 +101,7 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
proxy: MainThreadWebviewsShape,
viewType: string,
title: string,
viewColumn: vscode.ViewColumn,
viewColumn: vscode.ViewColumn | undefined,
editorOptions: vscode.WebviewPanelOptions,
webview: ExtHostWebview
) {
@@ -173,7 +173,7 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
get viewColumn(): vscode.ViewColumn | undefined {
this.assertNotDisposed();
if (this._viewColumn < 0) {
if (typeof this._viewColumn === 'number' && this._viewColumn < 0) {
// We are using a symbolic view column
// Return undefined instead to indicate that the real view column is currently unknown but will be resolved.
return undefined;
@@ -243,7 +243,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
this._proxy = mainContext.getProxy(MainContext.MainThreadWebviews);
}
public createWebview(
public createWebviewPanel(
extension: IExtensionDescription,
viewType: string,
title: string,
@@ -333,7 +333,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
const webview = new ExtHostWebview(webviewHandle, this._proxy, options);
const revivedPanel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeConverters.ViewColumn.to(position), options, webview);
const revivedPanel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview);
this._webviewPanels.set(webviewHandle, revivedPanel);
return Promise.resolve(serializer.deserializeWebviewPanel(revivedPanel, state));
}

View File

@@ -3,28 +3,34 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { join, relative } from 'path';
import { join } from 'vs/base/common/path';
import { delta as arrayDelta, mapArrayOrNot } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { Counter } from 'vs/base/common/numbers';
import { normalize } from 'vs/base/common/paths';
import { isLinux } from 'vs/base/common/platform';
import { basenameOrAuthority, dirname, isEqual } from 'vs/base/common/resources';
import { basenameOrAuthority, dirname, isEqual, relativePath } from 'vs/base/common/resources';
import { compare } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { ILogService } from 'vs/platform/log/common/log';
import { Severity } from 'vs/platform/notification/common/notification';
import { IRawFileMatch2, resultIsMatch } from 'vs/platform/search/common/search';
import { IRawFileMatch2, resultIsMatch } from 'vs/workbench/services/search/common/search';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Range, RelativePattern } from 'vs/workbench/api/node/extHostTypes';
import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import * as vscode from 'vscode';
import { ExtHostWorkspaceShape, IMainContext, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtHostWorkspaceShape, IWorkspaceData, MainThreadMessageServiceShape, MainThreadWorkspaceShape, IMainContext, MainContext, IStaticWorkspaceData } from './extHost.protocol';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Barrier } from 'vs/base/common/async';
export interface IExtHostWorkspaceProvider {
getWorkspaceFolder2(uri: vscode.Uri, resolveParent?: boolean): Promise<vscode.WorkspaceFolder | undefined>;
resolveWorkspaceFolder(uri: vscode.Uri): Promise<vscode.WorkspaceFolder | undefined>;
getWorkspaceFolders2(): Promise<vscode.WorkspaceFolder[] | undefined>;
resolveProxy(url: string): Promise<string | undefined>;
}
function isFolderEqual(folderA: URI, folderB: URI): boolean {
return isEqual(folderA, folderB, !isLinux);
@@ -56,7 +62,7 @@ interface MutableWorkspaceFolder extends vscode.WorkspaceFolder {
class ExtHostWorkspaceImpl extends Workspace {
static toExtHostWorkspace(data: IWorkspaceData, previousConfirmedWorkspace?: ExtHostWorkspaceImpl, previousUnconfirmedWorkspace?: ExtHostWorkspaceImpl): { workspace: ExtHostWorkspaceImpl, added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[] } {
static toExtHostWorkspace(data: IWorkspaceData | null, previousConfirmedWorkspace?: ExtHostWorkspaceImpl, previousUnconfirmedWorkspace?: ExtHostWorkspaceImpl): { workspace: ExtHostWorkspaceImpl | null, added: vscode.WorkspaceFolder[], removed: vscode.WorkspaceFolder[] } {
if (!data) {
return { workspace: null, added: [], removed: [] };
}
@@ -68,7 +74,7 @@ class ExtHostWorkspaceImpl extends Workspace {
// data and update their properties. It could be that an extension stored them
// for later use and we want to keep them "live" if they are still present.
const oldWorkspace = previousConfirmedWorkspace;
if (oldWorkspace) {
if (previousConfirmedWorkspace) {
folders.forEach((folderData, index) => {
const folderUri = URI.revive(folderData.uri);
const existingFolder = ExtHostWorkspaceImpl._findFolder(previousUnconfirmedWorkspace || previousConfirmedWorkspace, folderUri);
@@ -95,7 +101,7 @@ class ExtHostWorkspaceImpl extends Workspace {
return { workspace, added, removed };
}
private static _findFolder(workspace: ExtHostWorkspaceImpl, folderUriToFind: URI): MutableWorkspaceFolder {
private static _findFolder(workspace: ExtHostWorkspaceImpl, folderUriToFind: URI): MutableWorkspaceFolder | undefined {
for (let i = 0; i < workspace.folders.length; i++) {
const folder = workspace.workspaceFolders[i];
if (isFolderEqual(folder.uri, folderUriToFind)) {
@@ -109,7 +115,7 @@ class ExtHostWorkspaceImpl extends Workspace {
private readonly _workspaceFolders: vscode.WorkspaceFolder[] = [];
private readonly _structure = TernarySearchTree.forPaths<vscode.WorkspaceFolder>();
private constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[]) {
constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[]) {
super(id, folders.map(f => new WorkspaceFolder(f)));
// setup the workspace folder data structure
@@ -127,7 +133,7 @@ class ExtHostWorkspaceImpl extends Workspace {
return this._workspaceFolders.slice(0);
}
getWorkspaceFolder(uri: URI, resolveParent?: boolean): vscode.WorkspaceFolder {
getWorkspaceFolder(uri: URI, resolveParent?: boolean): vscode.WorkspaceFolder | undefined {
if (resolveParent && this._structure.get(uri.toString())) {
// `uri` is a workspace folder so we check for its parent
uri = dirname(uri);
@@ -135,51 +141,75 @@ class ExtHostWorkspaceImpl extends Workspace {
return this._structure.findSubstr(uri.toString());
}
resolveWorkspaceFolder(uri: URI): vscode.WorkspaceFolder {
resolveWorkspaceFolder(uri: URI): vscode.WorkspaceFolder | undefined {
return this._structure.get(uri.toString());
}
}
export class ExtHostWorkspace implements ExtHostWorkspaceShape {
export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspaceProvider {
private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
private readonly _proxy: MainThreadWorkspaceShape;
private _confirmedWorkspace: ExtHostWorkspaceImpl;
private _unconfirmedWorkspace: ExtHostWorkspaceImpl;
private _messageService: MainThreadMessageServiceShape;
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
private readonly _logService: ILogService;
private readonly _requestIdProvider: Counter;
private readonly _barrier: Barrier;
private _confirmedWorkspace?: ExtHostWorkspaceImpl;
private _unconfirmedWorkspace?: ExtHostWorkspaceImpl;
private readonly _proxy: MainThreadWorkspaceShape;
private readonly _messageService: MainThreadMessageServiceShape;
private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = [];
constructor(
mainContext: IMainContext,
data: IWorkspaceData,
private _logService: ILogService,
private _requestIdProvider: Counter
logService: ILogService,
requestIdProvider: Counter,
data?: IStaticWorkspaceData
) {
this._logService = logService;
this._requestIdProvider = requestIdProvider;
this._barrier = new Barrier();
this._proxy = mainContext.getProxy(MainContext.MainThreadWorkspace);
this._messageService = mainContext.getProxy(MainContext.MainThreadMessageService);
this._confirmedWorkspace = ExtHostWorkspaceImpl.toExtHostWorkspace(data).workspace;
this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, []) : undefined;
}
$initializeWorkspace(data: IWorkspaceData): void {
this.$acceptWorkspaceData(data);
this._barrier.open();
}
waitForInitializeCall(): Promise<boolean> {
return this._barrier.wait();
}
// --- workspace ---
get workspace(): Workspace {
get workspace(): Workspace | undefined {
return this._actualWorkspace;
}
get name(): string {
get name(): string | undefined {
return this._actualWorkspace ? this._actualWorkspace.name : undefined;
}
private get _actualWorkspace(): ExtHostWorkspaceImpl {
private get _actualWorkspace(): ExtHostWorkspaceImpl | undefined {
return this._unconfirmedWorkspace || this._confirmedWorkspace;
}
getWorkspaceFolders(): vscode.WorkspaceFolder[] {
getWorkspaceFolders(): vscode.WorkspaceFolder[] | undefined {
if (!this._actualWorkspace) {
return undefined;
}
return this._actualWorkspace.workspaceFolders.slice(0);
}
async getWorkspaceFolders2(): Promise<vscode.WorkspaceFolder[] | undefined> {
await this._barrier.wait();
if (!this._actualWorkspace) {
return undefined;
}
@@ -215,7 +245,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
// Simulate the updateWorkspaceFolders method on our data to do more validation
const newWorkspaceFolders = currentWorkspaceFolders.slice(0);
newWorkspaceFolders.splice(index, deleteCount, ...validatedDistinctWorkspaceFoldersToAdd.map(f => ({ uri: f.uri, name: f.name || basenameOrAuthority(f.uri), index: undefined })));
newWorkspaceFolders.splice(index, deleteCount, ...validatedDistinctWorkspaceFoldersToAdd.map(f => ({ uri: f.uri, name: f.name || basenameOrAuthority(f.uri), index: undefined! /* fixed later */ })));
for (let i = 0; i < newWorkspaceFolders.length; i++) {
const folder = newWorkspaceFolders[i];
@@ -240,7 +270,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
this._unconfirmedWorkspace = undefined;
// show error to user
this._messageService.$showMessage(Severity.Error, localize('updateerror', "Extension '{0}' failed to update workspace folders: {1}", extName, error.toString()), { extension }, []);
this._messageService.$showMessage(Severity.Error, localize('updateerror', "Extension '{0}' failed to update workspace folders: {1}", extName, error), { extension }, []);
});
}
@@ -250,21 +280,30 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
return true;
}
getWorkspaceFolder(uri: vscode.Uri, resolveParent?: boolean): vscode.WorkspaceFolder {
getWorkspaceFolder(uri: vscode.Uri, resolveParent?: boolean): vscode.WorkspaceFolder | undefined {
if (!this._actualWorkspace) {
return undefined;
}
return this._actualWorkspace.getWorkspaceFolder(uri, resolveParent);
}
resolveWorkspaceFolder(uri: vscode.Uri): vscode.WorkspaceFolder {
async getWorkspaceFolder2(uri: vscode.Uri, resolveParent?: boolean): Promise<vscode.WorkspaceFolder | undefined> {
await this._barrier.wait();
if (!this._actualWorkspace) {
return undefined;
}
return this._actualWorkspace.getWorkspaceFolder(uri, resolveParent);
}
async resolveWorkspaceFolder(uri: vscode.Uri): Promise<vscode.WorkspaceFolder | undefined> {
await this._barrier.wait();
if (!this._actualWorkspace) {
return undefined;
}
return this._actualWorkspace.resolveWorkspaceFolder(uri);
}
getPath(): string {
getPath(): string | undefined {
// this is legacy from the days before having
// multi-root and we keep it only alive if there
@@ -283,19 +322,22 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
getRelativePath(pathOrUri: string | vscode.Uri, includeWorkspace?: boolean): string {
let path: string;
let resource: URI | undefined;
let path: string = '';
if (typeof pathOrUri === 'string') {
resource = URI.file(pathOrUri);
path = pathOrUri;
} else if (typeof pathOrUri !== 'undefined') {
resource = pathOrUri;
path = pathOrUri.fsPath;
}
if (!path) {
if (!resource) {
return path;
}
const folder = this.getWorkspaceFolder(
typeof pathOrUri === 'string' ? URI.file(pathOrUri) : pathOrUri,
resource,
true
);
@@ -303,15 +345,15 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
return path;
}
if (typeof includeWorkspace === 'undefined') {
if (typeof includeWorkspace === 'undefined' && this._actualWorkspace) {
includeWorkspace = this._actualWorkspace.folders.length > 1;
}
let result = relative(folder.uri.fsPath, path);
if (includeWorkspace) {
let result = relativePath(folder.uri, resource);
if (includeWorkspace && folder.name) {
result = `${folder.name}/${result}`;
}
return normalize(result, true);
return result!;
}
private trySetWorkspaceFolders(folders: vscode.WorkspaceFolder[]): void {
@@ -324,7 +366,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
name: this._actualWorkspace.name,
configuration: this._actualWorkspace.configuration,
folders
} as IWorkspaceData, this._actualWorkspace).workspace;
} as IWorkspaceData, this._actualWorkspace).workspace || undefined;
}
}
@@ -334,7 +376,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
// Update our workspace object. We have a confirmed workspace, so we drop our
// unconfirmed workspace.
this._confirmedWorkspace = workspace;
this._confirmedWorkspace = workspace || undefined;
this._unconfirmedWorkspace = undefined;
// Events
@@ -346,11 +388,11 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
// --- search ---
findFiles(include: string | RelativePattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.Uri[]> {
findFiles(include: string | RelativePattern | undefined | null, exclude: vscode.GlobPattern | undefined | null, maxResults: number | undefined, extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.Uri[]> {
this._logService.trace(`extHostWorkspace#findFiles: fileSearch, extension: ${extensionId.value}, entryPoint: findFiles`);
let includePattern: string;
let includeFolder: URI;
let includePattern: string | undefined;
let includeFolder: URI | undefined;
if (include) {
if (typeof include === 'string') {
includePattern = include;
@@ -362,7 +404,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
}
}
let excludePatternOrDisregardExcludes: string | false;
let excludePatternOrDisregardExcludes: string | false | undefined = undefined;
if (exclude === null) {
excludePatternOrDisregardExcludes = false;
} else if (exclude) {
@@ -381,7 +423,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
.then(data => Array.isArray(data) ? data.map(URI.revive) : []);
}
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete> {
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete | undefined> {
this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId.value}, entryPoint: findTextInFiles`);
const requestId = this._requestIdProvider.getNext();
@@ -413,10 +455,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
beforeContext: options.beforeContext,
includePattern: options.include && globPatternToString(options.include),
excludePattern: options.exclude && globPatternToString(options.exclude)
excludePattern: options.exclude ? globPatternToString(options.exclude) : undefined
};
let isCanceled = false;
const isCanceled = false;
this._activeSearchCallbacks[requestId] = p => {
if (isCanceled) {
@@ -424,7 +466,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
}
const uri = URI.revive(p.resource);
p.results.forEach(result => {
p.results!.forEach(result => {
if (resultIsMatch(result)) {
callback(<vscode.TextSearchMatch>{
uri,
@@ -471,7 +513,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
return this._proxy.$saveAll(includeUntitled);
}
resolveProxy(url: string): Promise<string> {
resolveProxy(url: string): Promise<string | undefined> {
return this._proxy.$resolveProxy(url);
}
}

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IEditorGroupsService, IEditorGroup, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService, IEditorGroup, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
import { GroupIdentifier } from 'vs/workbench/common/editor';
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
@@ -31,6 +31,9 @@ export function viewColumnToEditorGroup(editorGroupService: IEditorGroupsService
export function editorGroupToViewColumn(editorGroupService: IEditorGroupsService, editorGroup: IEditorGroup | GroupIdentifier): EditorViewColumn {
const group = (typeof editorGroup === 'number') ? editorGroupService.getGroup(editorGroup) : editorGroup;
if (!group) {
throw new Error('Invalid group provided');
}
return editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE).indexOf(group);
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { UriComponents } from 'vs/base/common/uri';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
export interface TaskDefinitionDTO {
type: string;
@@ -66,6 +66,10 @@ export interface ShellExecutionDTO {
options?: ShellExecutionOptionsDTO;
}
export interface CustomExecutionDTO {
customExecution: 'customExecution';
}
export interface TaskSourceDTO {
label: string;
extensionId?: string;
@@ -79,16 +83,16 @@ export interface TaskHandleDTO {
export interface TaskDTO {
_id: string;
name: string;
execution: ProcessExecutionDTO | ShellExecutionDTO;
name?: string;
execution: ProcessExecutionDTO | ShellExecutionDTO | CustomExecutionDTO | undefined;
definition: TaskDefinitionDTO;
isBackground: boolean;
isBackground?: boolean;
source: TaskSourceDTO;
group?: string;
presentationOptions: TaskPresentationOptionsDTO;
presentationOptions?: TaskPresentationOptionsDTO;
problemMatchers: string[];
hasDefinedMatchers: boolean;
runOptions: RunOptionsDTO;
runOptions?: RunOptionsDTO;
}
export interface TaskSetDTO {
@@ -98,7 +102,7 @@ export interface TaskSetDTO {
export interface TaskExecutionDTO {
id: string;
task: TaskDTO;
task: TaskDTO | undefined;
}
export interface TaskProcessStartedDTO {