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

@@ -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);
}
}