Merge remote-tracking branch 'origin/master' into pr/g-arjones/60111

This commit is contained in:
Daniel Imms
2018-12-13 09:33:42 -08:00
2095 changed files with 72915 additions and 60923 deletions

View File

@@ -20,7 +20,7 @@ import { EditorGroupLayout } from 'vs/workbench/services/group/common/editorGrou
// -----------------------------------------------------------------
export interface ICommandsExecutor {
executeCommand<T>(id: string, ...args: any[]): Thenable<T>;
executeCommand<T>(id: string, ...args: any[]): Promise<T>;
}
function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) => any): ICommandHandler {
@@ -31,7 +31,7 @@ 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): Thenable<any> {
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),
@@ -44,7 +44,7 @@ CommandsRegistry.registerCommand(PreviewHTMLAPICommand.ID, adjustHandler(Preview
export class OpenFolderAPICommand {
public static ID = 'vscode.openFolder';
public static execute(executor: ICommandsExecutor, uri?: URI, forceNewWindow?: boolean): Thenable<any> {
public static execute(executor: ICommandsExecutor, uri?: URI, forceNewWindow?: boolean): Promise<any> {
if (!uri) {
return executor.executeCommand('_files.pickFolderAndOpen', forceNewWindow);
}
@@ -62,7 +62,7 @@ CommandsRegistry.registerCommand(OpenFolderAPICommand.ID, adjustHandler(OpenFold
export class DiffAPICommand {
public static ID = 'vscode.diff';
public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: vscode.TextDocumentShowOptions): Thenable<any> {
public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: vscode.TextDocumentShowOptions): Promise<any> {
return executor.executeCommand('_workbench.diff', [
left, right,
label,
@@ -76,7 +76,7 @@ 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): Thenable<any> {
public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, label?: string): Promise<any> {
let options: ITextEditorOptions;
let position: EditorViewColumn;
@@ -101,7 +101,7 @@ CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand
export class RemoveFromRecentlyOpenedAPICommand {
public static ID = 'vscode.removeFromRecentlyOpened';
public static execute(executor: ICommandsExecutor, path: string): Thenable<any> {
public static execute(executor: ICommandsExecutor, path: string): Promise<any> {
return executor.executeCommand('_workbench.removeFromRecentlyOpened', path);
}
}
@@ -109,7 +109,7 @@ CommandsRegistry.registerCommand(RemoveFromRecentlyOpenedAPICommand.ID, adjustHa
export class SetEditorLayoutAPICommand {
public static ID = 'vscode.setEditorLayout';
public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Thenable<any> {
public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Promise<any> {
return executor.executeCommand('layoutEditorGroups', layout);
}
}

View File

@@ -3,84 +3,76 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { score } from 'vs/editor/common/modes/languageSelector';
import * as platform from 'vs/base/common/platform';
import { localize } from 'vs/nls';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import product from 'vs/platform/node/product';
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 platform from 'vs/base/common/platform';
import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
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 { ExtHostFileSystemEventService } from 'vs/workbench/api/node/extHostFileSystemEventService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import product from 'vs/platform/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';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostComments } from 'vs/workbench/api/node/extHostComments';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs';
import { ExtHostDocumentContentProvider } from 'vs/workbench/api/node/extHostDocumentContentProviders';
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/node/extHostDocumentSaveParticipant';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { ExtHostTreeViews } from 'vs/workbench/api/node/extHostTreeViews';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostQuickOpen } from 'vs/workbench/api/node/extHostQuickOpen';
import { ExtHostProgress } from 'vs/workbench/api/node/extHostProgress';
import { ExtHostSCM } from 'vs/workbench/api/node/extHostSCM';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { ExtHostStatusBar } from 'vs/workbench/api/node/extHostStatusBar';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostOutputService } from 'vs/workbench/api/node/extHostOutputService';
import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService';
import { ExtHostMessageService } from 'vs/workbench/api/node/extHostMessageService';
import { ExtHostEditors } from 'vs/workbench/api/node/extHostTextEditors';
import { ExtHostLanguages } from 'vs/workbench/api/node/extHostLanguages';
import { ExtHostLanguageFeatures, ISchemeTransformer } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands';
import { ExtHostTask } from 'vs/workbench/api/node/extHostTask';
import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostWindow } from 'vs/workbench/api/node/extHostWindow';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import { URI } from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { TPromise } from 'vs/base/common/winjs.base';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as vscode from 'vscode';
import * as paths from 'vs/base/common/paths';
import * as files from 'vs/platform/files/common/files';
import { MainContext, ExtHostContext, IInitData, IMainContext } from './extHost.protocol';
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs';
import { ExtHostFileSystem } from 'vs/workbench/api/node/extHostFileSystem';
import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator';
import { OverviewRulerLane } from 'vs/editor/common/model';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { ExtHostFileSystem } from 'vs/workbench/api/node/extHostFileSystem';
import { ExtHostFileSystemEventService } from 'vs/workbench/api/node/extHostFileSystemEventService';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { ExtHostLanguageFeatures, ISchemeTransformer } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { ExtHostLanguages } from 'vs/workbench/api/node/extHostLanguages';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import { ExtHostMessageService } from 'vs/workbench/api/node/extHostMessageService';
import { ExtHostOutputService } from 'vs/workbench/api/node/extHostOutputService';
import { ExtHostProgress } from 'vs/workbench/api/node/extHostProgress';
import { ExtHostQuickOpen } from 'vs/workbench/api/node/extHostQuickOpen';
import { ExtHostSCM } from 'vs/workbench/api/node/extHostSCM';
import { ExtHostSearch } from 'vs/workbench/api/node/extHostSearch';
import { ExtHostStatusBar } from 'vs/workbench/api/node/extHostStatusBar';
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
import { ExtHostTask } from 'vs/workbench/api/node/extHostTask';
import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService';
import { ExtHostEditors } from 'vs/workbench/api/node/extHostTextEditors';
import { ExtHostTreeViews } from 'vs/workbench/api/node/extHostTreeViews';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import { ExtHostUrls } from 'vs/workbench/api/node/extHostUrls';
import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview';
import { ExtHostComments } from './extHostComments';
import { ExtHostSearch } from './extHostSearch';
import { ExtHostUrls } from './extHostUrls';
import { localize } from 'vs/nls';
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 { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import * as vscode from 'vscode';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
}
export function checkProposedApiEnabled(extension: IExtensionDescription): void {
if (!extension.enableProposedApi) {
throwProposedApiError(extension);
}
}
function throwProposedApiError(extension: IExtensionDescription): never {
throw new Error(`[${extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.id}`);
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry): typeof vscode;
}
function proposedApiFunction<T>(extension: IExtensionDescription, fn: T): T {
if (extension.enableProposedApi) {
return fn;
} else {
return throwProposedApiError.bind(null, extension);
return throwProposedApiError.bind(null, extension) as any as T;
}
}
@@ -93,10 +85,11 @@ export function createApiFactory(
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
extensionService: ExtHostExtensionService,
extHostLogService: ExtHostLogService
extHostLogService: ExtHostLogService,
extHostStorage: ExtHostStorage
): IExtensionApiFactory {
let schemeTransformer: ISchemeTransformer = null;
let schemeTransformer: ISchemeTransformer | null = null;
// Addressable instances
rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService);
@@ -121,38 +114,41 @@ export function createApiFactory(
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService));
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 extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer, extHostLogService, extHostConfiguration));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration));
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);
// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = Object.keys(ExtHostContext).map((key) => (<any>ExtHostContext)[key]);
rpcProtocol.assertRegistered(expected);
// Other instances
const extHostClipboard = new ExtHostClipboard(rpcProtocol);
const extHostMessageService = new ExtHostMessageService(rpcProtocol);
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol);
const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments);
// Register an output channel for exthost log
extHostOutputService.createOutputChannelFromLogFile(localize('extensionsLog', "Extension Host"), extHostLogService.logFile);
const name = localize('extensionsLog', "Extension Host");
extHostOutputService.createOutputChannelFromLogFile(name, extHostLogService.logFile);
// Register API-ish commands
ExtHostApiCommands.register(extHostCommands);
return function (extension: IExtensionDescription): typeof vscode {
return function (extension: IExtensionDescription, extensionRegistry: ExtensionDescriptionRegistry): typeof vscode {
// Check document selectors for being overly generic. Technically this isn't a problem but
// in practice many extensions say they support `fooLang` but need fs-access to do so. Those
// extension should specify then the `file`-scheme, e.g `{ scheme: 'fooLang', language: 'fooLang' }`
// We only inform once, it is not a warning because we just want to raise awareness and because
// we cannot say if the extension is doing it right or wrong...
let checkSelector = (function () {
const checkSelector = (function () {
let done = (!extension.isUnderDevelopment);
function informOnce(selector: vscode.DocumentSelector) {
if (!done) {
@@ -177,6 +173,24 @@ 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.id}' 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 = {
registerCommand(id: string, command: <T>(...args: any[]) => T | Thenable<T>, thisArgs?: any): vscode.Disposable {
@@ -216,7 +230,7 @@ export function createApiFactory(
});
}),
executeCommand<T>(id: string, ...args: any[]): Thenable<T> {
return extHostCommands.executeCommand<T>(id, ...args);
return extHostCommands.executeCommand<T>(checkCommand(id), ...args);
},
getCommands(filterInternal: boolean = false): Thenable<string[]> {
return extHostCommands.getCommands(filterInternal);
@@ -232,25 +246,28 @@ export function createApiFactory(
get appRoot() { return initData.environment.appRoot.fsPath; },
get logLevel() {
checkProposedApiEnabled(extension);
return extHostLogService.getLevel();
return typeConverters.LogLevel.to(extHostLogService.getLevel());
},
get onDidChangeLogLevel() {
checkProposedApiEnabled(extension);
return extHostLogService.onDidChangeLogLevel;
return Event.map(extHostLogService.onDidChangeLogLevel, l => typeConverters.LogLevel.to(l));
},
get clipboard(): vscode.Clipboard {
return extHostClipboard;
}
});
// namespace: extensions
const extensions: typeof vscode.extensions = {
getExtension(extensionId: string): Extension<any> {
let desc = extensionService.getExtensionDescription(extensionId);
let desc = extensionRegistry.getExtensionDescription(extensionId);
if (desc) {
return new Extension(extensionService, desc);
}
return undefined;
},
get all(): Extension<any>[] {
return extensionService.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc));
return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc));
}
};
@@ -275,64 +292,70 @@ export function createApiFactory(
return score(typeConverters.LanguageSelector.from(selector), document.uri, document.languageId, true);
},
registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
return extHostLanguageFeatures.registerCodeActionProvider(checkSelector(selector), provider, extension, metadata);
return extHostLanguageFeatures.registerCodeActionProvider(extension, checkSelector(selector), provider, metadata);
},
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCodeLensProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
},
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDefinitionProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider);
},
registerDeclarationProvider(selector: vscode.DocumentSelector, provider: vscode.DeclarationProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDeclarationProvider(extension, checkSelector(selector), provider);
},
registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable {
return extHostLanguageFeatures.registerImplementationProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerImplementationProvider(extension, checkSelector(selector), provider);
},
registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable {
return extHostLanguageFeatures.registerTypeDefinitionProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerTypeDefinitionProvider(extension, checkSelector(selector), provider);
},
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable {
return extHostLanguageFeatures.registerHoverProvider(checkSelector(selector), provider, extension.id);
return extHostLanguageFeatures.registerHoverProvider(extension, checkSelector(selector), provider, extension.id);
},
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentHighlightProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider);
},
registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable {
return extHostLanguageFeatures.registerReferenceProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerReferenceProvider(extension, checkSelector(selector), provider);
},
registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
return extHostLanguageFeatures.registerRenameProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerRenameProvider(extension, checkSelector(selector), provider);
},
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentSymbolProvider(checkSelector(selector), provider, extension);
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, metadata?: vscode.DocumentSymbolProviderMetadata): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentSymbolProvider(extension, checkSelector(selector), provider, metadata);
},
registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable {
return extHostLanguageFeatures.registerWorkspaceSymbolProvider(provider);
return extHostLanguageFeatures.registerWorkspaceSymbolProvider(extension, provider);
},
registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentFormattingEditProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerDocumentFormattingEditProvider(extension, checkSelector(selector), provider);
},
registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentRangeFormattingEditProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerDocumentRangeFormattingEditProvider(extension, checkSelector(selector), provider);
},
registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacters: string[]): vscode.Disposable {
return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters));
return extHostLanguageFeatures.registerOnTypeFormattingEditProvider(extension, checkSelector(selector), provider, [firstTriggerCharacter].concat(moreTriggerCharacters));
},
registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, firstItem?: string | vscode.SignatureHelpProviderMetadata, ...remaining: string[]): vscode.Disposable {
if (typeof firstItem === 'object') {
return extHostLanguageFeatures.registerSignatureHelpProvider(checkSelector(selector), provider, firstItem);
return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, firstItem);
}
return extHostLanguageFeatures.registerSignatureHelpProvider(checkSelector(selector), provider, typeof firstItem === 'undefined' ? [] : [firstItem, ...remaining]);
return extHostLanguageFeatures.registerSignatureHelpProvider(extension, checkSelector(selector), provider, typeof firstItem === 'undefined' ? [] : [firstItem, ...remaining]);
},
registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable {
return extHostLanguageFeatures.registerCompletionItemProvider(checkSelector(selector), provider, triggerCharacters);
return extHostLanguageFeatures.registerCompletionItemProvider(extension, checkSelector(selector), provider, triggerCharacters);
},
registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentLinkProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerDocumentLinkProvider(extension, checkSelector(selector), provider);
},
registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable {
return extHostLanguageFeatures.registerColorProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerColorProvider(extension, checkSelector(selector), provider);
},
registerFoldingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable {
return extHostLanguageFeatures.registerFoldingRangeProvider(checkSelector(selector), provider);
return extHostLanguageFeatures.registerFoldingRangeProvider(extension, checkSelector(selector), provider);
},
registerSelectionRangeProvider(selector: vscode.DocumentSelector, provider: vscode.SelectionRangeProvider): vscode.Disposable {
return extHostLanguageFeatures.registerSelectionRangeProvider(extension, selector, provider);
},
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => {
return extHostLanguageFeatures.setLanguageConfiguration(language, configuration);
@@ -348,17 +371,17 @@ export function createApiFactory(
return extHostEditors.getVisibleTextEditors();
},
get activeTerminal() {
return proposedApiFunction(extension, extHostTerminalService.activeTerminal);
return extHostTerminalService.activeTerminal;
},
get terminals() {
return extHostTerminalService.terminals;
},
showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Thenable<vscode.TextEditor> {
let documentPromise: TPromise<vscode.TextDocument>;
let documentPromise: Promise<vscode.TextDocument>;
if (URI.isUri(documentOrUri)) {
documentPromise = TPromise.wrap(workspace.openTextDocument(documentOrUri));
documentPromise = Promise.resolve(workspace.openTextDocument(documentOrUri));
} else {
documentPromise = TPromise.wrap(<vscode.TextDocument>documentOrUri);
documentPromise = Promise.resolve(<vscode.TextDocument>documentOrUri);
}
return documentPromise.then(document => {
return extHostEditors.showTextDocument(document, columnOrOptions, preserveFocus);
@@ -391,9 +414,9 @@ export function createApiFactory(
onDidOpenTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidOpenTerminal(listener, thisArg, disposables);
},
onDidChangeActiveTerminal: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
onDidChangeActiveTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidChangeActiveTerminal(listener, thisArg, disposables);
}),
},
get state() {
return extHostWindow.state;
},
@@ -441,7 +464,7 @@ 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.extensionLocation, viewType, title, showOptions, options);
return extHostWebviews.createWebview(extension, viewType, title, showOptions, options);
},
createTerminal(nameOrOptions: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
if (typeof nameOrOptions === 'object') {
@@ -453,18 +476,14 @@ export function createApiFactory(
return extHostTerminalService.createTerminalRenderer(name);
}),
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider);
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension);
},
createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider<any> }): vscode.TreeView<any> {
return extHostTreeViews.createTreeView(viewId, options);
return extHostTreeViews.createTreeView(viewId, options, extension);
},
registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer);
},
// proposed API
sampleFunction: proposedApiFunction(extension, () => {
return extHostMessageService.showMessage(extension, Severity.Info, 'Hello Proposed Api!', {}, []);
}),
registerDecorationProvider: proposedApiFunction(extension, (provider: vscode.DecorationProvider) => {
return extHostDecorations.registerDecorationProvider(provider, extension.id);
}),
@@ -546,9 +565,9 @@ export function createApiFactory(
let options = uriOrFileNameOrOptions as { language?: string; content?: string; };
if (typeof uriOrFileNameOrOptions === 'string') {
uriPromise = TPromise.as(URI.file(uriOrFileNameOrOptions));
uriPromise = Promise.resolve(URI.file(uriOrFileNameOrOptions));
} else if (uriOrFileNameOrOptions instanceof URI) {
uriPromise = TPromise.as(uriOrFileNameOrOptions);
uriPromise = Promise.resolve(uriOrFileNameOrOptions);
} else if (!options || typeof options === 'object') {
uriPromise = extHostDocuments.createDocumentData(options);
} else {
@@ -610,7 +629,7 @@ export function createApiFactory(
return exthostCommentProviders.registerDocumentCommentProvider(provider);
}),
registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => {
return exthostCommentProviders.registerWorkspaceCommentProvider(provider);
return exthostCommentProviders.registerWorkspaceCommentProvider(extension.id, provider);
}),
onDidRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables);
@@ -657,7 +676,13 @@ export function createApiFactory(
return extHostDebugService.onDidChangeBreakpoints(listener, thisArgs, disposables);
},
registerDebugConfigurationProvider(debugType: string, provider: vscode.DebugConfigurationProvider) {
return extHostDebugService.registerDebugConfigurationProvider(extension, debugType, provider);
return extHostDebugService.registerDebugConfigurationProvider(debugType, provider);
},
registerDebugAdapterDescriptorFactory(debugType: string, factory: vscode.DebugAdapterDescriptorFactory) {
return extHostDebugService.registerDebugAdapterDescriptorFactory(extension, debugType, factory);
},
registerDebugAdapterTrackerFactory(debugType: string, factory: vscode.DebugAdapterTrackerFactory) {
return extHostDebugService.registerDebugAdapterTrackerFactory(debugType, factory);
},
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration) {
return extHostDebugService.startDebugging(folder, nameOrConfig);
@@ -727,7 +752,6 @@ export function createApiFactory(
ConfigurationTarget: extHostTypes.ConfigurationTarget,
DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable,
DebugAdapterServer: extHostTypes.DebugAdapterServer,
DebugAdapterImplementation: extHostTypes.DebugAdapterImplementation,
DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior,
Diagnostic: extHostTypes.Diagnostic,
DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation,
@@ -762,7 +786,7 @@ export function createApiFactory(
Selection: extHostTypes.Selection,
ShellExecution: extHostTypes.ShellExecution,
ShellQuoting: extHostTypes.ShellQuoting,
SignatureHelpTriggerReason: extHostTypes.SignatureHelpTriggerReason,
SignatureHelpTriggerKind: extHostTypes.SignatureHelpTriggerKind,
SignatureHelp: extHostTypes.SignatureHelp,
SignatureInformation: extHostTypes.SignatureInformation,
SnippetString: extHostTypes.SnippetString,
@@ -772,6 +796,7 @@ export function createApiFactory(
SymbolInformation: extHostTypes.SymbolInformation,
SymbolKind: extHostTypes.SymbolKind,
Task: extHostTypes.Task,
Task2: extHostTypes.Task,
TaskGroup: extHostTypes.TaskGroup,
TaskPanelKind: extHostTypes.TaskPanelKind,
TaskRevealKind: extHostTypes.TaskRevealKind,
@@ -785,6 +810,7 @@ export function createApiFactory(
ThemeColor: extHostTypes.ThemeColor,
ThemeIcon: extHostTypes.ThemeIcon,
TreeItem: extHostTypes.TreeItem,
TreeItem2: extHostTypes.TreeItem,
TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState,
Uri: URI,
ViewColumn: extHostTypes.ViewColumn,
@@ -834,11 +860,11 @@ class Extension<T> implements vscode.Extension<T> {
}
}
export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): TPromise<void> {
return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie));
export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory, extensionRegistry: ExtensionDescriptionRegistry): Promise<void> {
return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie, extensionRegistry));
}
function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchTree<IExtensionDescription>): void {
function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchTree<IExtensionDescription>, extensionRegistry: ExtensionDescriptionRegistry): void {
// each extension is meant to get its own api implementation
const extApiImpl = new Map<string, typeof vscode>();
@@ -856,7 +882,7 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT
if (ext) {
let apiImpl = extApiImpl.get(ext.id);
if (!apiImpl) {
apiImpl = factory(ext);
apiImpl = factory(ext, extensionRegistry);
extApiImpl.set(ext.id, apiImpl);
}
return apiImpl;
@@ -867,24 +893,8 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT
let extensionPathsPretty = '';
extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.id}\n`);
console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
defaultApiImpl = factory(nullExtensionDescription);
defaultApiImpl = factory(nullExtensionDescription, extensionRegistry);
}
return defaultApiImpl;
};
}
const nullExtensionDescription: IExtensionDescription = {
id: 'nullExtensionDescription',
name: 'Null Extension Description',
publisher: 'vscode',
activationEvents: undefined,
contributes: undefined,
enableProposedApi: false,
engines: undefined,
extensionDependencies: undefined,
extensionLocation: undefined,
isBuiltin: false,
isUnderDevelopment: false,
main: undefined,
version: undefined
};

View File

@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationToken } from 'vs/base/common/cancellation';
import { SerializedError } from 'vs/base/common/errors';
import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
@@ -24,23 +25,24 @@ import { LabelRules } 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, IQueryOptions, IRawFileMatch2, IRawSearchQuery, ISearchCompleteStats } from 'vs/platform/search/common/search';
import { IPatternInfo, IRawFileMatch2, IRawQuery, IRawTextQuery, ISearchCompleteStats } from 'vs/platform/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';
import { EndOfLine, IFileOperationOptions, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { TaskDTO, TaskExecutionDTO, TaskFilterDTO, TaskHandleDTO, TaskProcessEndedDTO, TaskProcessStartedDTO, TaskSystemInfoDTO } from 'vs/workbench/api/shared/tasks';
import { ITreeItem } from 'vs/workbench/common/views';
import { IConfig, ITerminalSettings, IAdapterDescriptor } from 'vs/workbench/parts/debug/common/debug';
import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
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 { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol, ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { IProgressOptions, IProgressStep } from 'vs/workbench/services/progress/common/progress';
import { IRPCProtocol, ProxyIdentifier, 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 { CancellationToken } from 'vs/base/common/cancellation';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
@@ -48,6 +50,7 @@ export interface IEnvironment {
appSettingsHome: URI;
extensionDevelopmentLocationURI: URI;
extensionTestsPath: string;
globalStorageHome: URI;
}
export interface IWorkspaceData {
@@ -67,6 +70,8 @@ export interface IInitData {
telemetryInfo: ITelemetryInfo;
logLevel: LogLevel;
logsLocation: URI;
autoStart: boolean;
remoteAuthority?: string | null;
}
export interface IConfigurationInitData extends IConfigurationData {
@@ -79,6 +84,7 @@ export interface IWorkspaceConfigurationChangeEventData {
}
export interface IExtHostContext extends IRPCProtocol {
remoteAuthority: string;
}
export interface IMainContext extends IRPCProtocol {
@@ -86,24 +92,35 @@ export interface IMainContext extends IRPCProtocol {
// --- main thread
export interface MainThreadClipboardShape extends IDisposable {
$readText(): Promise<string>;
$writeText(value: string): Promise<void>;
}
export interface MainThreadCommandsShape extends IDisposable {
$registerCommand(id: string): void;
$unregisterCommand(id: string): void;
$executeCommand<T>(id: string, args: any[]): Thenable<T>;
$getCommands(): Thenable<string[]>;
$executeCommand<T>(id: string, args: any[]): Promise<T>;
$getCommands(): Promise<string[]>;
}
export interface CommentProviderFeatures {
startDraftLabel?: string;
deleteDraftLabel?: string;
finishDraftLabel?: string;
}
export interface MainThreadCommentsShape extends IDisposable {
$registerDocumentCommentProvider(handle: number): void;
$registerDocumentCommentProvider(handle: number, features: CommentProviderFeatures): void;
$unregisterDocumentCommentProvider(handle: number): void;
$registerWorkspaceCommentProvider(handle: number): void;
$registerWorkspaceCommentProvider(handle: number, extensionId: string): void;
$unregisterWorkspaceCommentProvider(handle: number): void;
$onDidCommentThreadsChange(handle: number, event: modes.CommentThreadChangedEvent): void;
}
export interface MainThreadConfigurationShape extends IDisposable {
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: UriComponents): Thenable<void>;
$removeConfigurationOption(target: ConfigurationTarget, key: string, resource: UriComponents): Thenable<void>;
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: UriComponents): Promise<void>;
$removeConfigurationOption(target: ConfigurationTarget, key: string, resource: UriComponents): Promise<void>;
}
export interface MainThreadDiagnosticsShape extends IDisposable {
@@ -127,8 +144,8 @@ export interface MainThreadDialogSaveOptions {
}
export interface MainThreadDiaglogsShape extends IDisposable {
$showOpenDialog(options: MainThreadDialogOpenOptions): Thenable<UriComponents[]>;
$showSaveDialog(options: MainThreadDialogSaveOptions): Thenable<UriComponents>;
$showOpenDialog(options: MainThreadDialogOpenOptions): Promise<UriComponents[]>;
$showSaveDialog(options: MainThreadDialogSaveOptions): Promise<UriComponents>;
}
export interface MainThreadDecorationsShape extends IDisposable {
@@ -144,9 +161,9 @@ export interface MainThreadDocumentContentProvidersShape extends IDisposable {
}
export interface MainThreadDocumentsShape extends IDisposable {
$tryCreateDocument(options?: { language?: string; content?: string; }): Thenable<UriComponents>;
$tryOpenDocument(uri: UriComponents): Thenable<void>;
$trySaveDocument(uri: UriComponents): Thenable<boolean>;
$tryCreateDocument(options?: { language?: string; content?: string; }): Promise<UriComponents>;
$tryOpenDocument(uri: UriComponents): Promise<void>;
$trySaveDocument(uri: UriComponents): Promise<boolean>;
}
export interface ITextEditorConfigurationUpdate {
@@ -187,26 +204,27 @@ export interface ITextDocumentShowOptions {
}
export interface MainThreadTextEditorsShape extends IDisposable {
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Thenable<string>;
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Promise<string>;
$registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void;
$removeTextEditorDecorationType(key: string): void;
$tryShowEditor(id: string, position: EditorViewColumn): Thenable<void>;
$tryHideEditor(id: string): Thenable<void>;
$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): Thenable<void>;
$trySetDecorations(id: string, key: string, ranges: editorCommon.IDecorationOptions[]): Thenable<void>;
$trySetDecorationsFast(id: string, key: string, ranges: number[]): Thenable<void>;
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): Thenable<void>;
$trySetSelections(id: string, selections: ISelection[]): Thenable<void>;
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): Thenable<boolean>;
$tryApplyWorkspaceEdit(workspaceEditDto: WorkspaceEditDto): Thenable<boolean>;
$tryInsertSnippet(id: string, template: string, selections: IRange[], opts: IUndoStopOptions): Thenable<boolean>;
$getDiffInformation(id: string): Thenable<editorCommon.ILineChange[]>;
$tryShowEditor(id: string, position: EditorViewColumn): Promise<void>;
$tryHideEditor(id: string): Promise<void>;
$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): Promise<void>;
$trySetDecorations(id: string, key: string, ranges: editorCommon.IDecorationOptions[]): Promise<void>;
$trySetDecorationsFast(id: string, key: string, ranges: number[]): Promise<void>;
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): Promise<void>;
$trySetSelections(id: string, selections: ISelection[]): Promise<void>;
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): Promise<boolean>;
$tryApplyWorkspaceEdit(workspaceEditDto: WorkspaceEditDto): Promise<boolean>;
$tryInsertSnippet(id: string, template: string, selections: IRange[], opts: IUndoStopOptions): Promise<boolean>;
$getDiffInformation(id: string): Promise<editorCommon.ILineChange[]>;
}
export interface MainThreadTreeViewsShape extends IDisposable {
$registerTreeViewDataProvider(treeViewId: string): void;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Thenable<void>;
$reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: { select: boolean, focus: boolean }): Thenable<void>;
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean }): void;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise<void>;
$reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise<void>;
$setMessage(treeViewId: string, message: string | IMarkdownString): void;
}
export interface MainThreadErrorsShape extends IDisposable {
@@ -268,10 +286,11 @@ export interface ISerializedSignatureHelpProviderMetadata {
export interface MainThreadLanguageFeaturesShape extends IDisposable {
$unregister(handle: number): void;
$registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[], extensionId: string): void;
$registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[], label: string): void;
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void;
$emitCodeLensEvent(eventHandle: number, event?: any): void;
$registerDeclaractionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerTypeDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerHoverProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
@@ -288,12 +307,13 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$registerDocumentLinkProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDocumentColorProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerFoldingRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerSelectionRangeProvider(handle: number, selector: ISerializedDocumentFilter[]): void;
$setLanguageConfiguration(handle: number, languageId: string, configuration: ISerializedLanguageConfiguration): void;
}
export interface MainThreadLanguagesShape extends IDisposable {
$getLanguages(): Thenable<string[]>;
$changeLanguage(resource: UriComponents, languageId: string): Thenable<void>;
$getLanguages(): Promise<string[]>;
$changeLanguage(resource: UriComponents, languageId: string): Promise<void>;
}
export interface MainThreadMessageOptions {
@@ -302,17 +322,17 @@ export interface MainThreadMessageOptions {
}
export interface MainThreadMessageServiceShape extends IDisposable {
$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable<number>;
$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Promise<number>;
}
export interface MainThreadOutputServiceShape extends IDisposable {
$register(label: string, log: boolean, file?: UriComponents): Thenable<string>;
$append(channelId: string, value: string): Thenable<void>;
$update(channelId: string): Thenable<void>;
$clear(channelId: string, till: number): Thenable<void>;
$reveal(channelId: string, preserveFocus: boolean): Thenable<void>;
$close(channelId: string): Thenable<void>;
$dispose(channelId: string): Thenable<void>;
$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>;
}
export interface MainThreadProgressShape extends IDisposable {
@@ -323,8 +343,8 @@ export interface MainThreadProgressShape extends IDisposable {
}
export interface MainThreadTerminalServiceShape extends IDisposable {
$createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean): Thenable<number>;
$createTerminalRenderer(name: string): Thenable<number>;
$createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean): Promise<{ id: number, name: string }>;
$createTerminalRenderer(name: string): Promise<number>;
$dispose(terminalId: number): void;
$hide(terminalId: number): void;
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
@@ -410,12 +430,12 @@ export interface TransferInputBox extends BaseTransferQuickInput {
}
export interface MainThreadQuickOpenShape extends IDisposable {
$show(options: IPickOptions<TransferQuickPickItems>, token: CancellationToken): Thenable<number | number[]>;
$setItems(items: TransferQuickPickItems[]): Thenable<void>;
$setError(error: Error): Thenable<void>;
$input(options: vscode.InputBoxOptions, validateInput: boolean, token: CancellationToken): Thenable<string>;
$createOrUpdate(params: TransferQuickInput): Thenable<void>;
$dispose(id: number): Thenable<void>;
$show(instance: number, options: IPickOptions<TransferQuickPickItems>, token: CancellationToken): Promise<number | number[]>;
$setItems(instance: number, items: TransferQuickPickItems[]): Promise<void>;
$setError(instance: number, error: Error): Promise<void>;
$input(options: vscode.InputBoxOptions, validateInput: boolean, token: CancellationToken): Promise<string>;
$createOrUpdate(params: TransferQuickInput): Promise<void>;
$dispose(id: number): Promise<void>;
}
export interface MainThreadStatusBarShape extends IDisposable {
@@ -424,8 +444,8 @@ export interface MainThreadStatusBarShape extends IDisposable {
}
export interface MainThreadStorageShape extends IDisposable {
$getValue<T>(shared: boolean, key: string): Thenable<T>;
$setValue(shared: boolean, key: string, value: any): Thenable<void>;
$getValue<T>(shared: boolean, key: string): Promise<T>;
$setValue(shared: boolean, key: string, value: object): Promise<void>;
}
export interface MainThreadTelemetryShape extends IDisposable {
@@ -440,14 +460,14 @@ export interface WebviewPanelShowOptions {
}
export interface MainThreadWebviewsShape extends IDisposable {
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionLocation: UriComponents): void;
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionId: string, extensionLocation: UriComponents): 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): Thenable<boolean>;
$postMessage(handle: WebviewPanelHandle, value: any): Promise<boolean>;
$registerSerializer(viewType: string): void;
$unregisterSerializer(viewType: string): void;
@@ -462,25 +482,26 @@ export interface WebviewPanelViewState {
export interface ExtHostWebviewsShape {
$onMessage(handle: WebviewPanelHandle, message: any): void;
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, newState: WebviewPanelViewState): void;
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Thenable<void>;
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: vscode.WebviewOptions): Thenable<void>;
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void>;
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: vscode.WebviewOptions): Promise<void>;
}
export interface MainThreadUrlsShape extends IDisposable {
$registerUriHandler(handle: number, extensionId: string): Thenable<void>;
$unregisterUriHandler(handle: number): Thenable<void>;
$registerUriHandler(handle: number, extensionId: string): Promise<void>;
$unregisterUriHandler(handle: number): Promise<void>;
}
export interface ExtHostUrlsShape {
$handleExternalUri(handle: number, uri: UriComponents): Thenable<void>;
$handleExternalUri(handle: number, uri: UriComponents): Promise<void>;
}
export interface MainThreadWorkspaceShape extends IDisposable {
$startFileSearch(includePattern: string, includeFolder: URI, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Thenable<UriComponents[]>;
$startTextSearch(query: IPatternInfo, options: IQueryOptions, requestId: number, token: CancellationToken): Thenable<vscode.TextSearchComplete>;
$checkExists(includes: string[], token: CancellationToken): Thenable<boolean>;
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string }[]): Thenable<void>;
$startFileSearch(includePattern: string, includeFolder: URI, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise<UriComponents[]>;
$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>;
}
export interface IFileChangeDto {
@@ -506,17 +527,18 @@ export interface MainThreadSearchShape extends IDisposable {
}
export interface MainThreadTaskShape extends IDisposable {
$registerTaskProvider(handle: number): Thenable<void>;
$unregisterTaskProvider(handle: number): Thenable<void>;
$fetchTasks(filter?: TaskFilterDTO): Thenable<TaskDTO[]>;
$executeTask(task: TaskHandleDTO | TaskDTO): Thenable<TaskExecutionDTO>;
$terminateTask(id: string): Thenable<void>;
$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;
}
export interface MainThreadExtensionServiceShape extends IDisposable {
$localShowMessage(severity: Severity, msg: string): void;
$onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void;
$onWillActivateExtension(extensionId: string): void;
$onDidActivateExtension(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void;
$onExtensionActivationFailed(extensionId: string): void;
$onExtensionRuntimeError(extensionId: string, error: SerializedError): void;
$addMessage(extensionId: string, severity: Severity, message: string): void;
@@ -572,6 +594,7 @@ export interface MainThreadSCMShape extends IDisposable {
$setInputBoxValue(sourceControlHandle: number, value: string): void;
$setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void;
$setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void;
$setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void;
}
@@ -582,25 +605,29 @@ export interface MainThreadDebugServiceShape extends IDisposable {
$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;
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasProvideDaMethod: boolean, hasProvideTrackerMethod: boolean, handle: number): Thenable<void>;
$unregisterDebugConfigurationProvider(handle: number): Thenable<void>;
$startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration): Thenable<boolean>;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Thenable<any>;
$appendDebugConsole(value: string): Thenable<void>;
$startBreakpointEvents(): Thenable<void>;
$registerBreakpoints(breakpoints: (ISourceMultiBreakpointDto | IFunctionBreakpointDto)[]): Thenable<void>;
$unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Thenable<void>;
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasProvideDaMethod: boolean, hasProvideTrackerMethod: 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>;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise<any>;
$appendDebugConsole(value: string): void;
$startBreakpointEvents(): void;
$registerBreakpoints(breakpoints: (ISourceMultiBreakpointDto | IFunctionBreakpointDto)[]): Promise<void>;
$unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): Promise<void>;
}
export interface MainThreadWindowShape extends IDisposable {
$getWindowVisibility(): Thenable<boolean>;
$getWindowVisibility(): Promise<boolean>;
}
// -- extension host
export interface ExtHostCommandsShape {
$executeContributedCommand<T>(id: string, ...args: any[]): Thenable<T>;
$getContributedCommandHandlerDescriptions(): Thenable<{ [id: string]: string | ICommandHandlerDescription }>;
$executeContributedCommand<T>(id: string, ...args: any[]): Promise<T>;
$getContributedCommandHandlerDescriptions(): Promise<{ [id: string]: string | ICommandHandlerDescription }>;
}
export interface ExtHostConfigurationShape {
@@ -631,7 +658,7 @@ export interface ExtHostDocumentsShape {
}
export interface ExtHostDocumentSaveParticipantShape {
$participateInSave(resource: UriComponents, reason: SaveReason): Thenable<boolean[]>;
$participateInSave(resource: UriComponents, reason: SaveReason): Promise<boolean[]>;
}
export interface ITextEditorAddData {
@@ -673,7 +700,7 @@ export interface ExtHostDocumentsAndEditorsShape {
}
export interface ExtHostTreeViewsShape {
$getChildren(treeViewId: string, treeItemHandle?: string): Thenable<ITreeItem[]>;
$getChildren(treeViewId: string, treeItemHandle?: string): Promise<ITreeItem[]>;
$setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void;
$setSelection(treeViewId: string, treeItemHandles: string[]): void;
$setVisible(treeViewId: string, visible: boolean): void;
@@ -685,30 +712,32 @@ export interface ExtHostWorkspaceShape {
}
export interface ExtHostFileSystemShape {
$stat(handle: number, resource: UriComponents): Thenable<IStat>;
$readdir(handle: number, resource: UriComponents): Thenable<[string, FileType][]>;
$readFile(handle: number, resource: UriComponents): Thenable<Buffer>;
$writeFile(handle: number, resource: UriComponents, content: Buffer, opts: FileWriteOptions): Thenable<void>;
$rename(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Thenable<void>;
$copy(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Thenable<void>;
$mkdir(handle: number, resource: UriComponents): Thenable<void>;
$delete(handle: number, resource: UriComponents, opts: FileDeleteOptions): Thenable<void>;
$stat(handle: number, resource: UriComponents): Promise<IStat>;
$readdir(handle: number, resource: UriComponents): Promise<[string, FileType][]>;
$readFile(handle: number, resource: UriComponents): Promise<Buffer>;
$writeFile(handle: number, resource: UriComponents, content: Buffer, opts: FileWriteOptions): Promise<void>;
$rename(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void>;
$copy(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void>;
$mkdir(handle: number, resource: UriComponents): Promise<void>;
$delete(handle: number, resource: UriComponents, opts: FileDeleteOptions): Promise<void>;
$watch(handle: number, session: number, resource: UriComponents, opts: IWatchOptions): void;
$unwatch(handle: number, session: number): void;
$open(handle: number, resource: UriComponents): Thenable<number>;
$close(handle: number, fd: number): Thenable<void>;
$read(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Thenable<number>;
$write(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Thenable<number>;
$open(handle: number, resource: UriComponents): Promise<number>;
$close(handle: number, fd: number): Promise<void>;
$read(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Promise<number>;
$write(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Promise<number>;
}
export interface ExtHostSearchShape {
$provideFileSearchResults(handle: number, session: number, query: IRawSearchQuery, token: CancellationToken): Thenable<ISearchCompleteStats>;
$clearCache(cacheKey: string): Thenable<void>;
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, query: IRawSearchQuery, token: CancellationToken): Thenable<ISearchCompleteStats>;
$provideFileSearchResults(handle: number, session: number, query: IRawQuery, token: CancellationToken): Promise<ISearchCompleteStats>;
$provideTextSearchResults(handle: number, session: number, query: IRawTextQuery, token: CancellationToken): Promise<ISearchCompleteStats>;
$clearCache(cacheKey: string): Promise<void>;
}
export interface ExtHostExtensionServiceShape {
$activateByEvent(activationEvent: string): Thenable<void>;
$resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority>;
$startExtensionHost(enabledExtensionIds: string[]): Promise<void>;
$activateByEvent(activationEvent: string): Promise<void>;
}
export interface FileSystemEvents {
@@ -719,7 +748,7 @@ export interface FileSystemEvents {
export interface ExtHostFileSystemEventServiceShape {
$onFileEvent(events: FileSystemEvents): void;
$onFileRename(oldUri: UriComponents, newUri: UriComponents): void;
$onWillRename(oldUri: UriComponents, newUri: UriComponents): Thenable<any>;
$onWillRename(oldUri: UriComponents, newUri: UriComponents): Promise<any>;
}
export interface ObjectIdentifier {
@@ -829,38 +858,40 @@ export interface CodeActionDto {
}
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Thenable<modes.DocumentSymbol[]>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Thenable<modes.ICodeLensSymbol[]>;
$resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol, token: CancellationToken): Thenable<modes.ICodeLensSymbol>;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<DefinitionLinkDto[]>;
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<DefinitionLinkDto[]>;
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<DefinitionLinkDto[]>;
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.Hover>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.DocumentHighlight[]>;
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Thenable<LocationDto[]>;
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Thenable<CodeActionDto[]>;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]>;
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]>;
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]>;
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Thenable<WorkspaceSymbolsDto>;
$resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto, token: CancellationToken): Thenable<WorkspaceSymbolDto>;
$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>;
$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[]>;
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise<WorkspaceSymbolsDto>;
$resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto, token: CancellationToken): Promise<WorkspaceSymbolDto>;
$releaseWorkspaceSymbols(handle: number, id: number): void;
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Thenable<WorkspaceEditDto>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.RenameLocation>;
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Thenable<SuggestResultDto>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.CompletionItem, token: CancellationToken): Thenable<modes.CompletionItem>;
$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>;
$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): Thenable<modes.SignatureHelp>;
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Thenable<modes.ILink[]>;
$resolveDocumentLink(handle: number, link: modes.ILink, token: CancellationToken): Thenable<modes.ILink>;
$provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): Thenable<IRawColorInfo[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo, token: CancellationToken): Thenable<modes.IColorPresentation[]>;
$provideFoldingRanges(handle: number, resource: UriComponents, context: modes.FoldingContext, token: CancellationToken): Thenable<modes.FoldingRange[]>;
$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>;
$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<IRange[]>;
}
export interface ExtHostQuickOpenShape {
$onItemSelected(handle: number): void;
$validateInput(input: string): Thenable<string>;
$validateInput(input: string): Promise<string>;
$onDidChangeActive(sessionId: number, handles: number[]): void;
$onDidChangeSelection(sessionId: number, handles: number[]): void;
$onDidAccept(sessionId: number): void;
@@ -887,27 +918,27 @@ export interface ExtHostTerminalServiceShape {
$acceptTerminalProcessLaunching(id: number): void;
$acceptTerminalTitleChange(id: number, name: string): void;
$acceptTerminalRendererDimensions(id: number, cols: number, rows: number): void;
$createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, cols: number, rows: number): void;
$createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUri: URI, 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;
}
export interface ExtHostSCMShape {
$provideOriginalResource(sourceControlHandle: number, uri: UriComponents, token: CancellationToken): Thenable<UriComponents>;
$provideOriginalResource(sourceControlHandle: number, uri: UriComponents, token: CancellationToken): Promise<UriComponents>;
$onInputBoxValueChange(sourceControlHandle: number, value: string): void;
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): Thenable<void>;
$validateInput(sourceControlHandle: number, value: string, cursorPosition: number): Thenable<[string, number] | undefined>;
$setSelectedSourceControls(selectedSourceControlHandles: number[]): Thenable<void>;
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): Promise<void>;
$validateInput(sourceControlHandle: number, value: string, cursorPosition: number): Promise<[string, number] | undefined>;
$setSelectedSourceControls(selectedSourceControlHandles: number[]): Promise<void>;
}
export interface ExtHostTaskShape {
$provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<TaskSet>;
$provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<TaskSetDTO>;
$onDidStartTask(execution: TaskExecutionDTO): void;
$onDidStartTaskProcess(value: TaskProcessStartedDTO): void;
$onDidEndTaskProcess(value: TaskProcessEndedDTO): void;
$OnDidEndTask(execution: TaskExecutionDTO): void;
$resolveVariables(workspaceFolder: UriComponents, variables: string[]): Thenable<any>;
$resolveVariables(workspaceFolder: UriComponents, toResolve: { process?: { name: string; cwd?: string }, variables: string[] }): Promise<{ process?: string; variables: { [key: string]: string } }>;
}
export interface IBreakpointDto {
@@ -951,21 +982,26 @@ export interface ISourceMultiBreakpointDto {
}[];
}
export interface IDebugSessionDto {
export interface IDebugSessionFullDto {
id: DebugSessionUUID;
type: string;
name: string;
folderUri: UriComponents | undefined;
configuration: IConfig;
}
export type IDebugSessionDto = IDebugSessionFullDto | DebugSessionUUID;
export interface ExtHostDebugServiceShape {
$substituteVariables(folder: UriComponents | undefined, config: IConfig): Thenable<IConfig>;
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable<void>;
$startDASession(handle: number, session: IDebugSessionDto, folder: UriComponents | undefined, debugConfiguration: IConfig): Thenable<void>;
$stopDASession(handle: number): Thenable<void>;
$substituteVariables(folder: UriComponents | undefined, config: IConfig): Promise<IConfig>;
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise<number | undefined>;
$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): Thenable<IConfig>;
$provideDebugConfigurations(handle: number, folder: UriComponents | undefined): Thenable<IConfig[]>;
$provideDebugAdapter(handle: number, session: IDebugSessionDto, folderUri: UriComponents | undefined, debugConfiguration: IConfig): Thenable<IAdapterDescriptor>;
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): Promise<IConfig>;
$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;
@@ -984,7 +1020,7 @@ export type DecorationData = [number, boolean, string, string, ThemeColor, strin
export type DecorationReply = { [id: number]: DecorationData };
export interface ExtHostDecorationsShape {
$provideDecorations(requests: DecorationRequest[], token: CancellationToken): Thenable<DecorationReply>;
$provideDecorations(requests: DecorationRequest[], token: CancellationToken): Promise<DecorationReply>;
}
export interface ExtHostWindowShape {
@@ -1004,17 +1040,25 @@ export interface ExtHostProgressShape {
}
export interface ExtHostCommentsShape {
$provideDocumentComments(handle: number, document: UriComponents): Thenable<modes.CommentInfo>;
$createNewCommentThread(handle: number, document: UriComponents, range: IRange, text: string): Thenable<modes.CommentThread>;
$replyToCommentThread(handle: number, document: UriComponents, range: IRange, commentThread: modes.CommentThread, text: string): Thenable<modes.CommentThread>;
$editComment(handle: number, document: UriComponents, comment: modes.Comment, text: string): Thenable<void>;
$deleteComment(handle: number, document: UriComponents, comment: modes.Comment): Thenable<void>;
$provideWorkspaceComments(handle: number): Thenable<modes.CommentThread[]>;
$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>;
$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): Promise<void>;
$deleteDraft(handle: number): Promise<void>;
$finishDraft(handle: number): Promise<void>;
$provideWorkspaceComments(handle: number): Promise<modes.CommentThread[]>;
}
export interface ExtHostStorageShape {
$acceptValue(shared: boolean, key: string, value: object): void;
}
// --- proxy identifiers
export const MainContext = {
MainThreadClipboard: <ProxyIdentifier<MainThreadClipboardShape>>createMainId<MainThreadClipboardShape>('MainThreadClipboard'),
MainThreadCommands: <ProxyIdentifier<MainThreadCommandsShape>>createMainId<MainThreadCommandsShape>('MainThreadCommands'),
MainThreadComments: createMainId<MainThreadCommentsShape>('MainThreadComments'),
MainThreadConfiguration: createMainId<MainThreadConfigurationShape>('MainThreadConfiguration'),
@@ -1076,6 +1120,7 @@ export const ExtHostContext = {
ExtHostWebviews: createExtId<ExtHostWebviewsShape>('ExtHostWebviews'),
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress'),
ExtHostComments: createMainId<ExtHostCommentsShape>('ExtHostComments'),
ExtHostStorage: createMainId<ExtHostStorageShape>('ExtHostStorage'),
ExtHostUrls: createExtId<ExtHostUrlsShape>('ExtHostUrls'),
ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
};

View File

@@ -17,7 +17,8 @@ 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 } from 'vs/base/common/arrays';
import { isFalsyOrEmpty, isNonEmptyArray } from 'vs/base/common/arrays';
import { IRange } from 'vs/editor/common/core/range';
export class ExtHostApiCommands {
@@ -47,6 +48,14 @@ export class ExtHostApiCommands {
],
returns: 'A promise that resolves to an array of Location-instances.'
});
this._register('vscode.executeDeclarationProvider', this._executeDeclaraionProvider, {
description: 'Execute all declaration provider.',
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
{ name: 'position', description: 'Position of a symbol', constraint: types.Position }
],
returns: 'A promise that resolves to an array of Location-instances.'
});
this._register('vscode.executeTypeDefinitionProvider', this._executeTypeDefinitionProvider, {
description: 'Execute all type definition providers.',
args: [
@@ -187,7 +196,14 @@ export class ExtHostApiCommands {
],
returns: 'A promise that resolves to an array of ColorPresentation objects.'
});
this._register('vscode.executeSelectionRangeProvider', this._executeSelectionRangeProvider, {
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 }
],
returns: 'A promise that resolves to an array of ranges.'
});
// -----------------------------------------------------------------
// The following commands are registered on both sides separately.
@@ -271,7 +287,7 @@ export class ExtHostApiCommands {
* @param query Search string to match query symbol names
* @return A promise that resolves to an array of symbol information.
*/
private _executeWorkspaceSymbolProvider(query: string): Thenable<types.SymbolInformation[]> {
private _executeWorkspaceSymbolProvider(query: string): Promise<types.SymbolInformation[]> {
return this._commands.executeCommand<[search.IWorkspaceSymbolProvider, search.IWorkspaceSymbol[]][]>('_executeWorkspaceSymbolProvider', { query }).then(value => {
const result: types.SymbolInformation[] = [];
if (Array.isArray(value)) {
@@ -283,7 +299,7 @@ export class ExtHostApiCommands {
});
}
private _executeDefinitionProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
private _executeDefinitionProvider(resource: URI, position: types.Position): Promise<types.Location[]> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -292,7 +308,16 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.location.to));
}
private _executeTypeDefinitionProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
private _executeDeclaraionProvider(resource: URI, position: types.Position): Promise<types.Location[]> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
};
return this._commands.executeCommand<modes.Location[]>('_executeDeclarationProvider', args)
.then(tryMapWith(typeConverters.location.to));
}
private _executeTypeDefinitionProvider(resource: URI, position: types.Position): Promise<types.Location[]> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -301,7 +326,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.location.to));
}
private _executeImplementationProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
private _executeImplementationProvider(resource: URI, position: types.Position): Promise<types.Location[]> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -310,7 +335,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.location.to));
}
private _executeHoverProvider(resource: URI, position: types.Position): Thenable<types.Hover[]> {
private _executeHoverProvider(resource: URI, position: types.Position): Promise<types.Hover[]> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -319,7 +344,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.Hover.to));
}
private _executeDocumentHighlights(resource: URI, position: types.Position): Thenable<types.DocumentHighlight[]> {
private _executeDocumentHighlights(resource: URI, position: types.Position): Promise<types.DocumentHighlight[]> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -328,7 +353,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.DocumentHighlight.to));
}
private _executeReferenceProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
private _executeReferenceProvider(resource: URI, position: types.Position): Promise<types.Location[]> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
@@ -337,7 +362,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(typeConverters.location.to));
}
private _executeDocumentRenameProvider(resource: URI, position: types.Position, newName: string): Thenable<types.WorkspaceEdit> {
private _executeDocumentRenameProvider(resource: URI, position: types.Position, newName: string): Promise<types.WorkspaceEdit> {
const args = {
resource,
position: position && typeConverters.Position.from(position),
@@ -354,7 +379,7 @@ export class ExtHostApiCommands {
});
}
private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Thenable<types.SignatureHelp> {
private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Promise<types.SignatureHelp> {
const args = {
resource,
position: position && typeConverters.Position.from(position),
@@ -368,7 +393,7 @@ export class ExtHostApiCommands {
});
}
private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string, maxItemsToResolve: number): Thenable<types.CompletionList> {
private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string, maxItemsToResolve: number): Promise<types.CompletionList> {
const args = {
resource,
position: position && typeConverters.Position.from(position),
@@ -377,14 +402,14 @@ export class ExtHostApiCommands {
};
return this._commands.executeCommand<modes.CompletionList>('_executeCompletionItemProvider', args).then(result => {
if (result) {
const items = result.suggestions.map(suggestion => typeConverters.Suggest.to(suggestion));
const items = result.suggestions.map(suggestion => typeConverters.CompletionItem.to(suggestion));
return new types.CompletionList(items, result.incomplete);
}
return undefined;
});
}
private _executeDocumentColorProvider(resource: URI): Thenable<types.ColorInformation[]> {
private _executeDocumentColorProvider(resource: URI): Promise<types.ColorInformation[]> {
const args = {
resource
};
@@ -396,7 +421,20 @@ export class ExtHostApiCommands {
});
}
private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range }): Thenable<types.ColorPresentation[]> {
private _executeSelectionRangeProvider(resource: URI, position: types.Position): Promise<types.Range[]> {
const args = {
resource,
position: position && typeConverters.Position.from(position)
};
return this._commands.executeCommand<IRange[]>('_executeSelectionRangeProvider', args).then(result => {
if (isNonEmptyArray(result)) {
return result.map(typeConverters.Range.to);
}
return [];
});
}
private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range }): Promise<types.ColorPresentation[]> {
const args = {
resource: context.uri,
color: typeConverters.Color.from(color),
@@ -410,7 +448,7 @@ export class ExtHostApiCommands {
});
}
private _executeDocumentSymbolProvider(resource: URI): Thenable<vscode.SymbolInformation[]> {
private _executeDocumentSymbolProvider(resource: URI): Promise<vscode.SymbolInformation[]> {
const args = {
resource
};
@@ -442,7 +480,7 @@ export class ExtHostApiCommands {
});
}
private _executeCodeActionProvider(resource: URI, range: types.Range): Thenable<(vscode.CodeAction | vscode.Command)[]> {
private _executeCodeActionProvider(resource: URI, range: types.Range): Promise<(vscode.CodeAction | vscode.Command)[]> {
const args = {
resource,
range: typeConverters.Range.from(range)
@@ -467,7 +505,7 @@ export class ExtHostApiCommands {
}));
}
private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Thenable<vscode.CodeLens[]> {
private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Promise<vscode.CodeLens[]> {
const args = { resource, itemResolveCount };
return this._commands.executeCommand<modes.ICodeLensSymbol[]>('_executeCodeLensProvider', args)
.then(tryMapWith(item => {
@@ -478,7 +516,7 @@ export class ExtHostApiCommands {
}
private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Promise<vscode.TextEdit[]> {
const args = {
resource,
options
@@ -487,7 +525,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): Thenable<vscode.TextEdit[]> {
private _executeFormatRangeProvider(resource: URI, range: types.Range, options: vscode.FormattingOptions): Promise<vscode.TextEdit[]> {
const args = {
resource,
range: typeConverters.Range.from(range),
@@ -497,7 +535,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): Thenable<vscode.TextEdit[]> {
private _executeFormatOnTypeProvider(resource: URI, position: types.Position, ch: string, options: vscode.FormattingOptions): Promise<vscode.TextEdit[]> {
const args = {
resource,
position: typeConverters.Position.from(position),
@@ -508,7 +546,7 @@ export class ExtHostApiCommands {
.then(tryMapWith(edit => new types.TextEdit(typeConverters.Range.to(edit.range), edit.text)));
}
private _executeDocumentLinkProvider(resource: URI): Thenable<vscode.DocumentLink[]> {
private _executeDocumentLinkProvider(resource: URI): Promise<vscode.DocumentLink[]> {
return this._commands.executeCommand<modes.ILink[]>('_executeLinkProvider', resource)
.then(tryMapWith(typeConverters.DocumentLink.to));
}

View File

@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IMainContext, MainContext, MainThreadClipboardShape } from 'vs/workbench/api/node/extHost.protocol';
import * as vscode from 'vscode';
export class ExtHostClipboard implements vscode.Clipboard {
private readonly _proxy: MainThreadClipboardShape;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadClipboard);
}
readText(): Promise<string> {
return this._proxy.$readText();
}
writeText(value: string): Promise<void> {
return this._proxy.$writeText(value);
}
}

View File

@@ -10,7 +10,7 @@ import * as extHostTypeConverter from 'vs/workbench/api/node/extHostTypeConverte
import { cloneAndChange } from 'vs/base/common/objects';
import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ObjectIdentifier, IMainContext } from './extHost.protocol';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import * as modes from 'vs/editor/common/modes';
import * as vscode from 'vscode';
import { ILogService } from 'vs/platform/log/common/log';
@@ -78,7 +78,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
});
}
executeCommand<T>(id: string, ...args: any[]): Thenable<T> {
executeCommand<T>(id: string, ...args: any[]): Promise<T> {
this._logService.trace('ExtHostCommands#executeCommand', id);
if (this._commands.has(id)) {
@@ -108,7 +108,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
}
}
private _executeContributedCommand<T>(id: string, args: any[]): Thenable<T> {
private _executeContributedCommand<T>(id: string, args: any[]): Promise<T> {
let { callback, thisArg, description } = this._commands.get(id);
if (description) {
for (let i = 0; i < description.args.length; i++) {
@@ -129,7 +129,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
}
}
$executeContributedCommand<T>(id: string, ...args: any[]): Thenable<T> {
$executeContributedCommand<T>(id: string, ...args: any[]): Promise<T> {
this._logService.trace('ExtHostCommands#$executeContributedCommand', id);
if (!this._commands.has(id)) {
@@ -140,7 +140,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
}
}
getCommands(filterUnderscoreCommands: boolean = false): Thenable<string[]> {
getCommands(filterUnderscoreCommands: boolean = false): Promise<string[]> {
this._logService.trace('ExtHostCommands#getCommands', filterUnderscoreCommands);
return this._proxy.$getCommands().then(result => {
@@ -151,7 +151,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
});
}
$getContributedCommandHandlerDescriptions(): Thenable<{ [id: string]: string | ICommandHandlerDescription }> {
$getContributedCommandHandlerDescriptions(): Promise<{ [id: string]: string | ICommandHandlerDescription }> {
const result: { [id: string]: string | ICommandHandlerDescription } = Object.create(null);
this._commands.forEach((command, id) => {
let { description } = command;
@@ -189,7 +189,7 @@ export class CommandsConverter {
title: command.title
};
if (command.command && !isFalsyOrEmpty(command.arguments)) {
if (command.command && isNonEmptyArray(command.arguments)) {
// we have a contributed command with arguments. that
// means we don't want to send the arguments around
@@ -226,7 +226,7 @@ export class CommandsConverter {
}
}
private _executeConvertedCommand<R>(...args: any[]): Thenable<R> {
private _executeConvertedCommand<R>(...args: any[]): Promise<R> {
const actualCmd = this._heap.get<vscode.Command>(args[0]);
return this._commands.executeCommand(actualCmd.command, ...actualCmd.arguments);
}

View File

@@ -3,9 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { asThenable } from 'vs/base/common/async';
import { asPromise } from 'vs/base/common/async';
import { URI, UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
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';
@@ -32,11 +31,12 @@ export class ExtHostComments implements ExtHostCommentsShape {
}
registerWorkspaceCommentProvider(
extensionId: string,
provider: vscode.WorkspaceCommentProvider
): vscode.Disposable {
const handle = ExtHostComments.handlePool++;
this._workspaceProviders.set(handle, provider);
this._proxy.$registerWorkspaceCommentProvider(handle);
this._proxy.$registerWorkspaceCommentProvider(handle, extensionId);
this.registerListeners(handle, provider);
return {
@@ -52,7 +52,11 @@ export class ExtHostComments implements ExtHostCommentsShape {
): vscode.Disposable {
const handle = ExtHostComments.handlePool++;
this._documentProviders.set(handle, provider);
this._proxy.$registerDocumentCommentProvider(handle);
this._proxy.$registerDocumentCommentProvider(handle, {
startDraftLabel: provider.startDraftLabel,
deleteDraftLabel: provider.deleteDraftLabel,
finishDraftLabel: provider.finishDraftLabel
});
this.registerListeners(handle, provider);
return {
@@ -63,35 +67,35 @@ export class ExtHostComments implements ExtHostCommentsShape {
};
}
$createNewCommentThread(handle: number, uri: UriComponents, range: IRange, text: string): Thenable<modes.CommentThread> {
$createNewCommentThread(handle: number, uri: UriComponents, range: IRange, text: string): Promise<modes.CommentThread> {
const data = this._documents.getDocumentData(URI.revive(uri));
const ran = <vscode.Range>extHostTypeConverter.Range.to(range);
if (!data || !data.document) {
return TPromise.as(null);
return Promise.resolve(null);
}
const provider = this._documentProviders.get(handle);
return asThenable(() => {
return asPromise(() => {
return provider.createNewCommentThread(data.document, ran, text, CancellationToken.None);
}).then(commentThread => commentThread ? convertToCommentThread(provider, commentThread, this._commandsConverter) : null);
}
$replyToCommentThread(handle: number, uri: UriComponents, range: IRange, thread: modes.CommentThread, text: string): Thenable<modes.CommentThread> {
$replyToCommentThread(handle: number, uri: UriComponents, range: IRange, thread: modes.CommentThread, text: string): Promise<modes.CommentThread> {
const data = this._documents.getDocumentData(URI.revive(uri));
const ran = <vscode.Range>extHostTypeConverter.Range.to(range);
if (!data || !data.document) {
return TPromise.as(null);
return Promise.resolve(null);
}
const provider = this._documentProviders.get(handle);
return asThenable(() => {
return asPromise(() => {
return provider.replyToCommentThread(data.document, ran, convertFromCommentThread(thread), text, CancellationToken.None);
}).then(commentThread => commentThread ? convertToCommentThread(provider, commentThread, this._commandsConverter) : null);
}
$editComment(handle: number, uri: UriComponents, comment: modes.Comment, text: string): Thenable<void> {
$editComment(handle: number, uri: UriComponents, comment: modes.Comment, text: string): Promise<void> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
@@ -99,12 +103,12 @@ export class ExtHostComments implements ExtHostCommentsShape {
}
const provider = this._documentProviders.get(handle);
return asThenable(() => {
return asPromise(() => {
return provider.editComment(data.document, convertFromComment(comment), text, CancellationToken.None);
});
}
$deleteComment(handle: number, uri: UriComponents, comment: modes.Comment): Thenable<void> {
$deleteComment(handle: number, uri: UriComponents, comment: modes.Comment): Promise<void> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
@@ -112,30 +116,51 @@ export class ExtHostComments implements ExtHostCommentsShape {
}
const provider = this._documentProviders.get(handle);
return asThenable(() => {
return asPromise(() => {
return provider.deleteComment(data.document, convertFromComment(comment), CancellationToken.None);
});
}
$provideDocumentComments(handle: number, uri: UriComponents): Thenable<modes.CommentInfo> {
$startDraft(handle: number): Promise<void> {
const provider = this._documentProviders.get(handle);
return asPromise(() => {
return provider.startDraft(CancellationToken.None);
});
}
$deleteDraft(handle: number): Promise<void> {
const provider = this._documentProviders.get(handle);
return asPromise(() => {
return provider.deleteDraft(CancellationToken.None);
});
}
$finishDraft(handle: number): Promise<void> {
const provider = this._documentProviders.get(handle);
return asPromise(() => {
return provider.finishDraft(CancellationToken.None);
});
}
$provideDocumentComments(handle: number, uri: UriComponents): Promise<modes.CommentInfo> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
return TPromise.as(null);
return Promise.resolve(null);
}
const provider = this._documentProviders.get(handle);
return asThenable(() => {
return asPromise(() => {
return provider.provideDocumentComments(data.document, CancellationToken.None);
}).then(commentInfo => commentInfo ? convertCommentInfo(handle, provider, commentInfo, this._commandsConverter) : null);
}
$provideWorkspaceComments(handle: number): Thenable<modes.CommentThread[]> {
$provideWorkspaceComments(handle: number): Promise<modes.CommentThread[]> {
const provider = this._workspaceProviders.get(handle);
if (!provider) {
return TPromise.as(null);
return Promise.resolve(null);
}
return asThenable(() => {
return asPromise(() => {
return provider.provideWorkspaceComments(CancellationToken.None);
}).then(comments =>
comments.map(comment => convertToCommentThread(provider, comment, this._commandsConverter)
@@ -146,10 +171,10 @@ export class ExtHostComments implements ExtHostCommentsShape {
provider.onDidChangeCommentThreads(event => {
this._proxy.$onDidCommentThreadsChange(handle, {
owner: handle,
changed: event.changed.map(thread => convertToCommentThread(provider, thread, this._commandsConverter)),
added: event.added.map(thread => convertToCommentThread(provider, thread, this._commandsConverter)),
removed: event.removed.map(thread => convertToCommentThread(provider, thread, this._commandsConverter))
removed: event.removed.map(thread => convertToCommentThread(provider, thread, this._commandsConverter)),
draftMode: !!(provider as vscode.DocumentCommentProvider).startDraft && !!(provider as vscode.DocumentCommentProvider).finishDraft ? (event.inDraftMode ? modes.DraftMode.InDraft : modes.DraftMode.NotInDraft) : modes.DraftMode.NotSupported
});
});
}
@@ -157,9 +182,9 @@ export class ExtHostComments implements ExtHostCommentsShape {
function convertCommentInfo(owner: number, provider: vscode.DocumentCommentProvider, vscodeCommentInfo: vscode.CommentInfo, commandsConverter: CommandsConverter): modes.CommentInfo {
return {
owner: owner,
threads: vscodeCommentInfo.threads.map(x => convertToCommentThread(provider, x, commandsConverter)),
commentingRanges: vscodeCommentInfo.commentingRanges ? vscodeCommentInfo.commentingRanges.map(range => extHostTypeConverter.Range.from(range)) : []
commentingRanges: vscodeCommentInfo.commentingRanges ? vscodeCommentInfo.commentingRanges.map(range => extHostTypeConverter.Range.from(range)) : [],
draftMode: provider.startDraft && provider.finishDraft ? (vscodeCommentInfo.inDraftMode ? modes.DraftMode.InDraft : modes.DraftMode.NotInDraft) : modes.DraftMode.NotSupported
};
}
@@ -199,7 +224,8 @@ function convertFromComment(comment: modes.Comment): vscode.Comment {
userName: comment.userName,
userIconPath: userIconPath,
canEdit: comment.canEdit,
canDelete: comment.canDelete
canDelete: comment.canDelete,
isDraft: comment.isDraft
};
}
@@ -214,6 +240,7 @@ function convertToComment(provider: vscode.DocumentCommentProvider | vscode.Work
userIconPath: iconPath,
canEdit: canEdit,
canDelete: canDelete,
command: vscodeComment.command ? commandsConverter.toInternal(vscodeComment.command) : null
command: vscodeComment.command ? commandsConverter.toInternal(vscodeComment.command) : null,
isDraft: vscodeComment.isDraft
};
}

View File

@@ -6,9 +6,8 @@
import * as paths from 'vs/base/common/paths';
import { Schemas } from 'vs/base/common/network';
import { URI, UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { Event, Emitter } from 'vs/base/common/event';
import { asThenable } from 'vs/base/common/async';
import { asPromise } from 'vs/base/common/async';
import * as nls from 'vs/nls';
import {
MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID,
@@ -16,31 +15,35 @@ 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 { generateUuid } from 'vs/base/common/uuid';
import { ExecutableDebugAdapter, SocketDebugAdapter, AbstractDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter } from 'vs/workbench/parts/debug/common/debug';
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 { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/node/variableResolver';
import { ExtHostConfiguration } from './extHostConfiguration';
import { convertToVSCPaths, convertToDAPaths } from 'vs/workbench/parts/debug/common/debugUtils';
import { convertToVSCPaths, convertToDAPaths, stringToUri, uriToString } from 'vs/workbench/parts/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';
export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _providerHandleCounter: number;
private _providerByHandle: Map<number, vscode.DebugConfigurationProvider>;
private _providerByType: Map<string, vscode.DebugConfigurationProvider>;
private _providers: TypeProviderPair[];
private _configProviderHandleCounter: number;
private _configProviders: ConfigProviderTuple[];
private _adapterFactoryHandleCounter: number;
private _adapterFactories: DescriptorFactoryTuple[];
private _trackerFactoryHandleCounter: number;
private _trackerFactories: TrackerFactoryTuple[];
private _debugServiceProxy: MainThreadDebugServiceShape;
private _debugSessions: Map<DebugSessionUUID, ExtHostDebugSession> = new Map<DebugSessionUUID, ExtHostDebugSession>();
@@ -70,7 +73,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _aexCommands: Map<string, string>;
private _debugAdapters: Map<number, IDebugAdapter>;
private _debugAdaptersTrackers: Map<number, vscode.IDebugAdapterTracker>;
private _debugAdaptersTrackers: Map<number, vscode.DebugAdapterTracker>;
private _variableResolver: IConfigurationResolverService;
@@ -86,10 +89,14 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _terminalService: ExtHostTerminalService,
private _commandService: ExtHostCommands
) {
this._providerHandleCounter = 0;
this._providerByHandle = new Map();
this._providerByType = new Map();
this._providers = [];
this._configProviderHandleCounter = 0;
this._configProviders = [];
this._adapterFactoryHandleCounter = 0;
this._adapterFactories = [];
this._trackerFactoryHandleCounter = 0;
this._trackerFactories = [];
this._aexCommands = new Map();
this._debugAdapters = new Map();
@@ -113,28 +120,29 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._breakpoints = new Map<string, vscode.Breakpoint>();
this._breakpointEventsActive = false;
// register all debug extensions
const debugTypes: string[] = [];
for (const ed of this._extensionService.getAllExtensionDescriptions()) {
if (ed.contributes) {
const debuggers = <IDebuggerContribution[]>ed.contributes['debuggers'];
if (debuggers && debuggers.length > 0) {
for (const dbg of debuggers) {
// only debugger contributions with a "label" are considered a "defining" debugger contribution
if (dbg.type && dbg.label) {
debugTypes.push(dbg.type);
if (dbg.adapterExecutableCommand) {
this._aexCommands.set(dbg.type, dbg.adapterExecutableCommand);
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" are considered a "defining" debugger contribution
if (dbg.type && dbg.label) {
debugTypes.push(dbg.type);
if (dbg.adapterExecutableCommand) {
this._aexCommands.set(dbg.type, dbg.adapterExecutableCommand);
}
}
}
}
}
}
}
if (debugTypes.length > 0) {
this._debugServiceProxy.$registerDebugTypes(debugTypes);
}
if (debugTypes.length > 0) {
this._debugServiceProxy.$registerDebugTypes(debugTypes);
}
});
}
// extension debug API
@@ -152,27 +160,19 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return result;
}
public addBreakpoints(breakpoints0: vscode.Breakpoint[]): Thenable<void> {
public addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void> {
this.startBreakpoints();
// assign uuids for brand new breakpoints
const breakpoints: vscode.Breakpoint[] = [];
for (const bp of breakpoints0) {
let id = bp['_id'];
if (id) { // has already id
if (this._breakpoints.has(id)) {
// already there
} else {
breakpoints.push(bp);
}
} else {
id = generateUuid();
bp['_id'] = id;
// filter only new breakpoints
const breakpoints = breakpoints0.filter(bp => {
const id = bp.id;
if (!this._breakpoints.has(id)) {
this._breakpoints.set(id, bp);
breakpoints.push(bp);
return true;
}
}
return false;
});
// send notification for added breakpoints
this.fireBreakpointChanges(breakpoints, [], []);
@@ -193,7 +193,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
dtos.push(dto);
}
dto.lines.push({
id: bp['_id'],
id: bp.id,
enabled: bp.enabled,
condition: bp.condition,
hitCondition: bp.hitCondition,
@@ -204,7 +204,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
} else if (bp instanceof FunctionBreakpoint) {
dtos.push({
type: 'function',
id: bp['_id'],
id: bp.id,
enabled: bp.enabled,
hitCondition: bp.hitCondition,
logMessage: bp.logMessage,
@@ -218,75 +218,103 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return this._debugServiceProxy.$registerBreakpoints(dtos);
}
public removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Thenable<void> {
public removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void> {
this.startBreakpoints();
// remove from array
const breakpoints: vscode.Breakpoint[] = [];
for (const b of breakpoints0) {
let id = b['_id'];
if (id && this._breakpoints.delete(id)) {
breakpoints.push(b);
}
}
const breakpoints = breakpoints0.filter(b => this._breakpoints.delete(b.id));
// send notification
this.fireBreakpointChanges([], breakpoints, []);
// unregister with VS Code
const ids = breakpoints.filter(bp => bp instanceof SourceBreakpoint).map(bp => bp['_id']);
const fids = breakpoints.filter(bp => bp instanceof FunctionBreakpoint).map(bp => bp['_id']);
const ids = breakpoints.filter(bp => bp instanceof SourceBreakpoint).map(bp => bp.id);
const fids = breakpoints.filter(bp => bp instanceof FunctionBreakpoint).map(bp => bp.id);
return this._debugServiceProxy.$unregisterBreakpoints(ids, fids);
}
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration): Thenable<boolean> {
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration): Promise<boolean> {
return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig);
}
public registerDebugConfigurationProvider(extension: IExtensionDescription, type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable {
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable {
if (!provider) {
return new Disposable(() => { });
}
// if a provider has a provideDebugAdapter method, we check the constraints specified in the API doc
if (provider.provideDebugAdapter) {
// a provider with this method can only be registered in the extension that contributes the debugger
if (!this.definesDebugType(extension, type)) {
throw new Error(`method 'provideDebugAdapter' must only be called from the extension that defines the '${type}' debugger.`);
}
// make sure that only one provider for this type is registered
if (this._providerByType.has(type)) {
throw new Error(`a provider with method 'provideDebugAdapter' can only be registered once per a type.`);
} else {
this._providerByType.set(type, provider);
}
if (provider.debugAdapterExecutable) {
console.error('DebugConfigurationProvider.debugAdapterExecutable is deprecated and will be removed soon; please use DebugAdapterDescriptorFactory.createDebugAdapterDescriptor instead.');
}
let handle = this._providerHandleCounter++;
this._providerByHandle.set(handle, provider);
this._providers.push({ type, provider });
if (provider.provideDebugAdapterTracker) {
// console.error('DebugConfigurationProvider.provideDebugAdapterTracker is deprecated and will be removed soon; please use DebugAdapterTrackerFactory.createDebugAdapterTracker instead.');
}
let handle = this._configProviderHandleCounter++;
this._configProviders.push({ type, handle, provider });
this._debugServiceProxy.$registerDebugConfigurationProvider(type,
!!provider.provideDebugConfigurations,
!!provider.resolveDebugConfiguration,
!!provider.debugAdapterExecutable || !!provider.provideDebugAdapter,
!!provider.provideDebugAdapterTracker, handle);
!!provider.debugAdapterExecutable, // TODO@AW: deprecated
!!provider.provideDebugAdapterTracker, // TODO@AW: deprecated
handle);
return new Disposable(() => {
this._providerByHandle.delete(handle);
this._providerByType.delete(type);
this._providers = this._providers.filter(p => p.provider !== provider); // remove
this._configProviders = this._configProviders.filter(p => p.provider !== provider); // remove
this._debugServiceProxy.$unregisterDebugConfigurationProvider(handle);
});
}
public registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable {
if (!factory) {
return new Disposable(() => { });
}
// a DebugAdapterDescriptorFactory can only be registered in the extension that contributes the debugger
if (!this.definesDebugType(extension, type)) {
throw new Error(`a DebugAdapterDescriptorFactory can only be registered from the extension that defines the '${type}' debugger.`);
}
// make sure that only one factory for this type is registered
if (this.getAdapterFactoryByType(type)) {
throw new Error(`a DebugAdapterDescriptorFactory can only be registered once per a type.`);
}
let handle = this._adapterFactoryHandleCounter++;
this._adapterFactories.push({ type, handle, factory });
this._debugServiceProxy.$registerDebugAdapterDescriptorFactory(type, handle);
return new Disposable(() => {
this._adapterFactories = this._adapterFactories.filter(p => p.factory !== factory); // remove
this._debugServiceProxy.$unregisterDebugAdapterDescriptorFactory(handle);
});
}
public registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable {
if (!factory) {
return new Disposable(() => { });
}
let handle = this._trackerFactoryHandleCounter++;
this._trackerFactories.push({ type, handle, factory });
this._debugServiceProxy.$registerDebugAdapterTrackerFactory(type, handle);
return new Disposable(() => {
this._trackerFactories = this._trackerFactories.filter(p => p.factory !== factory); // remove
this._debugServiceProxy.$unregisterDebugAdapterTrackerFactory(handle);
});
}
// RPC methods (ExtHostDebugServiceShape)
public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<void> {
public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Promise<number | undefined> {
if (args.kind === 'integrated') {
@@ -299,7 +327,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
});
}
return new TPromise(resolve => {
return new Promise(resolve => {
if (this._integratedTerminalInstance) {
this._integratedTerminalInstance.processId.then(pid => {
resolve(hasChildprocesses(pid));
@@ -317,12 +345,12 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._integratedTerminalInstance.show();
return new TPromise((resolve, error) => {
setTimeout(_ => {
const command = prepareCommand(args, config);
this._integratedTerminalInstance.sendText(command, true);
resolve(void 0);
}, 500);
return this._integratedTerminalInstance.processId.then(shellProcessId => {
const command = prepareCommand(args, config);
this._integratedTerminalInstance.sendText(command, true);
return shellProcessId;
});
});
@@ -336,7 +364,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return void 0;
}
public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): TPromise<IConfig> {
public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise<IConfig> {
if (!this._variableResolver) {
this._variableResolver = new ExtHostVariableResolverService(this._workspaceService, this._editorsService, this._configurationService);
}
@@ -352,15 +380,17 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
};
}
return TPromise.wrap(this._variableResolver.resolveAny(ws, config));
return Promise.resolve(this._variableResolver.resolveAny(ws, config));
}
public $startDASession(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): TPromise<void> {
public $startDASession(debugAdapterHandle: number, sessionDto: IDebugSessionDto): Promise<void> {
const mythis = this;
return this.getAdapterDescriptor(this._providerByType.get(config.type), sessionDto, folderUri, config).then(adapter => {
const session = this.getSession(sessionDto);
return this.getAdapterDescriptor(this.getAdapterFactoryByType(session.type), session).then(x => {
let da: AbstractDebugAdapter = undefined;
const adapter = this.convertToDto(x);
let da: AbstractDebugAdapter | undefined = undefined;
switch (adapter.type) {
@@ -369,7 +399,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
break;
case 'executable':
da = new ExecutableDebugAdapter(adapter, config.type);
da = new ExecutableDebugAdapter(adapter, session.type);
break;
case 'implementation':
@@ -381,43 +411,40 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
if (da) {
this._debugAdapters.set(handle, da);
this._debugAdapters.set(debugAdapterHandle, da);
return this.getDebugAdapterTrackers(sessionDto, folderUri, config).then(tracker => {
return this.getDebugAdapterTrackers(session).then(tracker => {
if (tracker) {
this._debugAdaptersTrackers.set(handle, tracker);
this._debugAdaptersTrackers.set(debugAdapterHandle, tracker);
}
da.onMessage(message => {
if (tracker) {
tracker.fromDebugAdapter(message);
tracker.onDidSendMessage(message);
}
// DA -> VS Code
convertToVSCPaths(message, source => {
if (paths.isAbsolute(source.path)) {
(<any>source).path = URI.file(source.path);
}
});
mythis._debugServiceProxy.$acceptDAMessage(handle, message);
message = convertToVSCPaths(message, source => stringToUri(source));
mythis._debugServiceProxy.$acceptDAMessage(debugAdapterHandle, message);
});
da.onError(err => {
if (tracker) {
tracker.debugAdapterError(err);
tracker.onError(err);
}
this._debugServiceProxy.$acceptDAError(handle, err.name, err.message, err.stack);
this._debugServiceProxy.$acceptDAError(debugAdapterHandle, err.name, err.message, err.stack);
});
da.onExit(code => {
if (tracker) {
tracker.debugAdapterExit(code, null);
tracker.onExit(code, undefined);
}
this._debugServiceProxy.$acceptDAExit(handle, code, null);
this._debugServiceProxy.$acceptDAExit(debugAdapterHandle, code, null);
});
if (tracker) {
tracker.startDebugAdapter();
tracker.onWillStartSession();
}
return da.startSession();
@@ -428,36 +455,33 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
});
}
public $sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): TPromise<void> {
// VS Code -> DA
convertToDAPaths(message, source => {
if (typeof source.path === 'object') {
source.path = URI.revive(source.path).fsPath;
}
});
public $sendDAMessage(debugAdapterHandle: number, message: DebugProtocol.ProtocolMessage): Promise<void> {
const tracker = this._debugAdaptersTrackers.get(handle);
// VS Code -> DA
message = convertToDAPaths(message, source => uriToString(source));
const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle); // TODO@AW: same handle?
if (tracker) {
tracker.toDebugAdapter(message);
tracker.onWillReceiveMessage(message);
}
const da = this._debugAdapters.get(handle);
const da = this._debugAdapters.get(debugAdapterHandle);
if (da) {
da.sendMessage(message);
}
return void 0;
}
public $stopDASession(handle: number): TPromise<void> {
public $stopDASession(debugAdapterHandle: number): Promise<void> {
const tracker = this._debugAdaptersTrackers.get(handle);
this._debugAdaptersTrackers.delete(handle);
const tracker = this._debugAdaptersTrackers.get(debugAdapterHandle);
this._debugAdaptersTrackers.delete(debugAdapterHandle);
if (tracker) {
tracker.stopDebugAdapter();
tracker.onWillStopSession();
}
const da = this._debugAdapters.get(handle);
this._debugAdapters.delete(handle);
const da = this._debugAdapters.get(debugAdapterHandle);
this._debugAdapters.delete(debugAdapterHandle);
if (da) {
return da.stopSession();
} else {
@@ -482,7 +506,7 @@ 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['_id'] = bpd.id;
(bp as any)._id = bpd.id;
this._breakpoints.set(bpd.id, bp);
a.push(bp);
}
@@ -526,37 +550,46 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this.fireBreakpointChanges(a, r, c);
}
public $provideDebugConfigurations(handle: number, folderUri: UriComponents | undefined): Thenable<vscode.DebugConfiguration[]> {
let provider = this._providerByHandle.get(handle);
public $provideDebugConfigurations(configProviderHandle: number, folderUri: UriComponents | undefined): Promise<vscode.DebugConfiguration[]> {
let provider = this.getConfigProviderByHandle(configProviderHandle);
if (!provider) {
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('no handler found'));
return Promise.reject(new Error('no handler found'));
}
if (!provider.provideDebugConfigurations) {
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('handler has no method provideDebugConfigurations'));
return Promise.reject(new Error('handler has no method provideDebugConfigurations'));
}
return asThenable(() => provider.provideDebugConfigurations(this.getFolder(folderUri), CancellationToken.None));
return asPromise(() => provider.provideDebugConfigurations(this.getFolder(folderUri), CancellationToken.None));
}
public $resolveDebugConfiguration(handle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Thenable<vscode.DebugConfiguration> {
let provider = this._providerByHandle.get(handle);
public $resolveDebugConfiguration(configProviderHandle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Promise<vscode.DebugConfiguration> {
let provider = this.getConfigProviderByHandle(configProviderHandle);
if (!provider) {
return TPromise.wrapError<vscode.DebugConfiguration>(new Error('no handler found'));
return Promise.reject(new Error('no handler found'));
}
if (!provider.resolveDebugConfiguration) {
return TPromise.wrapError<vscode.DebugConfiguration>(new Error('handler has no method resolveDebugConfiguration'));
return Promise.reject(new Error('handler has no method resolveDebugConfiguration'));
}
return asThenable(() => provider.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, CancellationToken.None));
return asPromise(() => provider.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, CancellationToken.None));
}
public $provideDebugAdapter(handle: number, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable<vscode.DebugAdapterDescriptor> {
let provider = this._providerByHandle.get(handle);
// TODO@AW legacy
public $legacyDebugAdapterExecutable(configProviderHandle: number, folderUri: UriComponents | undefined): Promise<IAdapterDescriptor> {
let provider = this.getConfigProviderByHandle(configProviderHandle);
if (!provider) {
return TPromise.wrapError<vscode.DebugAdapterExecutable>(new Error('no handler found'));
return Promise.reject(new Error('no handler found'));
}
if (!provider.debugAdapterExecutable && !provider.provideDebugAdapter) {
return TPromise.wrapError<vscode.DebugAdapterExecutable>(new Error('handler has no methods provideDebugAdapter or debugAdapterExecutable'));
if (!provider.debugAdapterExecutable) {
return Promise.reject(new Error('handler has no method debugAdapterExecutable'));
}
return this.getAdapterDescriptor(provider, this.getSession(sessionDto), folderUri, config);
return asPromise(() => provider.debugAdapterExecutable(this.getFolder(folderUri), CancellationToken.None)).then(x => this.convertToDto(x));
}
public $provideDebugAdapter(adapterProviderHandle: number, sessionDto: IDebugSessionDto): Promise<IAdapterDescriptor> {
let 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));
}
public $acceptDebugSessionStarted(sessionDto: IDebugSessionDto): void {
@@ -566,8 +599,11 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
public $acceptDebugSessionTerminated(sessionDto: IDebugSessionDto): void {
this._onDidTerminateDebugSession.fire(this.getSession(sessionDto));
this._debugSessions.delete(sessionDto.id);
const session = this.getSession(sessionDto);
if (session) {
this._onDidTerminateDebugSession.fire(session);
this._debugSessions.delete(session.id);
}
}
public $acceptDebugSessionActiveChanged(sessionDto: IDebugSessionDto): void {
@@ -588,6 +624,54 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
// private & dto helpers
private convertToDto(x: vscode.DebugAdapterDescriptor): IAdapterDescriptor {
if (x instanceof DebugAdapterExecutable) {
return <IDebugAdapterExecutable>{
type: 'executable',
command: x.command,
args: x.args,
options: x.options
};
} else if (x instanceof DebugAdapterServer) {
return <IDebugAdapterServer>{
type: 'server',
port: x.port,
host: x.host
};
} else /* if (x instanceof DebugAdapterImplementation) {
return <IDebugAdapterImplementation>{
type: 'implementation',
implementation: x.implementation
};
} else */ {
throw new Error('unexpected type');
}
}
private getAdapterFactoryByType(type: string): vscode.DebugAdapterDescriptorFactory {
const results = this._adapterFactories.filter(p => p.type === type);
if (results.length > 0) {
return results[0].factory;
}
return undefined;
}
private getAdapterProviderByHandle(handle: number): vscode.DebugAdapterDescriptorFactory {
const results = this._adapterFactories.filter(p => p.handle === handle);
if (results.length > 0) {
return results[0].factory;
}
return undefined;
}
private getConfigProviderByHandle(handle: number): vscode.DebugConfigurationProvider {
const results = this._configProviders.filter(p => p.handle === handle);
if (results.length > 0) {
return results[0].provider;
}
return undefined;
}
private definesDebugType(ed: IExtensionDescription, type: string) {
if (ed.contributes) {
const debuggers = <IDebuggerContribution[]>ed.contributes['debuggers'];
@@ -605,54 +689,84 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return false;
}
private getDebugAdapterTrackers(sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): TPromise<vscode.IDebugAdapterTracker> {
const session = this.getSession(sessionDto);
const folder = this.getFolder(folderUri);
private getDebugAdapterTrackers(session: ExtHostDebugSession): Promise<vscode.DebugAdapterTracker> {
const config = session.configuration;
const type = config.type;
const promises = this._providers
.filter(pair => pair.provider.provideDebugAdapterTracker && (pair.type === type || pair.type === '*'))
.map(pair => pair.provider.provideDebugAdapterTracker(session, folder, config, CancellationToken.None));
return TPromise.join(promises).then(trackers => {
if (trackers.length > 0) {
return new MultiTracker(trackers);
}
const promises1 = this._configProviders
.filter(tuple => tuple.provider.provideDebugAdapterTracker && (tuple.type === type || tuple.type === '*'))
.map(tuple => asPromise(() => tuple.provider.provideDebugAdapterTracker(session, session.workspaceFolder, session.configuration, CancellationToken.None)).then(p => p).catch(err => null));
const promises2 = this._trackerFactories
.filter(tuple => tuple.type === type || tuple.type === '*')
.map(tuple => asPromise(() => tuple.factory.createDebugAdapterTracker(session)).then(p => p).catch(err => null));
const promises = promises1.concat(promises2);
return Promise.race([
Promise.all(promises).then(trackers => {
trackers = trackers.filter(t => t); // filter null
if (trackers.length > 0) {
return new MultiTracker(trackers);
}
return undefined;
}),
new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
clearTimeout(timeout);
reject(new Error('timeout'));
}, 1000);
})
]).catch(err => {
// ignore errors
return undefined;
});
}
private getAdapterDescriptor(debugConfigProvider, sessionDto: IDebugSessionDto, folderUri: UriComponents | undefined, config: vscode.DebugConfiguration): Thenable<vscode.DebugAdapterDescriptor> {
private async getAdapterDescriptor(adapterProvider: vscode.DebugAdapterDescriptorFactory, session: ExtHostDebugSession): Promise<vscode.DebugAdapterDescriptor> {
// a "debugServer" attribute in the launch config takes precedence
if (typeof config.debugServer === 'number') {
return TPromise.wrap(new DebugAdapterServer(config.debugServer));
const serverPort = session.configuration.debugServer;
if (typeof serverPort === 'number') {
return Promise.resolve(new DebugAdapterServer(serverPort));
}
if (debugConfigProvider) {
// try the proposed "provideDebugAdapter" API
if (debugConfigProvider.provideDebugAdapter) {
const adapterExecutable = ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type);
return asThenable(() => debugConfigProvider.provideDebugAdapter(this.getSession(sessionDto), this.getFolder(folderUri), adapterExecutable, config, CancellationToken.None));
}
// try the deprecated "debugAdapterExecutable" API
if (debugConfigProvider.debugAdapterExecutable) {
return asThenable(() => debugConfigProvider.debugAdapterExecutable(this.getFolder(folderUri), CancellationToken.None));
// 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));
}
}
if (adapterProvider) {
const extensionRegistry = await this._extensionService.getExtensionRegistry();
return asPromise(() => adapterProvider.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry)));
}
// try deprecated command based extension API "adapterExecutableCommand" to determine the executable
const aex = this._aexCommands.get(config.type);
// TODO@AW legacy
const aex = this._aexCommands.get(session.type);
if (aex) {
const rootFolder = folderUri ? URI.revive(folderUri).toString() : undefined;
const folder = session.workspaceFolder;
const rootFolder = folder ? folder.uri.toString() : undefined;
return this._commandService.executeCommand(aex, rootFolder).then((ae: { command: string, args: string[] }) => {
return new DebugAdapterExecutable(ae.command, ae.args || []);
});
}
// fallback: use executable information from package.json
return TPromise.wrap(ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), config.type));
const extensionRegistry = await this._extensionService.getExtensionRegistry();
return Promise.resolve(this.daExecutableFromPackage(session, extensionRegistry));
}
private daExecutableFromPackage(session: ExtHostDebugSession, extensionRegistry: ExtensionDescriptionRegistry): DebugAdapterExecutable | undefined {
const dae = ExecutableDebugAdapter.platformAdapterExecutable(extensionRegistry.getAllExtensionDescriptions(), session.type);
if (dae) {
return new DebugAdapterExecutable(dae.command, dae.args, dae.options);
}
return undefined;
}
private startBreakpoints() {
@@ -674,12 +788,13 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private getSession(dto: IDebugSessionDto): ExtHostDebugSession {
if (dto) {
let debugSession = this._debugSessions.get(dto.id);
if (!debugSession) {
debugSession = new ExtHostDebugSession(this._debugServiceProxy, dto.id, dto.type, dto.name);
this._debugSessions.set(dto.id, debugSession);
if (typeof dto === 'string') {
return this._debugSessions.get(dto);
} 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;
}
return debugSession;
}
return undefined;
}
@@ -695,17 +810,13 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
export class ExtHostDebugSession implements vscode.DebugSession {
private _debugServiceProxy: MainThreadDebugServiceShape;
private _id: DebugSessionUUID;
private _type: string;
private _name: string;
constructor(proxy: MainThreadDebugServiceShape, id: DebugSessionUUID, type: string, name: string) {
this._debugServiceProxy = proxy;
this._id = id;
this._type = type;
this._name = name;
constructor(
private _debugServiceProxy: MainThreadDebugServiceShape,
private _id: DebugSessionUUID,
private _type: string,
private _name: string,
private _workspaceFolder: vscode.WorkspaceFolder | undefined,
private _configuration: vscode.DebugConfiguration) {
}
public get id(): string {
@@ -720,7 +831,15 @@ export class ExtHostDebugSession implements vscode.DebugSession {
return this._name;
}
public customRequest(command: string, args: any): Thenable<any> {
public get workspaceFolder(): vscode.WorkspaceFolder | undefined {
return this._workspaceFolder;
}
public get configuration(): vscode.DebugConfiguration {
return this._configuration;
}
public customRequest(command: string, args: any): Promise<any> {
return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args);
}
}
@@ -761,7 +880,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
return configurationService.getConfiguration(undefined, folderUri).get<string>(section);
},
getExecPath: (): string | undefined => {
return undefined; // does not exist in EH
return process.env['VSCODE_EXEC_PATH'];
},
getFilePath: (): string | undefined => {
const activeEditor = editorService.activeEditor();
@@ -791,98 +910,104 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
}
}
interface TypeProviderPair {
interface ConfigProviderTuple {
type: string;
handle: number;
provider: vscode.DebugConfigurationProvider;
}
interface DescriptorFactoryTuple {
type: string;
handle: number;
factory: vscode.DebugAdapterDescriptorFactory;
}
interface TrackerFactoryTuple {
type: string;
handle: number;
factory: vscode.DebugAdapterTrackerFactory;
}
class MultiTracker implements vscode.DebugAdapterTracker {
constructor(private trackers: vscode.DebugAdapterTracker[]) {
}
onWillStartSession(): void {
this.trackers.forEach(t => t.onWillStartSession ? t.onWillStartSession() : (t.startDebugAdapter ? t.startDebugAdapter() : void 0));
}
onWillReceiveMessage(message: any): void {
this.trackers.forEach(t => t.onWillReceiveMessage ? t.onWillReceiveMessage(message) : (t.toDebugAdapter ? t.toDebugAdapter(message) : void 0));
}
onDidSendMessage(message: any): void {
this.trackers.forEach(t => t.onDidSendMessage ? t.onDidSendMessage(message) : (t.fromDebugAdapter ? t.fromDebugAdapter(message) : void 0));
}
onWillStopSession(): void {
this.trackers.forEach(t => t.onWillStopSession ? t.onWillStopSession() : (t.stopDebugAdapter ? t.stopDebugAdapter() : void 0));
}
onError(error: Error): void {
this.trackers.forEach(t => t.onError ? t.onError(error) : (t.debugAdapterError ? t.debugAdapterError(error) : void 0));
}
onExit(code: number, signal: string): void {
this.trackers.forEach(t => t.onExit ? t.onExit(code, signal) : (t.debugAdapterExit ? t.debugAdapterExit(code, signal) : void 0));
}
}
interface IDapTransport {
start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void);
send(message: DebugProtocol.ProtocolMessage);
stop(): void;
}
class MultiTracker implements vscode.IDebugAdapterTracker {
constructor(private trackers: vscode.IDebugAdapterTracker[]) {
}
startDebugAdapter(): void {
this.trackers.forEach(t => t.startDebugAdapter ? t.startDebugAdapter() : void 0);
}
toDebugAdapter(message: any): void {
this.trackers.forEach(t => t.toDebugAdapter ? t.toDebugAdapter(message) : void 0);
}
fromDebugAdapter(message: any): void {
this.trackers.forEach(t => t.fromDebugAdapter ? t.fromDebugAdapter(message) : void 0);
}
debugAdapterError(error: Error): void {
this.trackers.forEach(t => t.debugAdapterError ? t.debugAdapterError(error) : void 0);
}
debugAdapterExit(code: number, signal: string): void {
this.trackers.forEach(t => t.debugAdapterExit ? t.debugAdapterExit(code, signal) : void 0);
}
stopDebugAdapter(): void {
this.trackers.forEach(t => t.stopDebugAdapter ? t.stopDebugAdapter() : void 0);
}
}
class DirectTransport implements IDapTransport {
private _sendUp: (msg: DebugProtocol.ProtocolMessage) => void;
constructor(private da: DirectDebugAdapter) {
}
start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void) {
this._sendUp = cb;
}
sendUp(message: DebugProtocol.ProtocolMessage) {
this._sendUp(message);
}
// DA -> VSCode
send(message: DebugProtocol.ProtocolMessage) {
this.da.acceptMessage(message);
}
stop(): void {
throw new Error('Method not implemented.');
}
}
class DirectDebugAdapter extends AbstractDebugAdapter {
class DirectDebugAdapter extends AbstractDebugAdapter implements IDapTransport {
readonly onError: Event<Error>;
readonly onExit: Event<number>;
private transport: DirectTransport;
private _sendUp: (msg: DebugProtocol.ProtocolMessage) => void;
constructor(implementation: any) {
super();
if (implementation.__setTransport) {
this.transport = new DirectTransport(this);
implementation.__setTransport(this.transport);
implementation.__setTransport(this);
}
}
startSession(): TPromise<void> {
return TPromise.wrap(void 0);
// IDapTransport
start(cb: (msg: DebugProtocol.ProtocolMessage) => void, errorcb: (event: DebugProtocol.Event) => void) {
this._sendUp = cb;
}
// AbstractDebugAdapter
startSession(): Promise<void> {
return Promise.resolve(void 0);
}
// AbstractDebugAdapter
// VSCode -> DA
sendMessage(message: DebugProtocol.ProtocolMessage): void {
this.transport.sendUp(message);
this._sendUp(message);
}
stopSession(): TPromise<void> {
this.transport.stop();
return TPromise.wrap(void 0);
// AbstractDebugAdapter
stopSession(): Promise<void> {
this.stop();
return Promise.resolve(void 0);
}
// IDapTransport
// DA -> VSCode
send(message: DebugProtocol.ProtocolMessage) {
this.acceptMessage(message);
}
// IDapTransport
stop(): void {
throw new Error('Method not implemented.');
}
}

View File

@@ -6,7 +6,6 @@
import * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { MainContext, IMainContext, ExtHostDecorationsShape, MainThreadDecorationsShape, DecorationData, DecorationRequest, DecorationReply } from 'vs/workbench/api/node/extHost.protocol';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -42,9 +41,9 @@ export class ExtHostDecorations implements ExtHostDecorationsShape {
});
}
$provideDecorations(requests: DecorationRequest[], token: CancellationToken): Thenable<DecorationReply> {
$provideDecorations(requests: DecorationRequest[], token: CancellationToken): Promise<DecorationReply> {
const result: DecorationReply = Object.create(null);
return TPromise.join(requests.map(request => {
return Promise.all(requests.map(request => {
const { handle, uri, id } = request;
if (!this._provider.has(handle)) {
// might have been unregistered in the meantime

View File

@@ -11,7 +11,7 @@ import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMain
import { DiagnosticSeverity, Diagnostic } from './extHostTypes';
import * as converter from './extHostTypeConverters';
import { mergeSort, equals } from 'vs/base/common/arrays';
import { Event, Emitter, debounceEvent, mapEvent } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { keys } from 'vs/base/common/map';
export class DiagnosticCollection implements vscode.DiagnosticCollection {
@@ -75,7 +75,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
}
// update single row
this._data.set(first.toString(), diagnostics);
this._data.set(first.toString(), diagnostics.slice());
toSync = [first];
} else if (Array.isArray(first)) {
@@ -249,7 +249,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
return { uris };
}
readonly onDidChangeDiagnostics: Event<vscode.DiagnosticChangeEvent> = mapEvent(debounceEvent(this._onDidChangeDiagnostics.event, ExtHostDiagnostics._debouncer, 50), ExtHostDiagnostics._mapper);
readonly onDidChangeDiagnostics: Event<vscode.DiagnosticChangeEvent> = Event.map(Event.debounce(this._onDidChangeDiagnostics.event, ExtHostDiagnostics._debouncer, 50), ExtHostDiagnostics._mapper);
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadDiagnostics);

View File

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

View File

@@ -4,14 +4,14 @@
*--------------------------------------------------------------------------------------------*/
import { ok } from 'vs/base/common/assert';
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel';
import { URI } from 'vs/base/common/uri';
import { Range, Position, EndOfLine } from 'vs/workbench/api/node/extHostTypes';
import * as vscode from 'vscode';
import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper';
import { MainThreadDocumentsShape } from './extHost.protocol';
import { Schemas } from 'vs/base/common/network';
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { MirrorTextModel } from 'vs/editor/common/model/mirrorTextModel';
import { ensureValidWordDefinition, getWordAtText } from 'vs/editor/common/model/wordHelper';
import { MainThreadDocumentsShape } from 'vs/workbench/api/node/extHost.protocol';
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 {
@@ -97,7 +97,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
this._isDirty = isDirty;
}
private _save(): Thenable<boolean> {
private _save(): Promise<boolean> {
if (this._isDisposed) {
return Promise.reject(new Error('Document has been closed'));
}

View File

@@ -48,7 +48,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
};
}
$participateInSave(data: UriComponents, reason: SaveReason): Thenable<boolean[]> {
$participateInSave(data: UriComponents, reason: SaveReason): Promise<boolean[]> {
const resource = URI.revive(data);
const entries = this._callbacks.toArray();
@@ -109,7 +109,7 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
const event = Object.freeze(<vscode.TextDocumentWillSaveEvent>{
document,
reason,
waitUntil(p: Thenable<any | vscode.TextEdit[]>) {
waitUntil(p: Promise<any | vscode.TextEdit[]>) {
if (Object.isFrozen(promises)) {
throw illegalState('waitUntil can not be called async');
}

View File

@@ -3,15 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { URI, UriComponents } from 'vs/base/common/uri';
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as TypeConverters from './extHostTypeConverters';
import * as vscode from 'vscode';
import { MainContext, MainThreadDocumentsShape, ExtHostDocumentsShape, IMainContext } from './extHost.protocol';
import { ExtHostDocumentData, setWordDefinitionFor } from './extHostDocumentData';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import { ExtHostDocumentsShape, IMainContext, MainContext, MainThreadDocumentsShape } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDocumentData, setWordDefinitionFor } from 'vs/workbench/api/node/extHostDocumentData';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import * as vscode from 'vscode';
export class ExtHostDocuments implements ExtHostDocumentsShape {
@@ -28,7 +28,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
private _toDispose: IDisposable[];
private _proxy: MainThreadDocumentsShape;
private _documentsAndEditors: ExtHostDocumentsAndEditors;
private _documentLoader = new Map<string, Thenable<ExtHostDocumentData>>();
private _documentLoader = new Map<string, Promise<ExtHostDocumentData>>();
constructor(mainContext: IMainContext, documentsAndEditors: ExtHostDocumentsAndEditors) {
this._proxy = mainContext.getProxy(MainContext.MainThreadDocuments);
@@ -67,7 +67,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
return undefined;
}
public ensureDocumentData(uri: URI): Thenable<ExtHostDocumentData> {
public ensureDocumentData(uri: URI): Promise<ExtHostDocumentData> {
let cached = this._documentsAndEditors.getDocument(uri.toString());
if (cached) {
@@ -89,7 +89,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
return promise;
}
public createDocumentData(options?: { language?: string; content?: string }): Thenable<URI> {
public createDocumentData(options?: { language?: string; content?: string }): Promise<URI> {
return this._proxy.$tryCreateDocument(options).then(data => URI.revive(data));
}

View File

@@ -3,15 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { dispose } from 'vs/base/common/lifecycle';
import { MainContext, ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IMainContext } from './extHost.protocol';
import { ExtHostDocumentData } from './extHostDocumentData';
import { ExtHostTextEditor } from './extHostTextEditor';
import * as assert from 'assert';
import * as typeConverters from './extHostTypeConverters';
import { Emitter, Event } from 'vs/base/common/event';
import { dispose } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { Disposable } from './extHostTypes';
import { ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IMainContext, MainContext } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData';
import { ExtHostTextEditor } from 'vs/workbench/api/node/extHostTextEditor';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape {

View File

@@ -6,16 +6,15 @@
import * as nls from 'vs/nls';
import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = TPromise.wrap<void>(void 0);
const NO_OP_VOID_PROMISE = Promise.resolve<void>(void 0);
export interface IExtensionMemento {
get<T>(key: string, defaultValue: T): T;
update(key: string, value: any): Thenable<boolean>;
update(key: string, value: any): Promise<boolean>;
}
export interface IExtensionContext {
@@ -24,6 +23,7 @@ export interface IExtensionContext {
globalState: IExtensionMemento;
extensionPath: string;
storagePath: string;
globalStoragePath: string;
asAbsolutePath(relativePath: string): string;
readonly logPath: string;
}
@@ -32,8 +32,8 @@ export interface IExtensionContext {
* Represents the source code (module) of an extension.
*/
export interface IExtensionModule {
activate(ctx: IExtensionContext): TPromise<IExtensionAPI>;
deactivate(): void;
activate?(ctx: IExtensionContext): Promise<IExtensionAPI>;
deactivate?(): void;
}
/**
@@ -43,6 +43,14 @@ export interface IExtensionAPI {
// _extensionAPIBrand: any;
}
/* __GDPR__FRAGMENT__
"ExtensionActivationTimes" : {
"startup": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"codeLoadingTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"activateCallTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"activateResolvedTime" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }
}
*/
export class ExtensionActivationTimes {
public static readonly NONE = new ExtensionActivationTimes(false, -1, -1, -1);
@@ -124,18 +132,18 @@ export class ExtensionActivationTimesBuilder {
export class ActivatedExtension {
public readonly activationFailed: boolean;
public readonly activationFailedError: Error;
public readonly activationFailedError: Error | null;
public readonly activationTimes: ExtensionActivationTimes;
public readonly module: IExtensionModule;
public readonly exports: IExtensionAPI;
public readonly exports: IExtensionAPI | undefined;
public readonly subscriptions: IDisposable[];
constructor(
activationFailed: boolean,
activationFailedError: Error,
activationFailedError: Error | null,
activationTimes: ExtensionActivationTimes,
module: IExtensionModule,
exports: IExtensionAPI,
exports: IExtensionAPI | undefined,
subscriptions: IDisposable[]
) {
this.activationFailed = activationFailed;
@@ -162,7 +170,7 @@ export class FailedExtension extends ActivatedExtension {
export interface IExtensionsActivatorHost {
showMessage(severity: Severity, message: string): void;
actualActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise<ActivatedExtension>;
actualActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension>;
}
export class ExtensionActivatedByEvent {
@@ -184,7 +192,7 @@ export class ExtensionsActivator {
private readonly _registry: ExtensionDescriptionRegistry;
private readonly _host: IExtensionsActivatorHost;
private readonly _activatingExtensions: { [extensionId: string]: TPromise<void>; };
private readonly _activatingExtensions: { [extensionId: string]: Promise<void>; };
private readonly _activatedExtensions: { [extensionId: string]: ActivatedExtension; };
/**
* A map of already activated events to speed things up if the same activation event is triggered multiple times.
@@ -210,7 +218,7 @@ export class ExtensionsActivator {
return this._activatedExtensions[extensionId];
}
public activateByEvent(activationEvent: string, reason: ExtensionActivationReason): TPromise<void> {
public activateByEvent(activationEvent: string, reason: ExtensionActivationReason): Promise<void> {
if (this._alreadyActivatedEvents[activationEvent]) {
return NO_OP_VOID_PROMISE;
}
@@ -220,7 +228,7 @@ export class ExtensionsActivator {
});
}
public activateById(extensionId: string, reason: ExtensionActivationReason): TPromise<void> {
public activateById(extensionId: string, reason: ExtensionActivationReason): Promise<void> {
let desc = this._registry.getExtensionDescription(extensionId);
if (!desc) {
throw new Error('Extension `' + extensionId + '` is not known');
@@ -273,15 +281,15 @@ export class ExtensionsActivator {
}
}
private _activateExtensions(extensionDescriptions: IExtensionDescription[], reason: ExtensionActivationReason, recursionLevel: number): TPromise<void> {
private _activateExtensions(extensionDescriptions: IExtensionDescription[], reason: ExtensionActivationReason, recursionLevel: number): Promise<void> {
// console.log(recursionLevel, '_activateExtensions: ', extensionDescriptions.map(p => p.id));
if (extensionDescriptions.length === 0) {
return TPromise.as(void 0);
return Promise.resolve(void 0);
}
extensionDescriptions = extensionDescriptions.filter((p) => !hasOwnProperty.call(this._activatedExtensions, p.id));
if (extensionDescriptions.length === 0) {
return TPromise.as(void 0);
return Promise.resolve(void 0);
}
if (recursionLevel > 10) {
@@ -292,7 +300,7 @@ export class ExtensionsActivator {
const error = new Error('More than 10 levels of dependencies (most likely a dependency loop)');
this._activatedExtensions[extensionDescriptions[i].id] = new FailedExtension(error);
}
return TPromise.as(void 0);
return Promise.resolve(void 0);
}
let greenMap: { [id: string]: IExtensionDescription; } = Object.create(null),
@@ -316,7 +324,7 @@ export class ExtensionsActivator {
if (red.length === 0) {
// Finally reached only leafs!
return TPromise.join(green.map((p) => this._activateExtension(p, reason))).then(_ => void 0);
return Promise.all(green.map((p) => this._activateExtension(p, reason))).then(_ => void 0);
}
return this._activateExtensions(green, reason, recursionLevel + 1).then(_ => {
@@ -324,16 +332,16 @@ export class ExtensionsActivator {
});
}
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise<void> {
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<void> {
if (hasOwnProperty.call(this._activatedExtensions, extensionDescription.id)) {
return TPromise.as(void 0);
return Promise.resolve(void 0);
}
if (hasOwnProperty.call(this._activatingExtensions, extensionDescription.id)) {
return this._activatingExtensions[extensionDescription.id];
}
this._activatingExtensions[extensionDescription.id] = this._host.actualActivateExtension(extensionDescription, reason).then(null, (err) => {
this._activatingExtensions[extensionDescription.id] = this._host.actualActivateExtension(extensionDescription, reason).then(void 0, (err) => {
this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionDescription.id, err.message));
console.error('Activating extension `' + extensionDescription.id + '` failed: ', err.message);
console.log('Here is the error stack: ', err.stack);

View File

@@ -3,24 +3,28 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { dispose } from 'vs/base/common/lifecycle';
import { join } from 'path';
import { mkdirp, dirExists, realpath, writeFile } from 'vs/base/node/pfs';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
import { createApiFactory, initializeExtensionApi } from 'vs/workbench/api/node/extHost.api.impl';
import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape, IMainContext } from './extHost.protocol';
import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule, ExtensionActivationTimesBuilder, ExtensionActivationTimes, ExtensionActivationReason, ExtensionActivatedByEvent, ExtensionActivatedByAPI } from 'vs/workbench/api/node/extHostExtensionActivator';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { TernarySearchTree } from 'vs/base/common/map';
import * as nls from 'vs/nls';
import * as path from 'path';
import { Barrier } from 'vs/base/common/async';
import { ILogService } from 'vs/platform/log/common/log';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import { IDisposable, dispose } 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';
import { createApiFactory, initializeExtensionApi, IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, IWorkspaceData, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } 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 { 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, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { connectProxyResolver } from 'vs/workbench/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';
class ExtensionMemento implements IExtensionMemento {
@@ -28,8 +32,9 @@ class ExtensionMemento implements IExtensionMemento {
private readonly _shared: boolean;
private readonly _storage: ExtHostStorage;
private readonly _init: Thenable<ExtensionMemento>;
private readonly _init: Promise<ExtensionMemento>;
private _value: { [n: string]: any; };
private readonly _storageListener: IDisposable;
constructor(id: string, global: boolean, storage: ExtHostStorage) {
this._id = id;
@@ -40,9 +45,15 @@ class ExtensionMemento implements IExtensionMemento {
this._value = value;
return this;
});
this._storageListener = this._storage.onDidChangeStorage(e => {
if (e.shared === this._shared && e.key === this._id) {
this._value = e.value;
}
});
}
get whenReady(): Thenable<ExtensionMemento> {
get whenReady(): Promise<ExtensionMemento> {
return this._init;
}
@@ -54,12 +65,16 @@ class ExtensionMemento implements IExtensionMemento {
return value;
}
update(key: string, value: any): Thenable<boolean> {
update(key: string, value: any): Promise<boolean> {
this._value[key] = value;
return this._storage
.setValue(this._shared, this._id, this._value)
.then(() => true);
}
dispose(): void {
this._storageListener.dispose();
}
}
class ExtensionStoragePath {
@@ -80,31 +95,35 @@ class ExtensionStoragePath {
return this._ready;
}
value(extension: IExtensionDescription): string {
workspaceValue(extension: IExtensionDescription): string {
if (this._value) {
return join(this._value, extension.id);
return path.join(this._value, extension.id);
}
return undefined;
}
globalValue(extension: IExtensionDescription): string {
return path.join(this._environment.globalStorageHome.fsPath, extension.id);
}
private async _getOrCreateWorkspaceStoragePath(): Promise<string> {
if (!this._workspace) {
return TPromise.as(undefined);
return Promise.resolve(undefined);
}
const storageName = this._workspace.id;
const storagePath = join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName);
const storagePath = path.join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName);
const exists = await dirExists(storagePath);
const exists = await pfs.dirExists(storagePath);
if (exists) {
return storagePath;
}
try {
await mkdirp(storagePath);
await writeFile(
join(storagePath, 'meta.json'),
await pfs.mkdirp(storagePath);
await pfs.writeFile(
path.join(storagePath, 'meta.json'),
JSON.stringify({
id: this._workspace.id,
configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(),
@@ -120,67 +139,111 @@ class ExtensionStoragePath {
}
}
interface ITestRunner {
run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
}
export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000;
private readonly _nativeExit: (code?: number) => void;
private readonly _initData: IInitData;
private readonly _extHostContext: IMainContext;
private readonly _extHostWorkspace: ExtHostWorkspace;
private readonly _extHostConfiguration: ExtHostConfiguration;
private readonly _extHostLogService: ExtHostLogService;
private readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape;
private readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape;
private readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape;
private readonly _barrier: Barrier;
private readonly _registry: ExtensionDescriptionRegistry;
private readonly _mainThreadTelemetry: MainThreadTelemetryShape;
private readonly _storage: ExtHostStorage;
private readonly _storagePath: ExtensionStoragePath;
private readonly _proxy: MainThreadExtensionServiceShape;
private readonly _extHostLogService: ExtHostLogService;
private _activator: ExtensionsActivator;
private _extensionPathIndex: TPromise<TernarySearchTree<IExtensionDescription>>;
/**
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
*/
constructor(initData: IInitData,
private readonly _activator: ExtensionsActivator;
private _extensionPathIndex: Promise<TernarySearchTree<IExtensionDescription>>;
private readonly _extensionApiFactory: IExtensionApiFactory;
private _started: boolean;
constructor(
nativeExit: (code?: number) => void,
initData: IInitData,
extHostContext: IMainContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
extHostLogService: ExtHostLogService
) {
this._nativeExit = nativeExit;
this._initData = initData;
this._extHostContext = extHostContext;
this._extHostWorkspace = extHostWorkspace;
this._extHostConfiguration = extHostConfiguration;
this._extHostLogService = extHostLogService;
this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace);
this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry);
this._mainThreadExtensionsProxy = this._extHostContext.getProxy(MainContext.MainThreadExtensionService);
this._barrier = new Barrier();
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
this._extHostLogService = extHostLogService;
this._mainThreadTelemetry = extHostContext.getProxy(MainContext.MainThreadTelemetry);
this._storage = new ExtHostStorage(extHostContext);
this._storage = new ExtHostStorage(this._extHostContext);
this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment);
this._proxy = extHostContext.getProxy(MainContext.MainThreadExtensionService);
this._activator = null;
this._activator = new ExtensionsActivator(this._registry, {
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);
}
},
actualActivateExtension: (extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
return this._activateExtension(extensionDescription, reason);
}
});
this._extensionPathIndex = null;
// initialize API first (i.e. do not release barrier until the API is initialized)
const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, this._extHostLogService);
this._extensionApiFactory = createApiFactory(this._initData, this._extHostContext, this._extHostWorkspace, this._extHostConfiguration, this, this._extHostLogService, this._storage);
initializeExtensionApi(this, apiFactory).then(() => {
this._activator = new ExtensionsActivator(this._registry, {
showMessage: (severity: Severity, message: string): void => {
this._proxy.$localShowMessage(severity, message);
switch (severity) {
case Severity.Error:
console.error(message);
break;
case Severity.Warning:
console.warn(message);
break;
default:
console.log(message);
}
},
actualActivateExtension: (extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise<ActivatedExtension> => {
return this._activateExtension(extensionDescription, reason);
}
});
this._started = false;
initializeExtensionApi(this, this._extensionApiFactory, this._registry).then(() => {
// Do this when extension service exists, but extensions are not being activated yet.
return connectProxyResolver(this._extHostWorkspace, this._extHostConfiguration, this, this._extHostLogService, this._mainThreadTelemetryProxy);
}).then(() => {
this._barrier.open();
});
if (this._initData.autoStart) {
this._startExtensionHost();
}
}
public onExtensionAPIReady(): TPromise<boolean> {
return this._barrier.wait();
public async deactivateAll(): Promise<void> {
let allPromises: Promise<void>[] = [];
try {
const allExtensions = this._registry.getAllExtensionDescriptions();
const allExtensionsIds = allExtensions.map(ext => ext.id);
const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id));
allPromises = activatedExtensions.map((extensionId) => {
return this._deactivate(extensionId);
});
} catch (err) {
// TODO: write to log once we have one
}
await allPromises;
}
public isActivated(extensionId: string): boolean {
@@ -190,40 +253,28 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return false;
}
public activateByEvent(activationEvent: string, startup: boolean): TPromise<void> {
private _activateByEvent(activationEvent: string, startup: boolean): Promise<void> {
const reason = new ExtensionActivatedByEvent(startup, activationEvent);
if (this._barrier.isOpen()) {
return this._activator.activateByEvent(activationEvent, reason);
} else {
return this._barrier.wait().then(() => this._activator.activateByEvent(activationEvent, reason));
}
return this._activator.activateByEvent(activationEvent, reason);
}
public activateById(extensionId: string, reason: ExtensionActivationReason): TPromise<void> {
if (this._barrier.isOpen()) {
return this._activator.activateById(extensionId, reason);
} else {
return this._barrier.wait().then(() => this._activator.activateById(extensionId, reason));
}
private _activateById(extensionId: string, reason: ExtensionActivationReason): Promise<void> {
return this._activator.activateById(extensionId, reason);
}
public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): TPromise<void> {
return this.activateById(extensionId, reason).then(() => {
public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): Promise<void> {
return this._activateById(extensionId, reason).then(() => {
const extension = this._activator.getActivatedExtension(extensionId);
if (extension.activationFailed) {
// activation failed => bubble up the error as the promise result
return TPromise.wrapError(extension.activationFailedError);
return Promise.reject(extension.activationFailedError);
}
return void 0;
});
}
public getAllExtensionDescriptions(): IExtensionDescription[] {
return this._registry.getAllExtensionDescriptions();
}
public getExtensionDescription(extensionId: string): IExtensionDescription {
return this._registry.getExtensionDescription(extensionId);
public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {
return this._barrier.wait().then(_ => this._registry);
}
public getExtensionExports(extensionId: string): IExtensionAPI {
@@ -235,23 +286,22 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
// create trie to enable fast 'filename -> extension id' look up
public getExtensionPathIndex(): TPromise<TernarySearchTree<IExtensionDescription>> {
public getExtensionPathIndex(): Promise<TernarySearchTree<IExtensionDescription>> {
if (!this._extensionPathIndex) {
const tree = TernarySearchTree.forPaths<IExtensionDescription>();
const extensions = this.getAllExtensionDescriptions().map(ext => {
const extensions = this._registry.getAllExtensionDescriptions().map(ext => {
if (!ext.main) {
return undefined;
}
return realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
return pfs.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
});
this._extensionPathIndex = TPromise.join(extensions).then(() => tree);
this._extensionPathIndex = Promise.all(extensions).then(() => tree);
}
return this._extensionPathIndex;
}
public deactivate(extensionId: string): TPromise<void> {
let result: TPromise<void> = TPromise.as(void 0);
private _deactivate(extensionId: string): Promise<void> {
let result = Promise.resolve(void 0);
if (!this._barrier.isOpen()) {
return result;
@@ -269,9 +319,9 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
// call deactivate if available
try {
if (typeof extension.module.deactivate === 'function') {
result = TPromise.wrap(extension.module.deactivate()).then(null, (err) => {
result = Promise.resolve(extension.module.deactivate()).then(void 0, (err) => {
// TODO: Do something with err if this is not the shutdown case
return TPromise.as(void 0);
return Promise.resolve(void 0);
});
}
} catch (err) {
@@ -289,24 +339,45 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
public addMessage(extensionId: string, severity: Severity, message: string): void {
this._proxy.$addMessage(extensionId, severity, message);
this._mainThreadExtensionsProxy.$addMessage(extensionId, severity, message);
}
// --- impl
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise<ActivatedExtension> {
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
this._mainThreadExtensionsProxy.$onWillActivateExtension(extensionDescription.id);
return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => {
const activationTimes = activatedExtension.activationTimes;
let activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
this._proxy.$onExtensionActivated(extensionDescription.id, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
this._mainThreadExtensionsProxy.$onDidActivateExtension(extensionDescription.id, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes);
return activatedExtension;
}, (err) => {
this._proxy.$onExtensionActivationFailed(extensionDescription.id);
this._mainThreadExtensionsProxy.$onExtensionActivationFailed(extensionDescription.id);
this._logExtensionActivationTimes(extensionDescription, reason, 'failure');
throw err;
});
}
private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise<ActivatedExtension> {
private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) {
let event = getTelemetryActivationEvent(extensionDescription, reason);
/* __GDPR__
"extensionActivationTimes" : {
"${include}": [
"${TelemetryActivationEvent}",
"${ExtensionActivationTimes}"
],
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._mainThreadTelemetryProxy.$publicLog('extensionActivationTimes', {
...event,
...(activationTimes || {}),
outcome,
});
}
private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
let event = getTelemetryActivationEvent(extensionDescription, reason);
/* __GDPR__
"activatePlugin" : {
@@ -315,39 +386,30 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
]
}
*/
this._mainThreadTelemetry.$publicLog('activatePlugin', event);
this._mainThreadTelemetryProxy.$publicLog('activatePlugin', event);
if (!extensionDescription.main) {
// Treat the extension as being empty => NOT AN ERROR CASE
return TPromise.as(new EmptyExtension(ExtensionActivationTimes.NONE));
return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
}
this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`);
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
return TPromise.join<any>([
return Promise.all<any>([
loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.id, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
}, (errors: any[]) => {
// Avoid failing with an array of errors, fail with a single error
if (errors[0]) {
return TPromise.wrapError<ActivatedExtension>(errors[0]);
}
if (errors[1]) {
return TPromise.wrapError<ActivatedExtension>(errors[1]);
}
return undefined;
});
}
private _loadExtensionContext(extensionDescription: IExtensionDescription): TPromise<IExtensionContext> {
private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<IExtensionContext> {
let globalState = new ExtensionMemento(extensionDescription.id, true, this._storage);
let workspaceState = new ExtensionMemento(extensionDescription.id, false, this._storage);
this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`);
return TPromise.join([
return Promise.all([
globalState.whenReady,
workspaceState.whenReady,
this._storagePath.whenReady
@@ -358,14 +420,15 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
workspaceState,
subscriptions: [],
get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
storagePath: this._storagePath.value(extensionDescription),
asAbsolutePath: (relativePath: string) => { return join(extensionDescription.extensionLocation.fsPath, relativePath); },
storagePath: this._storagePath.workspaceValue(extensionDescription),
get globalStoragePath(): string { checkProposedApiEnabled(extensionDescription); return that._storagePath.globalValue(extensionDescription); },
asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
logPath: that._extHostLogService.getLogDirectory(extensionDescription.id)
});
});
}
private static _callActivate(logService: ILogService, extensionId: string, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Thenable<ActivatedExtension> {
private static _callActivate(logService: ILogService, extensionId: string, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<ActivatedExtension> {
// Make sure the extension's surface is not undefined
extensionModule = extensionModule || {
activate: undefined,
@@ -377,47 +440,225 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
});
}
private static _callActivateOptional(logService: ILogService, extensionId: string, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Thenable<IExtensionAPI> {
private static _callActivateOptional(logService: ILogService, extensionId: string, extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<IExtensionAPI> {
if (typeof extensionModule.activate === 'function') {
try {
activationTimesBuilder.activateCallStart();
logService.trace(`ExtensionService#_callActivateOptional ${extensionId}`);
const activateResult: TPromise<IExtensionAPI> = extensionModule.activate.apply(global, [context]);
const activateResult: Promise<IExtensionAPI> = extensionModule.activate.apply(global, [context]);
activationTimesBuilder.activateCallStop();
activationTimesBuilder.activateResolveStart();
return TPromise.as(activateResult).then((value) => {
return Promise.resolve(activateResult).then((value) => {
activationTimesBuilder.activateResolveStop();
return value;
});
} catch (err) {
return TPromise.wrapError(err);
return Promise.reject(err);
}
} else {
// No activate found => the module is the extension's exports
return TPromise.as<IExtensionAPI>(extensionModule);
return Promise.resolve<IExtensionAPI>(extensionModule);
}
}
// -- eager activation
// Handle "eager" activation extensions
private _handleEagerExtensions(): Promise<void> {
this._activateByEvent('*', true).then(void 0, (err) => {
console.error(err);
});
return this._handleWorkspaceContainsEagerExtensions(this._initData.workspace);
}
private _handleWorkspaceContainsEagerExtensions(workspace: IWorkspaceData): Promise<void> {
if (!workspace || workspace.folders.length === 0) {
return Promise.resolve(null);
}
return Promise.all(
this._registry.getAllExtensionDescriptions().map((desc) => {
return this._handleWorkspaceContainsEagerExtension(workspace, desc);
})
).then(() => { });
}
private _handleWorkspaceContainsEagerExtension(workspace: IWorkspaceData, desc: IExtensionDescription): Promise<void> {
const activationEvents = desc.activationEvents;
if (!activationEvents) {
return Promise.resolve(void 0);
}
const fileNames: string[] = [];
const globPatterns: string[] = [];
for (let i = 0; i < activationEvents.length; i++) {
if (/^workspaceContains:/.test(activationEvents[i])) {
const fileNameOrGlob = activationEvents[i].substr('workspaceContains:'.length);
if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
globPatterns.push(fileNameOrGlob);
} else {
fileNames.push(fileNameOrGlob);
}
}
}
if (fileNames.length === 0 && globPatterns.length === 0) {
return Promise.resolve(void 0);
}
const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.id, fileName))).then(() => { });
const globPatternPromise = this._activateIfGlobPatterns(desc.id, globPatterns);
return Promise.all([fileNamePromise, globPatternPromise]).then(() => { });
}
private async _activateIfFileName(workspace: IWorkspaceData, extensionId: string, fileName: string): Promise<void> {
// find exact path
for (const { uri } of workspace.folders) {
if (await pfs.exists(path.join(URI.revive(uri).fsPath, fileName))) {
// the file was found
return (
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${fileName}`))
.then(void 0, err => console.error(err))
);
}
}
return undefined;
}
private async _activateIfGlobPatterns(extensionId: string, globPatterns: string[]): Promise<void> {
this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId}, entryPoint: workspaceContains`);
if (globPatterns.length === 0) {
return Promise.resolve(void 0);
}
const tokenSource = new CancellationTokenSource();
const searchP = this._mainThreadWorkspaceProxy.$checkExists(globPatterns, tokenSource.token);
const timer = setTimeout(async () => {
tokenSource.cancel();
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContainsTimeout:${globPatterns.join(',')}`))
.then(void 0, err => console.error(err));
}, ExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT);
let exists: boolean;
try {
exists = await searchP;
} catch (err) {
if (!errors.isPromiseCanceledError(err)) {
console.error(err);
}
}
tokenSource.dispose();
clearTimeout(timer);
if (exists) {
// a file was found matching one of the glob patterns
return (
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${globPatterns.join(',')}`))
.then(void 0, err => console.error(err))
);
}
return Promise.resolve(void 0);
}
private _handleExtensionTests(): Promise<void> {
if (!this._initData.environment.extensionTestsPath || !this._initData.environment.extensionDevelopmentLocationURI) {
return Promise.resolve(null);
}
// Require the test runner via node require from the provided path
let testRunner: ITestRunner;
let requireError: Error;
try {
testRunner = <any>require.__$__nodeRequire(this._initData.environment.extensionTestsPath);
} catch (error) {
requireError = error;
}
// 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) => {
if (error) {
e(error.toString());
} else {
c(null);
}
// after tests have run, we shutdown the host
this._gracefulExit(failures && failures > 0 ? 1 /* ERROR */ : 0 /* OK */);
});
});
}
// Otherwise make sure to shutdown anyway even in case of an error
else {
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)));
}
private _gracefulExit(code: number): void {
// to give the PH process a chance to flush any outstanding console
// messages to the main process, we delay the exit() by some time
setTimeout(() => this._nativeExit(code), 500);
}
private _startExtensionHost(): Promise<void> {
if (this._started) {
throw new Error(`Extension host is already started!`);
}
this._started = true;
return this._barrier.wait()
.then(() => this._handleEagerExtensions())
.then(() => this._handleExtensionTests())
.then(() => {
this._extHostLogService.info(`eager extensions activated`);
});
}
// -- called by main thread
public $activateByEvent(activationEvent: string): Thenable<void> {
return this.activateByEvent(activationEvent, false);
public async $resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> {
throw new Error(`Not implemented`);
}
public $startExtensionHost(enabledExtensionIds: string[]): Promise<void> {
this._registry.keepOnly(enabledExtensionIds);
return this._startExtensionHost();
}
public $activateByEvent(activationEvent: string): Promise<void> {
return (
this._barrier.wait()
.then(_ => this._activateByEvent(activationEvent, false))
);
}
}
function loadCommonJSModule<T>(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise<T> {
let r: T = null;
function loadCommonJSModule<T>(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
let r: T | null = null;
activationTimesBuilder.codeLoadingStart();
logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`);
try {
r = require.__$__nodeRequire<T>(modulePath);
} catch (e) {
return TPromise.wrapError<T>(e);
return Promise.reject(e);
} finally {
activationTimesBuilder.codeLoadingStop();
}
return TPromise.as(r);
return Promise.resolve(r);
}
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any {
@@ -429,6 +670,7 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription
"TelemetryActivationEvent" : {
"id": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"extensionVersion": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"activationEvents": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
@@ -438,6 +680,7 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription
let event = {
id: extensionDescription.id,
name: extensionDescription.name,
extensionVersion: extensionDescription.version,
publisherDisplayName: extensionDescription.publisher,
activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,
isBuiltin: extensionDescription.isBuiltin,

View File

@@ -7,49 +7,98 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystemShape, IFileChangeDto } from './extHost.protocol';
import * as vscode from 'vscode';
import * as files from 'vs/platform/files/common/files';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { values } from 'vs/base/common/map';
import { Range, FileChangeType } from 'vs/workbench/api/node/extHostTypes';
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
import { FileChangeType, DocumentLink } 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 { LabelRules } from 'vs/platform/label/common/label';
import { State, StateMachine, LinkComputer } from 'vs/editor/common/modes/linkComputer';
import { commonPrefixLength } from 'vs/base/common/strings';
import { CharCode } from 'vs/base/common/charCode';
class FsLinkProvider implements vscode.DocumentLinkProvider {
class FsLinkProvider {
private _schemes = new Set<string>();
private _regex: RegExp;
private _schemes: string[] = [];
private _stateMachine: StateMachine;
add(scheme: string): void {
this._regex = undefined;
this._schemes.add(scheme);
this._stateMachine = undefined;
this._schemes.push(scheme);
}
delete(scheme: string): void {
if (this._schemes.delete(scheme)) {
this._regex = undefined;
let idx = this._schemes.indexOf(scheme);
if (idx >= 0) {
this._schemes.splice(idx, 1);
this._stateMachine = undefined;
}
}
private _initStateMachine(): void {
if (!this._stateMachine) {
// 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;
let prevState: State;
let nextState = State.LastKnownState;
for (const scheme of schemes) {
// skip the common prefix of the prev scheme
// and continue with its last state
let pos = !prevScheme ? 0 : commonPrefixLength(prevScheme, scheme);
if (pos === 0) {
prevState = State.Start;
} else {
prevState = nextState;
}
for (; pos < scheme.length; pos++) {
// keep creating new (next) states until the
// end (and the BeforeColon-state) is reached
if (pos + 1 === scheme.length) {
nextState = State.BeforeColon;
} else {
nextState += 1;
}
edges.push([prevState, scheme.toUpperCase().charCodeAt(pos), nextState]);
edges.push([prevState, scheme.toLowerCase().charCodeAt(pos), nextState]);
prevState = nextState;
}
prevScheme = scheme;
}
// all link must match this pattern `<scheme>:/<more>`
edges.push([State.BeforeColon, CharCode.Colon, State.AfterColon]);
edges.push([State.AfterColon, CharCode.Slash, State.End]);
this._stateMachine = new StateMachine(edges);
}
}
provideDocumentLinks(document: vscode.TextDocument): vscode.ProviderResult<vscode.DocumentLink[]> {
if (this._schemes.size === 0) {
return undefined;
}
if (!this._regex) {
this._regex = new RegExp(`(${(values(this._schemes).join('|'))}):[^\\s]+`, 'gi');
}
let result: vscode.DocumentLink[] = [];
let max = Math.min(document.lineCount, 2500);
for (let line = 0; line < max; line++) {
this._regex.lastIndex = 0;
let textLine = document.lineAt(line);
let m: RegExpMatchArray;
while (m = this._regex.exec(textLine.text)) {
const target = URI.parse(m[0]);
if (target.path[0] !== '/') {
continue;
}
const range = new Range(line, this._regex.lastIndex - m[0].length, line, this._regex.lastIndex);
result.push({ target, range });
this._initStateMachine();
const result: vscode.DocumentLink[] = [];
const links = LinkComputer.computeLinks({
getLineContent(lineNumber: number): string {
return document.lineAt(lineNumber - 1).text;
},
getLineCount(): number {
return document.lineCount;
}
}, 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
}
}
return result;
@@ -64,9 +113,10 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
private readonly _usedSchemes = new Set<string>();
private readonly _watches = new Map<number, IDisposable>();
private _linkProviderRegistration: IDisposable;
private _handlePool: number = 0;
constructor(mainContext: IMainContext, extHostLanguageFeatures: ExtHostLanguageFeatures) {
constructor(mainContext: IMainContext, private _extHostLanguageFeatures: ExtHostLanguageFeatures) {
this._proxy = mainContext.getProxy(MainContext.MainThreadFileSystem);
this._usedSchemes.add(Schemas.file);
this._usedSchemes.add(Schemas.untitled);
@@ -77,8 +127,16 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
this._usedSchemes.add(Schemas.https);
this._usedSchemes.add(Schemas.mailto);
this._usedSchemes.add(Schemas.data);
}
extHostLanguageFeatures.registerDocumentLinkProvider('*', this._linkProvider);
dispose(): void {
dispose(this._linkProviderRegistration);
}
private _registerLinkProviderIfNotYetRegistered(): void {
if (!this._linkProviderRegistration) {
this._linkProviderRegistration = this._extHostLanguageFeatures.registerDocumentLinkProvider(undefined, '*', this._linkProvider);
}
}
registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean, isReadonly?: boolean } = {}) {
@@ -87,6 +145,9 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
throw new Error(`a provider for the scheme '${scheme}' is already registered`);
}
//
this._registerLinkProviderIfNotYetRegistered();
const handle = this._handlePool++;
this._linkProvider.add(scheme);
this._usedSchemes.add(scheme);
@@ -153,41 +214,59 @@ 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);
}
$readdir(handle: number, resource: UriComponents): Promise<[string, files.FileType][]> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(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 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));
}
$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));
}
$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));
}
$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));
}
$mkdir(handle: number, resource: UriComponents): Promise<void> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(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);
this._watches.set(session, subscription);
}
@@ -201,18 +280,22 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
}
$open(handle: number, resource: UriComponents): Promise<number> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).open(URI.revive(resource)));
}
$close(handle: number, fd: number): Promise<void> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).close(fd));
}
$read(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Promise<number> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).read(fd, pos, data, offset, length));
}
$write(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Promise<number> {
this._checkProviderExists(handle);
return Promise.resolve(this._fsProvider.get(handle).write(fd, pos, data, offset, length));
}

View File

@@ -131,15 +131,15 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
getOnWillRenameFileEvent(extension: IExtensionDescription): Event<vscode.FileWillRenameEvent> {
return (listener, thisArg, disposables) => {
let wrappedListener = <WillRenameListener><any>function () {
listener.apply(thisArg, arguments);
};
const wrappedListener: WillRenameListener = <any>((e: vscode.FileWillRenameEvent) => {
listener.call(thisArg, e);
});
wrappedListener.extension = extension;
return this._onWillRenameFile.event(wrappedListener, undefined, disposables);
};
}
$onWillRename(oldUriDto: UriComponents, newUriDto: UriComponents): Thenable<any> {
$onWillRename(oldUriDto: UriComponents, newUriDto: UriComponents): Promise<any> {
const oldUri = URI.revive(oldUriDto);
const newUri = URI.revive(newUriDto);
@@ -148,7 +148,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ
return {
oldUri,
newUri,
waitUntil: (thenable: Thenable<vscode.WorkspaceEdit>): void => {
waitUntil: (thenable: Promise<vscode.WorkspaceEdit>): void => {
if (Object.isFrozen(bucket)) {
throw new TypeError('waitUntil cannot be called async');
}

View File

@@ -14,12 +14,12 @@ import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { asThenable } from 'vs/base/common/async';
import { asPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata } from './extHost.protocol';
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { isFalsyOrEmpty, isNonEmptyArray } from 'vs/base/common/arrays';
import { isObject } from 'vs/base/common/types';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
@@ -38,9 +38,9 @@ class OutlineAdapter {
this._provider = provider;
}
provideDocumentSymbols(resource: URI, token: CancellationToken): Thenable<modes.DocumentSymbol[]> {
provideDocumentSymbols(resource: URI, token: CancellationToken): Promise<modes.DocumentSymbol[]> {
let doc = this._documents.getDocumentData(resource).document;
return asThenable(() => this._provider.provideDocumentSymbols(doc, token)).then(value => {
return asPromise(() => this._provider.provideDocumentSymbols(doc, token)).then(value => {
if (isFalsyOrEmpty(value)) {
return undefined;
}
@@ -104,10 +104,10 @@ class CodeLensAdapter {
private readonly _provider: vscode.CodeLensProvider
) { }
provideCodeLenses(resource: URI, token: CancellationToken): Thenable<modes.ICodeLensSymbol[]> {
provideCodeLenses(resource: URI, token: CancellationToken): Promise<modes.ICodeLensSymbol[]> {
const doc = this._documents.getDocumentData(resource).document;
return asThenable(() => this._provider.provideCodeLenses(doc, token)).then(lenses => {
return asPromise(() => this._provider.provideCodeLenses(doc, token)).then(lenses => {
if (Array.isArray(lenses)) {
return lenses.map(lens => {
const id = this._heapService.keep(lens);
@@ -121,18 +121,18 @@ class CodeLensAdapter {
});
}
resolveCodeLens(resource: URI, symbol: modes.ICodeLensSymbol, token: CancellationToken): Thenable<modes.ICodeLensSymbol> {
resolveCodeLens(resource: URI, symbol: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol> {
const lens = this._heapService.get<vscode.CodeLens>(ObjectIdentifier.of(symbol));
if (!lens) {
return undefined;
}
let resolve: Thenable<vscode.CodeLens>;
let resolve: Promise<vscode.CodeLens>;
if (typeof this._provider.resolveCodeLens !== 'function' || lens.isResolved) {
resolve = Promise.resolve(lens);
} else {
resolve = asThenable(() => this._provider.resolveCodeLens(lens, token));
resolve = asPromise(() => this._provider.resolveCodeLens(lens, token));
}
return resolve.then(newLens => {
@@ -159,10 +159,24 @@ class DefinitionAdapter {
private readonly _provider: vscode.DefinitionProvider
) { }
provideDefinition(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
provideDefinition(resource: URI, position: IPosition, token: CancellationToken): Promise<modes.DefinitionLink[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asThenable(() => this._provider.provideDefinition(doc, pos, token)).then(convertToDefinitionLinks);
return asPromise(() => this._provider.provideDefinition(doc, pos, token)).then(convertToDefinitionLinks);
}
}
class DeclarationAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.DeclarationProvider
) { }
provideDeclaration(resource: URI, position: IPosition, token: CancellationToken): Promise<modes.DefinitionLink[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asPromise(() => this._provider.provideDeclaration(doc, pos, token)).then(convertToDefinitionLinks);
}
}
@@ -173,10 +187,10 @@ class ImplementationAdapter {
private readonly _provider: vscode.ImplementationProvider
) { }
provideImplementation(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
provideImplementation(resource: URI, position: IPosition, token: CancellationToken): Promise<modes.DefinitionLink[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asThenable(() => this._provider.provideImplementation(doc, pos, token)).then(convertToDefinitionLinks);
return asPromise(() => this._provider.provideImplementation(doc, pos, token)).then(convertToDefinitionLinks);
}
}
@@ -187,10 +201,10 @@ class TypeDefinitionAdapter {
private readonly _provider: vscode.TypeDefinitionProvider
) { }
provideTypeDefinition(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
provideTypeDefinition(resource: URI, position: IPosition, token: CancellationToken): Promise<modes.DefinitionLink[]> {
const doc = this._documents.getDocumentData(resource).document;
const pos = typeConvert.Position.to(position);
return asThenable(() => this._provider.provideTypeDefinition(doc, pos, token)).then(convertToDefinitionLinks);
return asPromise(() => this._provider.provideTypeDefinition(doc, pos, token)).then(convertToDefinitionLinks);
}
}
@@ -201,12 +215,12 @@ class HoverAdapter {
private readonly _provider: vscode.HoverProvider,
) { }
public provideHover(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.Hover> {
public provideHover(resource: URI, position: IPosition, token: CancellationToken): Promise<modes.Hover> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asThenable(() => this._provider.provideHover(doc, pos, token)).then(value => {
return asPromise(() => this._provider.provideHover(doc, pos, token)).then(value => {
if (!value || isFalsyOrEmpty(value.contents)) {
return undefined;
}
@@ -229,12 +243,12 @@ class DocumentHighlightAdapter {
private readonly _provider: vscode.DocumentHighlightProvider
) { }
provideDocumentHighlights(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.DocumentHighlight[]> {
provideDocumentHighlights(resource: URI, position: IPosition, token: CancellationToken): Promise<modes.DocumentHighlight[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asThenable(() => this._provider.provideDocumentHighlights(doc, pos, token)).then(value => {
return asPromise(() => this._provider.provideDocumentHighlights(doc, pos, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.DocumentHighlight.from);
}
@@ -250,11 +264,11 @@ class ReferenceAdapter {
private readonly _provider: vscode.ReferenceProvider
) { }
provideReferences(resource: URI, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Thenable<modes.Location[]> {
provideReferences(resource: URI, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<modes.Location[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asThenable(() => this._provider.provideReferences(doc, pos, context, token)).then(value => {
return asPromise(() => this._provider.provideReferences(doc, pos, context, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.location.from);
}
@@ -278,7 +292,7 @@ class CodeActionAdapter {
private readonly _extensionId: string
) { }
provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Thenable<CodeActionDto[]> {
provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionDto[]> {
const doc = this._documents.getDocumentData(resource).document;
const ran = Selection.isISelection(rangeOrSelection)
@@ -297,7 +311,7 @@ class CodeActionAdapter {
only: context.only ? new CodeActionKind(context.only) : undefined
};
return asThenable(() => this._provider.provideCodeActions(doc, ran, codeActionContext, token)).then(commandsOrActions => {
return asPromise(() => this._provider.provideCodeActions(doc, ran, codeActionContext, token)).then(commandsOrActions => {
if (isFalsyOrEmpty(commandsOrActions)) {
return undefined;
}
@@ -349,11 +363,11 @@ class DocumentFormattingAdapter {
private readonly _provider: vscode.DocumentFormattingEditProvider
) { }
provideDocumentFormattingEdits(resource: URI, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> {
provideDocumentFormattingEdits(resource: URI, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]> {
const { document } = this._documents.getDocumentData(resource);
return asThenable(() => this._provider.provideDocumentFormattingEdits(document, <any>options, token)).then(value => {
return asPromise(() => this._provider.provideDocumentFormattingEdits(document, <any>options, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.TextEdit.from);
}
@@ -369,12 +383,12 @@ class RangeFormattingAdapter {
private readonly _provider: vscode.DocumentRangeFormattingEditProvider
) { }
provideDocumentRangeFormattingEdits(resource: URI, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> {
provideDocumentRangeFormattingEdits(resource: URI, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]> {
const { document } = this._documents.getDocumentData(resource);
const ran = typeConvert.Range.to(range);
return asThenable(() => this._provider.provideDocumentRangeFormattingEdits(document, ran, <any>options, token)).then(value => {
return asPromise(() => this._provider.provideDocumentRangeFormattingEdits(document, ran, <any>options, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.TextEdit.from);
}
@@ -392,12 +406,12 @@ class OnTypeFormattingAdapter {
autoFormatTriggerCharacters: string[] = []; // not here
provideOnTypeFormattingEdits(resource: URI, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> {
provideOnTypeFormattingEdits(resource: URI, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]> {
const { document } = this._documents.getDocumentData(resource);
const pos = typeConvert.Position.to(position);
return asThenable(() => this._provider.provideOnTypeFormattingEdits(document, pos, ch, <any>options, token)).then(value => {
return asPromise(() => this._provider.provideOnTypeFormattingEdits(document, pos, ch, <any>options, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.TextEdit.from);
}
@@ -416,10 +430,10 @@ class NavigateTypeAdapter {
this._provider = provider;
}
provideWorkspaceSymbols(search: string, token: CancellationToken): Thenable<WorkspaceSymbolsDto> {
provideWorkspaceSymbols(search: string, token: CancellationToken): Promise<WorkspaceSymbolsDto> {
const result: WorkspaceSymbolsDto = IdObject.mixin({ symbols: [] });
return asThenable(() => this._provider.provideWorkspaceSymbols(search, token)).then(value => {
if (!isFalsyOrEmpty(value)) {
return asPromise(() => this._provider.provideWorkspaceSymbols(search, token)).then(value => {
if (isNonEmptyArray(value)) {
for (const item of value) {
if (!item) {
// drop
@@ -442,7 +456,7 @@ class NavigateTypeAdapter {
});
}
resolveWorkspaceSymbol(symbol: WorkspaceSymbolDto, token: CancellationToken): Thenable<WorkspaceSymbolDto> {
resolveWorkspaceSymbol(symbol: WorkspaceSymbolDto, token: CancellationToken): Promise<WorkspaceSymbolDto> {
if (typeof this._provider.resolveWorkspaceSymbol !== 'function') {
return Promise.resolve(symbol);
@@ -450,7 +464,7 @@ class NavigateTypeAdapter {
const item = this._symbolCache[symbol._id];
if (item) {
return asThenable(() => this._provider.resolveWorkspaceSymbol(item, token)).then(value => {
return asPromise(() => this._provider.resolveWorkspaceSymbol(item, token)).then(value => {
return value && mixin(symbol, typeConvert.WorkspaceSymbol.from(value), true);
});
}
@@ -479,12 +493,12 @@ class RenameAdapter {
private readonly _provider: vscode.RenameProvider
) { }
provideRenameEdits(resource: URI, position: IPosition, newName: string, token: CancellationToken): Thenable<WorkspaceEditDto> {
provideRenameEdits(resource: URI, position: IPosition, newName: string, token: CancellationToken): Promise<WorkspaceEditDto> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asThenable(() => this._provider.provideRenameEdits(doc, pos, newName, token)).then(value => {
return asPromise(() => this._provider.provideRenameEdits(doc, pos, newName, token)).then(value => {
if (!value) {
return undefined;
}
@@ -500,7 +514,7 @@ class RenameAdapter {
});
}
resolveRenameLocation(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.RenameLocation & modes.Rejection> {
resolveRenameLocation(resource: URI, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation & modes.Rejection> {
if (typeof this._provider.prepareRename !== 'function') {
return Promise.resolve(undefined);
}
@@ -508,7 +522,7 @@ class RenameAdapter {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asThenable(() => this._provider.prepareRename(doc, pos, token)).then(rangeOrLocation => {
return asPromise(() => this._provider.prepareRename(doc, pos, token)).then(rangeOrLocation => {
let range: vscode.Range;
let text: string;
@@ -569,13 +583,13 @@ class SuggestAdapter {
this._provider = provider;
}
provideCompletionItems(resource: URI, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Thenable<SuggestResultDto> {
provideCompletionItems(resource: URI, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<SuggestResultDto> {
const doc = this._documents.getDocumentData(resource).document;
const pos = typeConvert.Position.to(position);
return asThenable<vscode.CompletionItem[] | vscode.CompletionList>(
() => this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.from(context))
return asPromise<vscode.CompletionItem[] | vscode.CompletionList>(
() => this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.to(context))
).then(value => {
const _id = this._idPool++;
@@ -616,7 +630,7 @@ class SuggestAdapter {
});
}
resolveCompletionItem(resource: URI, position: IPosition, suggestion: modes.CompletionItem, token: CancellationToken): Thenable<modes.CompletionItem> {
resolveCompletionItem(resource: URI, position: IPosition, suggestion: modes.CompletionItem, token: CancellationToken): Promise<modes.CompletionItem> {
if (typeof this._provider.resolveCompletionItem !== 'function') {
return Promise.resolve(suggestion);
@@ -628,7 +642,7 @@ class SuggestAdapter {
return Promise.resolve(suggestion);
}
return asThenable(() => this._provider.resolveCompletionItem(item, token)).then(resolvedItem => {
return asPromise(() => this._provider.resolveCompletionItem(item, token)).then(resolvedItem => {
if (!resolvedItem) {
return suggestion;
@@ -664,38 +678,32 @@ class SuggestAdapter {
label: item.label,
kind: typeConvert.CompletionItemKind.from(item.kind),
detail: item.detail,
documentation: item.documentation,
documentation: typeConvert.MarkdownString.fromStrict(item.documentation),
filterText: item.filterText,
sortText: item.sortText,
preselect: item.preselect,
//
range: undefined,
insertText: undefined,
insertTextRules: item.keepWhitespace ? modes.CompletionItemInsertTextRule.KeepWhitespace : 0,
additionalTextEdits: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
command: this._commands.toInternal(item.command),
commitCharacters: item.commitCharacters,
// help with perf
_labelLow: item.label.toLowerCase(),
_filterTextLow: item.filterText && item.filterText.toLowerCase(),
_sortTextLow: item.sortText && item.sortText.toLowerCase()
commitCharacters: item.commitCharacters
};
// 'insertText'-logic
if (item.textEdit) {
result.insertText = item.textEdit.newText;
result.insertTextIsSnippet = false;
} else if (typeof item.insertText === 'string') {
result.insertText = item.insertText;
result.insertTextIsSnippet = false;
} else if (item.insertText instanceof SnippetString) {
result.insertText = item.insertText.value;
result.insertTextIsSnippet = true;
result.insertTextRules += modes.CompletionItemInsertTextRule.InsertAsSnippet;
} else {
result.insertText = item.label;
result.insertTextIsSnippet = false;
}
// 'overwrite[Before|After]'-logic
@@ -725,12 +733,12 @@ class SignatureHelpAdapter {
private readonly _provider: vscode.SignatureHelpProvider
) { }
provideSignatureHelp(resource: URI, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Thenable<modes.SignatureHelp> {
provideSignatureHelp(resource: URI, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<modes.SignatureHelp> {
const doc = this._documents.getDocumentData(resource).document;
const pos = typeConvert.Position.to(position);
return asThenable(() => this._provider.provideSignatureHelp(doc, pos, token, context)).then(value => {
return asPromise(() => this._provider.provideSignatureHelp(doc, pos, token, context)).then(value => {
if (value) {
return typeConvert.SignatureHelp.from(value);
}
@@ -747,10 +755,10 @@ class LinkProviderAdapter {
private readonly _provider: vscode.DocumentLinkProvider
) { }
provideLinks(resource: URI, token: CancellationToken): Thenable<modes.ILink[]> {
provideLinks(resource: URI, token: CancellationToken): Promise<modes.ILink[]> {
const doc = this._documents.getDocumentData(resource).document;
return asThenable(() => this._provider.provideDocumentLinks(doc, token)).then(links => {
return asPromise(() => this._provider.provideDocumentLinks(doc, token)).then(links => {
if (!Array.isArray(links)) {
return undefined;
}
@@ -765,7 +773,7 @@ class LinkProviderAdapter {
});
}
resolveLink(link: modes.ILink, token: CancellationToken): Thenable<modes.ILink> {
resolveLink(link: modes.ILink, token: CancellationToken): Promise<modes.ILink> {
if (typeof this._provider.resolveDocumentLink !== 'function') {
return undefined;
}
@@ -776,7 +784,7 @@ class LinkProviderAdapter {
return undefined;
}
return asThenable(() => this._provider.resolveDocumentLink(item, token)).then(value => {
return asPromise(() => this._provider.resolveDocumentLink(item, token)).then(value => {
if (value) {
return typeConvert.DocumentLink.from(value);
}
@@ -792,9 +800,9 @@ class ColorProviderAdapter {
private _provider: vscode.DocumentColorProvider
) { }
provideColors(resource: URI, token: CancellationToken): Thenable<IRawColorInfo[]> {
provideColors(resource: URI, token: CancellationToken): Promise<IRawColorInfo[]> {
const doc = this._documents.getDocumentData(resource).document;
return asThenable(() => this._provider.provideDocumentColors(doc, token)).then(colors => {
return asPromise(() => this._provider.provideDocumentColors(doc, token)).then(colors => {
if (!Array.isArray(colors)) {
return [];
}
@@ -810,11 +818,11 @@ class ColorProviderAdapter {
});
}
provideColorPresentations(resource: URI, raw: IRawColorInfo, token: CancellationToken): Thenable<modes.IColorPresentation[]> {
provideColorPresentations(resource: URI, raw: IRawColorInfo, token: CancellationToken): Promise<modes.IColorPresentation[]> {
const document = this._documents.getDocumentData(resource).document;
const range = typeConvert.Range.to(raw.range);
const color = typeConvert.Color.to(raw.color);
return asThenable(() => this._provider.provideColorPresentations(color, { document, range }, token)).then(value => {
return asPromise(() => this._provider.provideColorPresentations(color, { document, range }, token)).then(value => {
return value.map(typeConvert.ColorPresentation.from);
});
}
@@ -827,9 +835,9 @@ class FoldingProviderAdapter {
private _provider: vscode.FoldingRangeProvider
) { }
provideFoldingRanges(resource: URI, context: modes.FoldingContext, token: CancellationToken): Thenable<modes.FoldingRange[]> {
provideFoldingRanges(resource: URI, context: modes.FoldingContext, token: CancellationToken): Promise<modes.FoldingRange[]> {
const doc = this._documents.getDocumentData(resource).document;
return asThenable(() => this._provider.provideFoldingRanges(doc, context, token)).then(ranges => {
return asPromise(() => this._provider.provideFoldingRanges(doc, context, token)).then(ranges => {
if (!Array.isArray(ranges)) {
return void 0;
}
@@ -838,11 +846,46 @@ class FoldingProviderAdapter {
}
}
class SelectionRangeAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _provider: vscode.SelectionRangeProvider
) { }
provideSelectionRanges(resource: URI, position: IPosition, token: CancellationToken): Promise<IRange[]> {
const { document } = this._documents.getDocumentData(resource);
const pos = typeConvert.Position.to(position);
return asPromise(() => this._provider.provideSelectionRanges(document, pos, token)).then(ranges => {
if (isFalsyOrEmpty(ranges)) {
return undefined;
}
let result: IRange[] = [];
let last: vscode.Position | vscode.Range = pos;
for (const range of ranges) {
if (!range.contains(last)) {
throw new Error('INVALID selection range, must contain the previous range');
}
result.push(typeConvert.Range.from(range));
last = range;
}
return result;
});
}
}
type Adapter = OutlineAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter
| ColorProviderAdapter | FoldingProviderAdapter;
| ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter;
class AdapterData {
constructor(
readonly adapter: Adapter,
readonly extension: IExtensionDescription | undefined
) { }
}
export interface ISchemeTransformer {
transformOutgoing(scheme: string): string;
@@ -858,7 +901,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
private _commands: ExtHostCommands;
private _heapService: ExtHostHeapService;
private _diagnostics: ExtHostDiagnostics;
private _adapter = new Map<number, Adapter>();
private _adapter = new Map<number, AdapterData>();
private readonly _logService: ILogService;
constructor(
@@ -926,39 +969,55 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return ExtHostLanguageFeatures._handlePool++;
}
private _withAdapter<A, R>(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A) => Thenable<R>): Thenable<R> {
let adapter = this._adapter.get(handle);
if (!(adapter instanceof ctor)) {
return Promise.reject(new Error('no adapter found'));
private _withAdapter<A, R>(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A) => Promise<R>): Promise<R> {
let data = this._adapter.get(handle);
if (data.adapter instanceof ctor) {
let t1: number;
if (data.extension) {
t1 = Date.now();
this._logService.trace(`[${data.extension.id}] INVOKE provider '${(ctor as any).name}'`);
}
let p = callback(data.adapter);
if (data.extension) {
Promise.resolve(p).then(
() => this._logService.trace(`[${data.extension.id}] provider DONE after ${Date.now() - t1}ms`),
err => {
this._logService.error(`[${data.extension.id}] provider FAILED`);
this._logService.error(err);
}
);
}
return p;
}
return callback(<any>adapter);
return Promise.reject(new Error('no adapter found'));
}
private _addNewAdapter(adapter: Adapter): number {
private _addNewAdapter(adapter: Adapter, extension: IExtensionDescription): number {
const handle = this._nextHandle();
this._adapter.set(handle, adapter);
this._adapter.set(handle, new AdapterData(adapter, extension));
return handle;
}
// --- outline
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, extension?: IExtensionDescription): vscode.Disposable {
const handle = this._addNewAdapter(new OutlineAdapter(this._documents, provider));
this._proxy.$registerOutlineSupport(handle, this._transformDocumentSelector(selector), extension ? extension.displayName || extension.name : undefined);
registerDocumentSymbolProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, metadata?: vscode.DocumentSymbolProviderMetadata): vscode.Disposable {
const handle = this._addNewAdapter(new OutlineAdapter(this._documents, provider), extension);
const displayName = (metadata && metadata.label) || (extension && (extension.displayName || extension.name)) || undefined;
this._proxy.$registerOutlineSupport(handle, this._transformDocumentSelector(selector), displayName);
return this._createDisposable(handle);
}
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Thenable<modes.DocumentSymbol[]> {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[]> {
return this._withAdapter(handle, OutlineAdapter, adapter => adapter.provideDocumentSymbols(URI.revive(resource), token));
}
// --- code lens
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
registerCodeLensProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
const handle = this._nextHandle();
const eventHandle = typeof provider.onDidChangeCodeLenses === 'function' ? this._nextHandle() : undefined;
this._adapter.set(handle, new CodeLensAdapter(this._documents, this._commands.converter, this._heapService, provider));
this._adapter.set(handle, new AdapterData(new CodeLensAdapter(this._documents, this._commands.converter, this._heapService, provider), extension));
this._proxy.$registerCodeLensSupport(handle, this._transformDocumentSelector(selector), eventHandle);
let result = this._createDisposable(handle);
@@ -970,140 +1029,150 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return result;
}
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Thenable<modes.ICodeLensSymbol[]> {
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.ICodeLensSymbol[]> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token));
}
$resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol, token: CancellationToken): Thenable<modes.ICodeLensSymbol> {
$resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(URI.revive(resource), symbol, token));
}
// --- declaration
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
const handle = this._addNewAdapter(new DefinitionAdapter(this._documents, provider));
this._proxy.$registerDeclaractionSupport(handle, this._transformDocumentSelector(selector));
registerDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
const handle = this._addNewAdapter(new DefinitionAdapter(this._documents, provider), extension);
this._proxy.$registerDefinitionSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DefinitionLink[]> {
return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position, token));
}
registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable {
const handle = this._addNewAdapter(new ImplementationAdapter(this._documents, provider));
registerDeclarationProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DeclarationProvider): vscode.Disposable {
const handle = this._addNewAdapter(new DeclarationAdapter(this._documents, provider), extension);
this._proxy.$registerDeclarationSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DefinitionLink[]> {
return this._withAdapter(handle, DeclarationAdapter, adapter => adapter.provideDeclaration(URI.revive(resource), position, token));
}
registerImplementationProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable {
const handle = this._addNewAdapter(new ImplementationAdapter(this._documents, provider), extension);
this._proxy.$registerImplementationSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DefinitionLink[]> {
return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position, token));
}
registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable {
const handle = this._addNewAdapter(new TypeDefinitionAdapter(this._documents, provider));
registerTypeDefinitionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable {
const handle = this._addNewAdapter(new TypeDefinitionAdapter(this._documents, provider), extension);
this._proxy.$registerTypeDefinitionSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DefinitionLink[]> {
return this._withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position, token));
}
// --- extra info
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: string): vscode.Disposable {
const handle = this._addNewAdapter(new HoverAdapter(this._documents, provider));
registerHoverProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.HoverProvider, extensionId?: string): vscode.Disposable {
const handle = this._addNewAdapter(new HoverAdapter(this._documents, provider), extension);
this._proxy.$registerHoverProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.Hover> {
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.Hover> {
return this._withAdapter(handle, HoverAdapter, adapter => adapter.provideHover(URI.revive(resource), position, token));
}
// --- occurrences
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
const handle = this._addNewAdapter(new DocumentHighlightAdapter(this._documents, provider));
registerDocumentHighlightProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
const handle = this._addNewAdapter(new DocumentHighlightAdapter(this._documents, provider), extension);
this._proxy.$registerDocumentHighlightProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.DocumentHighlight[]> {
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DocumentHighlight[]> {
return this._withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position, token));
}
// --- references
registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable {
const handle = this._addNewAdapter(new ReferenceAdapter(this._documents, provider));
registerReferenceProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable {
const handle = this._addNewAdapter(new ReferenceAdapter(this._documents, provider), extension);
this._proxy.$registerReferenceSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Thenable<modes.Location[]> {
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<modes.Location[]> {
return this._withAdapter(handle, ReferenceAdapter, adapter => adapter.provideReferences(URI.revive(resource), position, context, token));
}
// --- quick fix
registerCodeActionProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, extension?: IExtensionDescription, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension ? extension.id : ''));
registerCodeActionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider, this._logService, extension.id), extension);
this._proxy.$registerQuickFixSupport(handle, this._transformDocumentSelector(selector), metadata && metadata.providedCodeActionKinds ? metadata.providedCodeActionKinds.map(kind => kind.value) : undefined);
return this._createDisposable(handle);
}
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Thenable<CodeActionDto[]> {
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<CodeActionDto[]> {
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), rangeOrSelection, context, token));
}
// --- formatting
registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable {
const handle = this._addNewAdapter(new DocumentFormattingAdapter(this._documents, provider));
registerDocumentFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable {
const handle = this._addNewAdapter(new DocumentFormattingAdapter(this._documents, provider), extension);
this._proxy.$registerDocumentFormattingSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> {
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]> {
return this._withAdapter(handle, DocumentFormattingAdapter, adapter => adapter.provideDocumentFormattingEdits(URI.revive(resource), options, token));
}
registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable {
const handle = this._addNewAdapter(new RangeFormattingAdapter(this._documents, provider));
registerDocumentRangeFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable {
const handle = this._addNewAdapter(new RangeFormattingAdapter(this._documents, provider), extension);
this._proxy.$registerRangeFormattingSupport(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> {
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]> {
return this._withAdapter(handle, RangeFormattingAdapter, adapter => adapter.provideDocumentRangeFormattingEdits(URI.revive(resource), range, options, token));
}
registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, triggerCharacters: string[]): vscode.Disposable {
const handle = this._addNewAdapter(new OnTypeFormattingAdapter(this._documents, provider));
registerOnTypeFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, triggerCharacters: string[]): vscode.Disposable {
const handle = this._addNewAdapter(new OnTypeFormattingAdapter(this._documents, provider), extension);
this._proxy.$registerOnTypeFormattingSupport(handle, this._transformDocumentSelector(selector), triggerCharacters);
return this._createDisposable(handle);
}
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> {
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Promise<ISingleEditOperation[]> {
return this._withAdapter(handle, OnTypeFormattingAdapter, adapter => adapter.provideOnTypeFormattingEdits(URI.revive(resource), position, ch, options, token));
}
// --- navigate types
registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable {
const handle = this._addNewAdapter(new NavigateTypeAdapter(provider));
registerWorkspaceSymbolProvider(extension: IExtensionDescription, provider: vscode.WorkspaceSymbolProvider): vscode.Disposable {
const handle = this._addNewAdapter(new NavigateTypeAdapter(provider), extension);
this._proxy.$registerNavigateTypeSupport(handle);
return this._createDisposable(handle);
}
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Thenable<WorkspaceSymbolsDto> {
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise<WorkspaceSymbolsDto> {
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.provideWorkspaceSymbols(search, token));
}
$resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto, token: CancellationToken): Thenable<WorkspaceSymbolDto> {
$resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto, token: CancellationToken): Promise<WorkspaceSymbolDto> {
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.resolveWorkspaceSymbol(symbol, token));
}
@@ -1113,33 +1182,33 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- rename
registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider));
registerRenameProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
const handle = this._addNewAdapter(new RenameAdapter(this._documents, provider), extension);
this._proxy.$registerRenameSupport(handle, this._transformDocumentSelector(selector), RenameAdapter.supportsResolving(provider));
return this._createDisposable(handle);
}
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Thenable<WorkspaceEditDto> {
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<WorkspaceEditDto> {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.provideRenameEdits(URI.revive(resource), position, newName, token));
}
$resolveRenameLocation(handle: number, resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.RenameLocation> {
$resolveRenameLocation(handle: number, resource: URI, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation> {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.resolveRenameLocation(URI.revive(resource), position, token));
}
// --- suggestion
registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable {
const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider));
registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable {
const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider), extension);
this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider));
return this._createDisposable(handle);
}
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Thenable<SuggestResultDto> {
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise<SuggestResultDto> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token));
}
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.CompletionItem, token: CancellationToken): Thenable<modes.CompletionItem> {
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.CompletionItem, token: CancellationToken): Promise<modes.CompletionItem> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, suggestion, token));
}
@@ -1149,60 +1218,72 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- parameter hints
registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, metadataOrTriggerChars?: string[] | vscode.SignatureHelpProviderMetadata): vscode.Disposable {
registerSignatureHelpProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, metadataOrTriggerChars?: string[] | vscode.SignatureHelpProviderMetadata): vscode.Disposable {
const metadata: ISerializedSignatureHelpProviderMetadata = Array.isArray(metadataOrTriggerChars)
? { triggerCharacters: metadataOrTriggerChars, retriggerCharacters: [] }
: metadataOrTriggerChars;
const handle = this._addNewAdapter(new SignatureHelpAdapter(this._documents, provider));
const handle = this._addNewAdapter(new SignatureHelpAdapter(this._documents, provider), extension);
this._proxy.$registerSignatureHelpProvider(handle, this._transformDocumentSelector(selector), metadata);
return this._createDisposable(handle);
}
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Thenable<modes.SignatureHelp> {
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Promise<modes.SignatureHelp> {
return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, context, token));
}
// --- links
registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
const handle = this._addNewAdapter(new LinkProviderAdapter(this._documents, this._heapService, provider));
registerDocumentLinkProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
const handle = this._addNewAdapter(new LinkProviderAdapter(this._documents, this._heapService, provider), extension);
this._proxy.$registerDocumentLinkProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Thenable<modes.ILink[]> {
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.ILink[]> {
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(URI.revive(resource), token));
}
$resolveDocumentLink(handle: number, link: modes.ILink, token: CancellationToken): Thenable<modes.ILink> {
$resolveDocumentLink(handle: number, link: modes.ILink, token: CancellationToken): Promise<modes.ILink> {
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link, token));
}
registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable {
const handle = this._addNewAdapter(new ColorProviderAdapter(this._documents, provider));
registerColorProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable {
const handle = this._addNewAdapter(new ColorProviderAdapter(this._documents, provider), extension);
this._proxy.$registerDocumentColorProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): Thenable<IRawColorInfo[]> {
$provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): Promise<IRawColorInfo[]> {
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColors(URI.revive(resource), token));
}
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo, token: CancellationToken): Thenable<modes.IColorPresentation[]> {
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo, token: CancellationToken): Promise<modes.IColorPresentation[]> {
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token));
}
registerFoldingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable {
const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider));
registerFoldingRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable {
const handle = this._addNewAdapter(new FoldingProviderAdapter(this._documents, provider), extension);
this._proxy.$registerFoldingRangeProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideFoldingRanges(handle: number, resource: UriComponents, context: vscode.FoldingContext, token: CancellationToken): Thenable<modes.FoldingRange[]> {
$provideFoldingRanges(handle: number, resource: UriComponents, context: vscode.FoldingContext, token: CancellationToken): Promise<modes.FoldingRange[]> {
return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource), context, token));
}
// --- smart select
registerSelectionRangeProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.SelectionRangeProvider): vscode.Disposable {
const handle = this._addNewAdapter(new SelectionRangeAdapter(this._documents, provider), extension);
this._proxy.$registerSelectionRangeProvider(handle, this._transformDocumentSelector(selector));
return this._createDisposable(handle);
}
$provideSelectionRanges(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<IRange[]> {
return this._withAdapter(handle, SelectionRangeAdapter, adapter => adapter.provideSelectionRanges(URI.revive(resource), position, token));
}
// --- configuration
private static _serializeRegExp(regExp: RegExp): ISerializedRegExp {
@@ -1214,7 +1295,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
}
return {
pattern: regExp.source,
flags: (regExp.global ? 'g' : '') + (regExp.ignoreCase ? 'i' : '') + (regExp.multiline ? 'm' : ''),
flags: regExpFlags(regExp),
};
}

View File

@@ -20,11 +20,11 @@ export class ExtHostLanguages {
this._documents = documents;
}
getLanguages(): Thenable<string[]> {
getLanguages(): Promise<string[]> {
return this._proxy.$getLanguages();
}
changeLanguage(uri: vscode.Uri, languageId: string): Thenable<vscode.TextDocument> {
changeLanguage(uri: vscode.Uri, languageId: string): Promise<vscode.TextDocument> {
return this._proxy.$changeLanguage(uri, languageId).then(() => {
return this._documents.getDocumentData(uri).document;
});

View File

@@ -4,8 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { join } from 'vs/base/common/paths';
import { LogLevel } from 'vs/workbench/api/node/extHostTypes';
import { ILogService, DelegatedLogService } from 'vs/platform/log/common/log';
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';
import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions';

View File

@@ -20,9 +20,9 @@ export class ExtHostMessageService {
this._proxy = mainContext.getProxy(MainContext.MainThreadMessageService);
}
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string, rest: string[]): Thenable<string | undefined>;
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Thenable<vscode.MessageItem | undefined>;
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Thenable<string | vscode.MessageItem | undefined> {
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string, rest: string[]): Promise<string | undefined>;
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 };
let items: (string | vscode.MessageItem)[];

View File

@@ -14,7 +14,7 @@ import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
export abstract class AbstractExtHostOutputChannel extends Disposable implements vscode.OutputChannel {
readonly _id: Thenable<string>;
readonly _id: Promise<string>;
private readonly _name: string;
protected readonly _proxy: MainThreadOutputServiceShape;
private _disposed: boolean;

View File

@@ -7,11 +7,10 @@ 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 } from 'vs/platform/progress/common/progress';
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 { IProgressStep } from 'vs/workbench/services/progress/common/progress';
export class ExtHostProgress implements ExtHostProgressShape {
@@ -90,4 +89,4 @@ class ProgressCallback extends Progress<IProgressStep> {
throttledReport(p: IProgressStep): void {
this._proxy.$progressReport(this._handle, p);
}
}
}

View File

@@ -3,11 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { asThenable } from 'vs/base/common/async';
import { asPromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { InputBox, InputBoxOptions, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode';
@@ -29,23 +28,27 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
private _sessions = new Map<number, ExtHostQuickInput>();
private _instances = 0;
constructor(mainContext: IMainContext, workspace: ExtHostWorkspace, commands: ExtHostCommands) {
this._proxy = mainContext.getProxy(MainContext.MainThreadQuickOpen);
this._workspace = workspace;
this._commands = commands;
}
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, enableProposedApi: boolean, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Thenable<QuickPickItem[] | undefined>;
showQuickPick(itemsOrItemsPromise: string[] | Thenable<string[]>, enableProposedApi: boolean, options?: QuickPickOptions, token?: CancellationToken): Thenable<string | undefined>;
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, enableProposedApi: boolean, options?: QuickPickOptions, token?: CancellationToken): Thenable<QuickPickItem | undefined>;
showQuickPick(itemsOrItemsPromise: Item[] | Thenable<Item[]>, enableProposedApi: boolean, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable<Item | Item[] | undefined> {
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise<QuickPickItem[]>, enableProposedApi: boolean, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Promise<QuickPickItem[] | undefined>;
showQuickPick(itemsOrItemsPromise: string[] | Promise<string[]>, enableProposedApi: boolean, options?: QuickPickOptions, token?: CancellationToken): Promise<string | undefined>;
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Promise<QuickPickItem[]>, enableProposedApi: boolean, options?: QuickPickOptions, token?: CancellationToken): Promise<QuickPickItem | undefined>;
showQuickPick(itemsOrItemsPromise: Item[] | Promise<Item[]>, enableProposedApi: boolean, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Promise<Item | Item[] | undefined> {
// clear state from last invocation
this._onDidSelectItem = undefined;
const itemsPromise = <TPromise<Item[]>>TPromise.wrap(itemsOrItemsPromise);
const itemsPromise = <Promise<Item[]>>Promise.resolve(itemsOrItemsPromise);
const quickPickWidget = this._proxy.$show({
const instance = ++this._instances;
const quickPickWidget = this._proxy.$show(instance, {
placeHolder: options && options.placeHolder,
matchOnDescription: options && options.matchOnDescription,
matchOnDetail: options && options.matchOnDetail,
@@ -53,8 +56,11 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
canPickMany: options && options.canPickMany
}, token);
return TPromise.any(<TPromise<number | Item[]>[]>[quickPickWidget, itemsPromise]).then(values => {
if (values.key === '0') {
const widgetClosedMarker = {};
const widgetClosedPromise = quickPickWidget.then(() => widgetClosedMarker);
return Promise.race([widgetClosedPromise, itemsPromise]).then(result => {
if (result === widgetClosedMarker) {
return undefined;
}
@@ -77,9 +83,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
description = item.description;
detail = item.detail;
picked = item.picked;
if (enableProposedApi) {
alwaysShow = item.alwaysShow;
}
alwaysShow = item.alwaysShow;
}
pickItems.push({
label,
@@ -99,7 +103,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
}
// show items
this._proxy.$setItems(pickItems);
this._proxy.$setItems(instance, pickItems);
return quickPickWidget.then(handle => {
if (typeof handle === 'number') {
@@ -110,14 +114,14 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
return undefined;
});
});
}).then(null, err => {
}).then(void 0, err => {
if (isPromiseCanceledError(err)) {
return undefined;
}
this._proxy.$setError(err);
this._proxy.$setError(instance, err);
return TPromise.wrapError(err);
return Promise.reject(err);
});
}
@@ -129,33 +133,31 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
// ---- input
showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Thenable<string> {
showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Promise<string> {
// global validate fn used in callback below
this._validateInput = options && options.validateInput;
return this._proxy.$input(options, typeof this._validateInput === 'function', token)
.then(null, err => {
.then(void 0, err => {
if (isPromiseCanceledError(err)) {
return undefined;
}
this._proxy.$setError(err);
return TPromise.wrapError(err);
return Promise.reject(err);
});
}
$validateInput(input: string): Thenable<string> {
$validateInput(input: string): Promise<string> {
if (this._validateInput) {
return asThenable(() => this._validateInput(input));
return asPromise(() => this._validateInput(input));
}
return undefined;
}
// ---- workspace folder picker
showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token = CancellationToken.None): Thenable<WorkspaceFolder> {
showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions, token = CancellationToken.None): Promise<WorkspaceFolder> {
return this._commands.executeCommand('_workbench.pickWorkspaceFolder', [options]).then((selectedFolder: WorkspaceFolder) => {
if (!selectedFolder) {
return undefined;
@@ -477,7 +479,7 @@ class ExtHostQuickPick<T extends QuickPickItem> extends ExtHostQuickInput implem
private _selectedItems: T[] = [];
private _onDidChangeSelectionEmitter = new Emitter<T[]>();
constructor(proxy: MainThreadQuickOpenShape, extensionId: string, private _enableProposedApi: boolean, onDispose: () => void) {
constructor(proxy: MainThreadQuickOpenShape, extensionId: string, enableProposedApi: boolean, onDispose: () => void) {
super(proxy, extensionId, onDispose);
this._disposables.push(
this._onDidChangeActiveEmitter,
@@ -505,7 +507,7 @@ class ExtHostQuickPick<T extends QuickPickItem> extends ExtHostQuickInput implem
handle: i,
detail: item.detail,
picked: item.picked,
alwaysShow: this._enableProposedApi ? item.alwaysShow : undefined
alwaysShow: item.alwaysShow
}))
});
}

View File

@@ -4,11 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { URI, UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { Event, Emitter, once } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { debounce } from 'vs/base/common/decorators';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { asThenable } from 'vs/base/common/async';
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';
@@ -199,6 +198,18 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox {
this._proxy.$setValidationProviderIsEnabled(this._sourceControlHandle, !!fn);
}
private _visible: boolean = true;
get visible(): boolean {
return this._visible;
}
set visible(visible: boolean | undefined) {
visible = !!visible;
this._visible = visible;
this._proxy.$setInputBoxVisibility(this._sourceControlHandle, visible);
}
constructor(private _extension: IExtensionDescription, private _proxy: MainThreadSCMShape, private _sourceControlHandle: number) {
// noop
}
@@ -268,14 +279,14 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
return this._resourceStatesMap.get(handle);
}
$executeResourceCommand(handle: number): Thenable<void> {
$executeResourceCommand(handle: number): Promise<void> {
const command = this._resourceStatesCommandsMap.get(handle);
if (!command) {
return Promise.resolve(null);
}
return asThenable(() => this._commands.executeCommand(command.command, ...command.arguments));
return asPromise(() => this._commands.executeCommand(command.command, ...command.arguments));
}
_takeResourceStateSnapshot(): SCMRawResourceSplice[] {
@@ -467,7 +478,7 @@ class ExtHostSourceControl implements vscode.SourceControl {
this.eventuallyUpdateResourceStates();
});
once(group.onDidDispose)(() => {
Event.once(group.onDidDispose)(() => {
this.updatedResourceGroups.delete(group);
updateListener.dispose();
this._groups.delete(group.handle);
@@ -597,73 +608,73 @@ export class ExtHostSCM implements ExtHostSCMShape {
return inputBox;
}
$provideOriginalResource(sourceControlHandle: number, uriComponents: UriComponents, token: CancellationToken): Thenable<UriComponents> {
$provideOriginalResource(sourceControlHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise<UriComponents> {
const uri = URI.revive(uriComponents);
this.logService.trace('ExtHostSCM#$provideOriginalResource', sourceControlHandle, uri.toString());
const sourceControl = this._sourceControls.get(sourceControlHandle);
if (!sourceControl || !sourceControl.quickDiffProvider) {
return TPromise.as(null);
return Promise.resolve(null);
}
return asThenable(() => sourceControl.quickDiffProvider.provideOriginalResource(uri, token));
return asPromise(() => sourceControl.quickDiffProvider.provideOriginalResource(uri, token));
}
$onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise<void> {
$onInputBoxValueChange(sourceControlHandle: number, value: string): Promise<void> {
this.logService.trace('ExtHostSCM#$onInputBoxValueChange', sourceControlHandle);
const sourceControl = this._sourceControls.get(sourceControlHandle);
if (!sourceControl) {
return TPromise.as(null);
return Promise.resolve(null);
}
sourceControl.inputBox.$onInputBoxValueChange(value);
return TPromise.as(null);
return Promise.resolve(null);
}
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): Thenable<void> {
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): Promise<void> {
this.logService.trace('ExtHostSCM#$executeResourceCommand', sourceControlHandle, groupHandle, handle);
const sourceControl = this._sourceControls.get(sourceControlHandle);
if (!sourceControl) {
return TPromise.as(null);
return Promise.resolve(null);
}
const group = sourceControl.getResourceGroup(groupHandle);
if (!group) {
return TPromise.as(null);
return Promise.resolve(null);
}
return group.$executeResourceCommand(handle);
}
$validateInput(sourceControlHandle: number, value: string, cursorPosition: number): Thenable<[string, number] | undefined> {
$validateInput(sourceControlHandle: number, value: string, cursorPosition: number): Promise<[string, number] | undefined> {
this.logService.trace('ExtHostSCM#$validateInput', sourceControlHandle);
const sourceControl = this._sourceControls.get(sourceControlHandle);
if (!sourceControl) {
return TPromise.as(undefined);
return Promise.resolve(undefined);
}
if (!sourceControl.inputBox.validateInput) {
return TPromise.as(undefined);
return Promise.resolve(undefined);
}
return asThenable(() => sourceControl.inputBox.validateInput(value, cursorPosition)).then(result => {
return asPromise(() => sourceControl.inputBox.validateInput(value, cursorPosition)).then(result => {
if (!result) {
return TPromise.as(undefined);
return Promise.resolve(undefined);
}
return TPromise.as<[string, number]>([result.message, result.type]);
return Promise.resolve<[string, number]>([result.message, result.type]);
});
}
$setSelectedSourceControls(selectedSourceControlHandles: number[]): Thenable<void> {
$setSelectedSourceControls(selectedSourceControlHandles: number[]): Promise<void> {
this.logService.trace('ExtHostSCM#$setSelectedSourceControls', selectedSourceControlHandles);
const set = new Set<number>();
@@ -697,6 +708,6 @@ export class ExtHostSCM implements ExtHostSCMShape {
});
this._selectedSourceControlHandles = set;
return TPromise.as(null);
return Promise.resolve(null);
}
}

View File

@@ -7,136 +7,18 @@ 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 { toErrorMessage } from 'vs/base/common/errorMessage';
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 { TPromise } from 'vs/base/common/winjs.base';
import { compareItemsByScore, IItemAccessor, prepareQuery, ScorerCache } from 'vs/base/parts/quickopen/common/quickOpenScorer';
import { ICachedSearchStats, IFileIndexProviderStats, IFileMatch, IFileSearchStats, IFolderQuery, IRawSearchQuery, ISearchCompleteStats, ISearchQuery } from 'vs/platform/search/common/search';
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';
export interface IInternalFileMatch {
base: URI;
original?: URI;
relativePath?: string; // Not present for extraFiles or absolute path matches
basename: string;
size?: number;
}
/**
* Computes the patterns that the provider handles. Discards sibling clauses and 'false' patterns
*/
export function resolvePatternsForProvider(globalPattern: glob.IExpression, folderPattern: glob.IExpression): string[] {
const merged = {
...(globalPattern || {}),
...(folderPattern || {})
};
return Object.keys(merged)
.filter(key => {
const value = merged[key];
return typeof value === 'boolean' && value;
});
}
export class QueryGlobTester {
private _excludeExpression: glob.IExpression;
private _parsedExcludeExpression: glob.ParsedExpression;
private _parsedIncludeExpression: glob.ParsedExpression;
constructor(config: ISearchQuery, folderQuery: IFolderQuery) {
this._excludeExpression = {
...(config.excludePattern || {}),
...(folderQuery.excludePattern || {})
};
this._parsedExcludeExpression = glob.parse(this._excludeExpression);
// Empty includeExpression means include nothing, so no {} shortcuts
let includeExpression: glob.IExpression = config.includePattern;
if (folderQuery.includePattern) {
if (includeExpression) {
includeExpression = {
...includeExpression,
...folderQuery.includePattern
};
} else {
includeExpression = folderQuery.includePattern;
}
}
if (includeExpression) {
this._parsedIncludeExpression = glob.parse(includeExpression);
}
}
/**
* Guaranteed sync - siblingsFn should not return a promise.
*/
public includedInQuerySync(testPath: string, basename?: string, hasSibling?: (name: string) => boolean): boolean {
if (this._parsedExcludeExpression && this._parsedExcludeExpression(testPath, basename, hasSibling)) {
return false;
}
if (this._parsedIncludeExpression && !this._parsedIncludeExpression(testPath, basename, hasSibling)) {
return false;
}
return true;
}
/**
* Guaranteed async.
*/
public includedInQuery(testPath: string, basename?: string, hasSibling?: (name: string) => boolean | TPromise<boolean>): TPromise<boolean> {
const excludeP = this._parsedExcludeExpression ?
TPromise.as(this._parsedExcludeExpression(testPath, basename, hasSibling)).then(result => !!result) :
TPromise.wrap(false);
return excludeP.then(excluded => {
if (excluded) {
return false;
}
return this._parsedIncludeExpression ?
TPromise.as(this._parsedIncludeExpression(testPath, basename, hasSibling)).then(result => !!result) :
TPromise.wrap(true);
}).then(included => {
return included;
});
}
public hasSiblingExcludeClauses(): boolean {
return hasSiblingClauses(this._excludeExpression);
}
}
function hasSiblingClauses(pattern: glob.IExpression): boolean {
for (let key in pattern) {
if (typeof pattern[key] !== 'boolean') {
return true;
}
}
return false;
}
export interface IDirectoryEntry {
base: URI;
relativePath: string;
basename: string;
}
export interface IDirectoryTree {
rootEntries: IDirectoryEntry[];
pathToEntries: { [relativePath: string]: IDirectoryEntry[] };
}
interface IInternalSearchComplete<T = IFileSearchStats> {
limitHit: boolean;
results: IInternalFileMatch[];
@@ -160,7 +42,7 @@ export class FileIndexSearchEngine {
private globalExcludePattern: glob.ParsedExpression;
constructor(private config: ISearchQuery, private provider: vscode.FileIndexProvider) {
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;
@@ -182,15 +64,11 @@ export class FileIndexSearchEngine {
this.activeCancellationTokens = new Set();
}
public search(_onResult: (match: IInternalFileMatch) => void): TPromise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }> {
if (this.config.folderQueries.length !== 1) {
throw new Error('Searches just one folder');
}
public search(_onResult: (match: IInternalFileMatch) => void): Promise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }> {
// Searches a single folder
const folderQuery = this.config.folderQueries[0];
return new TPromise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }>((resolve, reject) => {
return new Promise<{ isLimitHit: boolean, stats: IFileIndexProviderStats }>((resolve, reject) => {
const onResult = (match: IInternalFileMatch) => {
this.resultCount++;
_onResult(match);
@@ -215,21 +93,31 @@ export class FileIndexSearchEngine {
});
}
return this.searchInFolder(folderQuery, _onResult)
.then(stats => {
resolve({
isLimitHit: this.isLimitHit,
stats
});
}, (err: Error) => {
reject(new Error(toErrorMessage(err)));
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): TPromise<IFileIndexProviderStats> {
private searchInFolder(fq: IFolderQuery<URI>, onResult: (match: IInternalFileMatch) => void): Promise<IFileIndexProviderStats> {
let cancellation = new CancellationTokenSource();
return new TPromise((resolve, reject) => {
return new Promise((resolve, reject) => {
const options = this.getSearchOptionsForFolder(fq);
const tree = this.initDirectoryTree();
@@ -257,7 +145,7 @@ export class FileIndexSearchEngine {
let providerSW: StopWatch;
let providerTime: number;
let fileWalkTime: number;
new TPromise(resolve => process.nextTick(resolve))
new Promise(resolve => process.nextTick(resolve))
.then(() => {
this.activeCancellationTokens.add(cancellation);
providerSW = StopWatch.create();
@@ -301,9 +189,9 @@ export class FileIndexSearchEngine {
folder: fq.folder,
excludes,
includes,
useIgnoreFiles: !this.config.disregardIgnoreFiles,
useGlobalIgnoreFiles: !this.config.disregardGlobalIgnoreFiles,
followSymlinks: !this.config.ignoreSymlinks
useIgnoreFiles: !fq.disregardIgnoreFiles,
useGlobalIgnoreFiles: !fq.disregardGlobalIgnoreFiles,
followSymlinks: !fq.ignoreSymlinks
};
}
@@ -415,7 +303,7 @@ export class FileIndexSearchManager {
private readonly folderCacheKeys = new Map<string, Set<string>>();
public fileSearch(config: ISearchQuery, provider: vscode.FileIndexProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise<ISearchCompleteStats> {
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) {
@@ -452,7 +340,7 @@ export class FileIndexSearchManager {
});
}
private getFolderCacheKey(config: ISearchQuery): string {
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)) {
@@ -470,7 +358,7 @@ export class FileIndexSearchManager {
};
}
private doSortedSearch(engine: FileIndexSearchEngine, config: ISearchQuery, token: CancellationToken): TPromise<IInternalSearchComplete> {
private doSortedSearch(engine: FileIndexSearchEngine, config: IFileQuery, token: CancellationToken): Promise<IInternalSearchComplete> {
let allResultsPromise = createCancelablePromise<IInternalSearchComplete<IFileIndexProviderStats>>(token => {
return this.doSearch(engine, token);
});
@@ -492,7 +380,7 @@ export class FileIndexSearchManager {
allResultsPromise = this.preventCancellation(allResultsPromise);
}
return TPromise.wrap<IInternalSearchComplete>(
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();
@@ -524,7 +412,7 @@ export class FileIndexSearchManager {
return this.caches[cacheKey] = new Cache();
}
private trySortedSearchFromCache(config: ISearchQuery, token: CancellationToken): TPromise<IInternalSearchComplete> {
private trySortedSearchFromCache(config: IFileQuery, token: CancellationToken): Promise<IInternalSearchComplete> {
const folderCacheKey = this.getFolderCacheKey(config);
const cache = folderCacheKey && this.caches[folderCacheKey];
if (!cache) {
@@ -558,7 +446,7 @@ export class FileIndexSearchManager {
return undefined;
}
private sortResults(config: IRawSearchQuery, results: IInternalFileMatch[], scorerCache: ScorerCache, token: CancellationToken): TPromise<IInternalFileMatch[]> {
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
@@ -580,7 +468,7 @@ export class FileIndexSearchManager {
}
}
private getResultsFromCache(cache: Cache, searchValue: string, token: CancellationToken): TPromise<IInternalSearchComplete<ICachedSearchStats>> {
private getResultsFromCache(cache: Cache, searchValue: string, token: CancellationToken): Promise<IInternalSearchComplete<ICachedSearchStats>> {
const cacheLookupSW = StopWatch.create();
if (path.isAbsolute(searchValue)) {
@@ -614,7 +502,7 @@ export class FileIndexSearchManager {
const cacheLookupTime = cacheLookupSW.elapsed();
const cacheFilterSW = StopWatch.create();
return new TPromise<IInternalSearchComplete<ICachedSearchStats>>((c, e) => {
return new Promise<IInternalSearchComplete<ICachedSearchStats>>((c, e) => {
token.onCancellationRequested(() => e(canceled()));
cacheRow.promise.then(complete => {
@@ -650,7 +538,7 @@ export class FileIndexSearchManager {
});
}
private doSearch(engine: FileIndexSearchEngine, token: CancellationToken): TPromise<IInternalSearchComplete<IFileIndexProviderStats>> {
private doSearch(engine: FileIndexSearchEngine, token: CancellationToken): Promise<IInternalSearchComplete<IFileIndexProviderStats>> {
token.onCancellationRequested(() => engine.cancel());
const results: IInternalFileMatch[] = [];
const onResult = match => results.push(match);
@@ -664,9 +552,9 @@ export class FileIndexSearchManager {
});
}
public clearCache(cacheKey: string): TPromise<void> {
public clearCache(cacheKey: string): void {
if (!this.folderCacheKeys.has(cacheKey)) {
return TPromise.wrap(undefined);
return undefined;
}
const expandedKeys = this.folderCacheKeys.get(cacheKey);
@@ -674,7 +562,7 @@ export class FileIndexSearchManager {
this.folderCacheKeys.delete(cacheKey);
return TPromise.as(undefined);
return undefined;
}
private preventCancellation<C>(promise: CancelablePromise<C>): CancelablePromise<C> {

View File

@@ -3,18 +3,20 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import * as glob from 'vs/base/common/glob';
import { toDisposable } from 'vs/base/common/lifecycle';
import * as resources from 'vs/base/common/resources';
import { StopWatch } from 'vs/base/common/stopwatch';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import * as extfs from 'vs/base/node/extfs';
import { IFileMatch, IFileSearchProviderStats, IFolderQuery, IPatternInfo, IRawSearchQuery, ISearchCompleteStats, ISearchQuery, ITextSearchResult } from 'vs/platform/search/common/search';
import { FileIndexSearchManager, IDirectoryEntry, IDirectoryTree, IInternalFileMatch, QueryGlobTester, resolvePatternsForProvider } from 'vs/workbench/api/node/extHostSearch.fileIndex';
import { ILogService } from 'vs/platform/log/common/log';
import { IFileQuery, IFolderQuery, IRawFileQuery, IRawQuery, IRawTextQuery, ISearchCompleteStats, ITextQuery } from 'vs/platform/search/common/search';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { FileIndexSearchManager } from 'vs/workbench/api/node/extHostSearch.fileIndex';
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';
@@ -25,18 +27,26 @@ export interface ISchemeTransformer {
export class ExtHostSearch implements ExtHostSearchShape {
private readonly _proxy: MainThreadSearchShape;
private readonly _fileSearchProvider = new Map<number, vscode.FileSearchProvider>();
private readonly _textSearchProvider = new Map<number, vscode.TextSearchProvider>();
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 _fileSearchManager: FileSearchManager;
private _fileIndexSearchManager: FileIndexSearchManager;
constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer, private _extfs = extfs) {
constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer, private _logService: ILogService, configService: ExtHostConfiguration, private _extfs = extfs) {
this._proxy = mainContext.getProxy(MainContext.MainThreadSearch);
this._fileSearchManager = new FileSearchManager();
this._fileIndexSearchManager = new FileIndexSearchManager();
registerEHProviders(this, _logService, configService);
}
private _transformScheme(scheme: string): string {
@@ -46,72 +56,138 @@ export class ExtHostSearch implements ExtHostSearchShape {
return scheme;
}
registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider) {
const handle = this._handlePool++;
this._fileSearchProvider.set(handle, provider);
this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._fileSearchProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
});
}
registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider): IDisposable {
if (this._textSearchUsedSchemes.has(scheme)) {
throw new Error(`a provider for the scheme '${scheme}' is already registered`);
}
registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider) {
this._textSearchUsedSchemes.add(scheme);
const handle = this._handlePool++;
this._textSearchProvider.set(handle, provider);
this._proxy.$registerTextSearchProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._textSearchUsedSchemes.delete(scheme);
this._textSearchProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
});
}
registerFileIndexProvider(scheme: string, provider: vscode.FileIndexProvider) {
registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider): IDisposable {
if (this._fileSearchUsedSchemes.has(scheme)) {
throw new Error(`a provider for the scheme '${scheme}' is already registered`);
}
this._fileSearchUsedSchemes.add(scheme);
const handle = this._handlePool++;
this._fileSearchProvider.set(handle, provider);
this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._fileSearchUsedSchemes.delete(scheme);
this._fileSearchProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
});
}
registerInternalFileSearchProvider(scheme: string, provider: SearchService): IDisposable {
const handle = this._handlePool++;
this._internalFileSearchProvider = provider;
this._internalFileSearchHandle = handle;
this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme));
return toDisposable(() => {
this._internalFileSearchProvider = null;
this._proxy.$unregisterProvider(handle);
});
}
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: IRawSearchQuery, token: CancellationToken): Thenable<ISearchCompleteStats> {
const provider = this._fileSearchProvider.get(handle);
$provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: CancellationToken): Promise<ISearchCompleteStats> {
const query = reviveQuery(rawQuery);
if (provider) {
return this._fileSearchManager.fileSearch(query, provider, batch => {
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
}, token);
if (handle === this._internalFileSearchHandle) {
return this.doInternalFileSearch(handle, session, query, 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);
const provider = this._fileSearchProvider.get(handle);
if (provider) {
return this._fileSearchManager.fileSearch(query, provider, batch => {
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);
}
}
}
$clearCache(cacheKey: string): Thenable<void> {
// Actually called once per provider.
// Only relevant to file index search.
return this._fileIndexSearchManager.clearCache(cacheKey);
private doInternalFileSearch(handle: number, session: number, rawQuery: IFileQuery, token: CancellationToken): Promise<ISearchCompleteStats> {
const onResult = (ev) => {
if (isSerializedFileMatch(ev)) {
ev = [ev];
}
if (Array.isArray(ev)) {
this._proxy.$handleFileMatch(handle, session, ev.map(m => URI.file(m.path)));
return;
}
if (ev.message) {
this._logService.debug('ExtHostSearch', ev.message);
}
};
return this._internalFileSearchProvider.doFileSearch(rawQuery, onResult, token);
}
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, rawQuery: IRawSearchQuery, token: CancellationToken): Thenable<ISearchCompleteStats> {
$clearCache(cacheKey: string): Promise<void> {
if (this._internalFileSearchProvider) {
this._internalFileSearchProvider.clearCache(cacheKey);
}
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 TPromise.as(undefined);
return Promise.resolve(undefined);
}
const query = reviveQuery(rawQuery);
const engine = new TextSearchEngine(pattern, query, provider, this._extfs);
const engine = new TextSearchManager(query, provider, this._extfs);
return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress), token);
}
}
function reviveQuery(rawQuery: IRawSearchQuery): ISearchQuery {
function registerEHProviders(extHostSearch: ExtHostSearch, logService: ILogService, configService: ExtHostConfiguration) {
if (configService.getConfiguration('searchRipgrep').enable || configService.getConfiguration('search').runInExtensionHost) {
const outputChannel = new OutputChannel(logService);
extHostSearch.registerTextSearchProvider('file', new RipgrepSearchProvider(outputChannel));
extHostSearch.registerInternalFileSearchProvider('file', new SearchService());
}
}
function reviveQuery<U extends IRawQuery>(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery {
return {
...rawQuery,
...<any>rawQuery, // TODO
...{
folderQueries: rawQuery.folderQueries && rawQuery.folderQueries.map(reviveFolderQuery),
extraFileResources: rawQuery.extraFileResources && rawQuery.extraFileResources.map(components => URI.revive(components))
@@ -126,603 +202,3 @@ function reviveFolderQuery(rawFolderQuery: IFolderQuery<UriComponents>): IFolder
};
}
class TextSearchResultsCollector {
private _batchedCollector: BatchedCollector<IFileMatch>;
private _currentFolderIdx: number;
private _currentUri: URI;
private _currentFileMatch: IFileMatch;
constructor(private _onResult: (result: IFileMatch[]) => void) {
this._batchedCollector = new BatchedCollector<IFileMatch>(512, items => this.sendItems(items));
}
add(data: vscode.TextSearchResult, folderIdx: number): void {
// Collects TextSearchResults into IInternalFileMatches and collates using BatchedCollector.
// This is efficient for ripgrep which sends results back one file at a time. It wouldn't be efficient for other search
// providers that send results in random order. We could do this step afterwards instead.
if (this._currentFileMatch && (this._currentFolderIdx !== folderIdx || resources.isEqual(this._currentUri, data.uri))) {
this.pushToCollector();
this._currentFileMatch = null;
}
if (!this._currentFileMatch) {
this._currentFileMatch = {
resource: data.uri,
matches: []
};
}
this._currentFileMatch.matches.push(extensionResultToFrontendResult(data));
}
private pushToCollector(): void {
const size = this._currentFileMatch ?
this._currentFileMatch.matches.length :
0;
this._batchedCollector.addItem(this._currentFileMatch, size);
}
flush(): void {
this.pushToCollector();
this._batchedCollector.flush();
}
private sendItems(items: IFileMatch[]): void {
this._onResult(items);
}
}
function extensionResultToFrontendResult(data: vscode.TextSearchResult): ITextSearchResult {
return {
preview: {
match: {
startLineNumber: data.preview.match.start.line,
startColumn: data.preview.match.start.character,
endLineNumber: data.preview.match.end.line,
endColumn: data.preview.match.end.character
},
text: data.preview.text
},
range: {
startLineNumber: data.range.start.line,
startColumn: data.range.start.character,
endLineNumber: data.range.end.line,
endColumn: data.range.end.character
}
};
}
/**
* Collects items that have a size - before the cumulative size of collected items reaches START_BATCH_AFTER_COUNT, the callback is called for every
* set of items collected.
* But after that point, the callback is called with batches of maxBatchSize.
* If the batch isn't filled within some time, the callback is also called.
*/
class BatchedCollector<T> {
private static readonly TIMEOUT = 4000;
// After START_BATCH_AFTER_COUNT items have been collected, stop flushing on timeout
private static readonly START_BATCH_AFTER_COUNT = 50;
private totalNumberCompleted = 0;
private batch: T[] = [];
private batchSize = 0;
private timeoutHandle: any;
constructor(private maxBatchSize: number, private cb: (items: T[]) => void) {
}
addItem(item: T, size: number): void {
if (!item) {
return;
}
this.addItemToBatch(item, size);
}
addItems(items: T[], size: number): void {
if (!items) {
return;
}
if (this.maxBatchSize > 0) {
this.addItemsToBatch(items, size);
} else {
this.cb(items);
}
}
private addItemToBatch(item: T, size: number): void {
this.batch.push(item);
this.batchSize += size;
this.onUpdate();
}
private addItemsToBatch(item: T[], size: number): void {
this.batch = this.batch.concat(item);
this.batchSize += size;
this.onUpdate();
}
private onUpdate(): void {
if (this.totalNumberCompleted < BatchedCollector.START_BATCH_AFTER_COUNT) {
// Flush because we aren't batching yet
this.flush();
} else if (this.batchSize >= this.maxBatchSize) {
// Flush because the batch is full
this.flush();
} else if (!this.timeoutHandle) {
// No timeout running, start a timeout to flush
this.timeoutHandle = setTimeout(() => {
this.flush();
}, BatchedCollector.TIMEOUT);
}
}
flush(): void {
if (this.batchSize) {
this.totalNumberCompleted += this.batchSize;
this.cb(this.batch);
this.batch = [];
this.batchSize = 0;
if (this.timeoutHandle) {
clearTimeout(this.timeoutHandle);
this.timeoutHandle = 0;
}
}
}
}
class TextSearchEngine {
private collector: TextSearchResultsCollector;
private isLimitHit: boolean;
private resultCount = 0;
constructor(private pattern: IPatternInfo, private config: ISearchQuery, private provider: vscode.TextSearchProvider, private _extfs: typeof extfs) {
}
public search(onProgress: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise<ISearchCompleteStats> {
const folderQueries = this.config.folderQueries;
const tokenSource = new CancellationTokenSource();
token.onCancellationRequested(() => tokenSource.cancel());
return new TPromise<ISearchCompleteStats>((resolve, reject) => {
this.collector = new TextSearchResultsCollector(onProgress);
let isCanceled = false;
const onResult = (match: vscode.TextSearchResult, folderIdx: number) => {
if (isCanceled) {
return;
}
if (this.resultCount >= this.config.maxResults) {
this.isLimitHit = true;
isCanceled = true;
tokenSource.cancel();
}
if (!this.isLimitHit) {
this.resultCount++;
this.collector.add(match, folderIdx);
}
};
// For each root folder
TPromise.join(folderQueries.map((fq, i) => {
return this.searchInFolder(fq, r => onResult(r, i), tokenSource.token);
})).then(results => {
tokenSource.dispose();
this.collector.flush();
const someFolderHitLImit = results.some(result => result && result.limitHit);
resolve({
limitHit: this.isLimitHit || someFolderHitLImit,
stats: {
type: 'textSearchProvider'
}
});
}, (errs: Error[]) => {
tokenSource.dispose();
const errMsg = errs
.map(err => toErrorMessage(err))
.filter(msg => !!msg)[0];
reject(new Error(errMsg));
});
});
}
private searchInFolder(folderQuery: IFolderQuery<URI>, onResult: (result: vscode.TextSearchResult) => void, token: CancellationToken): TPromise<vscode.TextSearchComplete> {
const queryTester = new QueryGlobTester(this.config, folderQuery);
const testingPs = [];
const progress = {
report: (result: vscode.TextSearchResult) => {
const hasSibling = folderQuery.folder.scheme === 'file' && glob.hasSiblingPromiseFn(() => {
return this.readdir(path.dirname(result.uri.fsPath));
});
const relativePath = path.relative(folderQuery.folder.fsPath, result.uri.fsPath);
testingPs.push(
queryTester.includedInQuery(relativePath, path.basename(relativePath), hasSibling)
.then(included => {
if (included) {
onResult(result);
}
}));
}
};
const searchOptions = this.getSearchOptionsForFolder(folderQuery);
return new TPromise(resolve => process.nextTick(resolve))
.then(() => this.provider.provideTextSearchResults(patternInfoToQuery(this.pattern), searchOptions, progress, token))
.then(result => {
return TPromise.join(testingPs)
.then(() => result);
});
}
private readdir(dirname: string): TPromise<string[]> {
return new TPromise((resolve, reject) => {
this._extfs.readdir(dirname, (err, files) => {
if (err) {
return reject(err);
}
resolve(files);
});
});
}
private getSearchOptionsForFolder(fq: IFolderQuery<URI>): vscode.TextSearchOptions {
const includes = resolvePatternsForProvider(this.config.includePattern, fq.includePattern);
const excludes = resolvePatternsForProvider(this.config.excludePattern, fq.excludePattern);
return {
folder: URI.from(fq.folder),
excludes,
includes,
useIgnoreFiles: !this.config.disregardIgnoreFiles,
useGlobalIgnoreFiles: !this.config.disregardGlobalIgnoreFiles,
followSymlinks: !this.config.ignoreSymlinks,
encoding: this.config.fileEncoding,
maxFileSize: this.config.maxFileSize,
maxResults: this.config.maxResults,
previewOptions: this.config.previewOptions
};
}
}
function patternInfoToQuery(patternInfo: IPatternInfo): vscode.TextSearchQuery {
return <vscode.TextSearchQuery>{
isCaseSensitive: patternInfo.isCaseSensitive || false,
isRegExp: patternInfo.isRegExp || false,
isWordMatch: patternInfo.isWordMatch || false,
pattern: patternInfo.pattern
};
}
class FileSearchEngine {
private filePattern: string;
private includePattern: glob.ParsedExpression;
private maxResults: number;
private exists: boolean;
private isLimitHit: boolean;
private resultCount: number;
private isCanceled: boolean;
private activeCancellationTokens: Set<CancellationTokenSource>;
private globalExcludePattern: glob.ParsedExpression;
constructor(private config: ISearchQuery, private provider: vscode.FileSearchProvider) {
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>();
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): TPromise<IInternalSearchComplete> {
const folderQueries = this.config.folderQueries;
return new TPromise((resolve, reject) => {
const onResult = (match: IInternalFileMatch) => {
this.resultCount++;
_onResult(match);
};
// Support that the file pattern is a full path to a file that exists
if (this.isCanceled) {
return resolve({ limitHit: this.isLimitHit });
}
// 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 });
});
}
// For each root folder
TPromise.join(folderQueries.map(fq => {
return this.searchInFolder(fq, onResult);
})).then(stats => {
resolve({
limitHit: this.isLimitHit,
stats: stats[0] // Only looking at single-folder workspace stats...
});
}, (errs: Error[]) => {
const errMsg = errs
.map(err => toErrorMessage(err))
.filter(msg => !!msg)[0];
reject(new Error(errMsg));
});
});
}
private searchInFolder(fq: IFolderQuery<URI>, onResult: (match: IInternalFileMatch) => void): TPromise<IFileSearchProviderStats> {
let cancellation = new CancellationTokenSource();
return new TPromise((resolve, reject) => {
const options = this.getSearchOptionsForFolder(fq);
const tree = this.initDirectoryTree();
const queryTester = new QueryGlobTester(this.config, fq);
const noSiblingsClauses = !queryTester.hasSiblingExcludeClauses();
let providerSW: StopWatch;
new TPromise(_resolve => process.nextTick(_resolve))
.then(() => {
this.activeCancellationTokens.add(cancellation);
providerSW = StopWatch.create();
return this.provider.provideFileSearchResults(
{
pattern: this.config.filePattern || ''
},
options,
cancellation.token);
})
.then(results => {
const providerTime = providerSW.elapsed();
const postProcessSW = StopWatch.create();
if (this.isCanceled) {
return null;
}
if (results) {
results.forEach(result => {
const relativePath = path.relative(fq.folder.fsPath, result.fsPath);
if (noSiblingsClauses) {
const basename = path.basename(result.fsPath);
this.matchFile(onResult, { base: fq.folder, relativePath, basename });
return;
}
// TODO: Optimize siblings clauses with ripgrep here.
this.addDirectoryEntries(tree, fq.folder, relativePath, onResult);
});
}
this.activeCancellationTokens.delete(cancellation);
if (this.isCanceled) {
return null;
}
this.matchDirectoryTree(tree, queryTester, onResult);
return <IFileSearchProviderStats>{
providerTime,
postProcessTime: postProcessSW.elapsed()
};
}).then(
stats => {
cancellation.dispose();
resolve(stats);
},
err => {
cancellation.dispose();
reject(err);
});
});
}
private getSearchOptionsForFolder(fq: IFolderQuery<URI>): vscode.FileSearchOptions {
const includes = resolvePatternsForProvider(this.config.includePattern, fq.includePattern);
const excludes = resolvePatternsForProvider(this.config.excludePattern, fq.excludePattern);
return {
folder: fq.folder,
excludes,
includes,
useIgnoreFiles: !this.config.disregardIgnoreFiles,
useGlobalIgnoreFiles: !this.config.disregardGlobalIgnoreFiles,
followSymlinks: !this.config.ignoreSymlinks,
maxResults: this.config.maxResults
};
}
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[]) {
const hasSibling = glob.hasSiblingFn(() => entries.map(entry => entry.basename));
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
if (!queryTester.includedInQuerySync(relativePath, basename, filePattern !== basename ? hasSibling : undefined)) {
continue;
}
const sub = pathToEntries[relativePath];
if (sub) {
matchDirectory(sub);
} else {
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.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);
}
}
}
}
interface IInternalSearchComplete {
limitHit: boolean;
stats?: IFileSearchProviderStats;
}
class FileSearchManager {
private static readonly BATCH_SIZE = 512;
fileSearch(config: ISearchQuery, provider: vscode.FileSearchProvider, onBatch: (matches: IFileMatch[]) => void, token: CancellationToken): TPromise<ISearchCompleteStats> {
const engine = new FileSearchEngine(config, provider);
let resultCount = 0;
const onInternalResult = (batch: IInternalFileMatch[]) => {
resultCount += batch.length;
onBatch(batch.map(m => this.rawMatchToSearchItem(m)));
};
return this.doSearch(engine, FileSearchManager.BATCH_SIZE, onInternalResult, token).then(
result => {
return <ISearchCompleteStats>{
limitHit: result.limitHit,
stats: {
fromCache: false,
type: 'fileSearchProvider',
resultCount,
detailStats: result.stats
}
};
});
}
private rawMatchToSearchItem(match: IInternalFileMatch): IFileMatch {
if (match.relativePath) {
return {
resource: resources.joinPath(match.base, match.relativePath)
};
} else {
// extraFileResources
return {
resource: match.base
};
}
}
private doSearch(engine: FileSearchEngine, batchSize: number, onResultBatch: (matches: IInternalFileMatch[]) => void, token: CancellationToken): TPromise<IInternalSearchComplete> {
token.onCancellationRequested(() => {
engine.cancel();
});
const _onResult = match => {
if (match) {
batch.push(match);
if (batchSize > 0 && batch.length >= batchSize) {
onResultBatch(batch);
batch = [];
}
}
};
let batch: IInternalFileMatch[] = [];
return engine.search(_onResult).then(result => {
if (batch.length) {
onResultBatch(batch);
}
return result;
}, error => {
if (batch.length) {
onResultBatch(batch);
}
return TPromise.wrapError(error);
});
}
}

View File

@@ -3,21 +3,35 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainContext, MainThreadStorageShape, IMainContext } from './extHost.protocol';
import { MainContext, MainThreadStorageShape, IMainContext, ExtHostStorageShape } from './extHost.protocol';
import { Emitter } from 'vs/base/common/event';
export class ExtHostStorage {
export interface IStorageChangeEvent {
shared: boolean;
key: string;
value: object;
}
export class ExtHostStorage implements ExtHostStorageShape {
private _proxy: MainThreadStorageShape;
private _onDidChangeStorage = new Emitter<IStorageChangeEvent>();
readonly onDidChangeStorage = this._onDidChangeStorage.event;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadStorage);
}
getValue<T>(shared: boolean, key: string, defaultValue?: T): Thenable<T> {
getValue<T>(shared: boolean, key: string, defaultValue?: T): Promise<T> {
return this._proxy.$getValue<T>(shared, key).then(value => value || defaultValue);
}
setValue(shared: boolean, key: string, value: any): Thenable<void> {
setValue(shared: boolean, key: string, value: object): Promise<void> {
return this._proxy.$setValue(shared, key, value);
}
$acceptValue(shared: boolean, key: string, value: object): void {
this._onDidChangeStorage.fire({ shared, key, value });
}
}

View File

@@ -3,15 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import * as Objects from 'vs/base/common/objects';
import { asThenable } from 'vs/base/common/async';
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 * as tasks from 'vs/workbench/parts/tasks/common/tasks';
import { MainContext, MainThreadTaskShape, ExtHostTaskShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
@@ -20,7 +20,7 @@ import { ExtHostWorkspace } 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
ShellExecutionOptionsDTO, ShellExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO
} from '../shared/tasks';
import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
@@ -28,432 +28,6 @@ import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { CancellationToken } from 'vs/base/common/cancellation';
/*
namespace ProblemPattern {
export function from(value: vscode.ProblemPattern | vscode.MultiLineProblemPattern): Problems.ProblemPattern | Problems.MultiLineProblemPattern {
if (value === void 0 || value === null) {
return undefined;
}
if (Array.isArray(value)) {
let result: Problems.ProblemPattern[] = [];
for (let pattern of value) {
let converted = fromSingle(pattern);
if (!converted) {
return undefined;
}
result.push(converted);
}
return result;
} else {
return fromSingle(value);
}
}
function copyProperty(target: Problems.ProblemPattern, source: vscode.ProblemPattern, tk: keyof Problems.ProblemPattern) {
let sk: keyof vscode.ProblemPattern = tk;
let value = source[sk];
if (typeof value === 'number') {
target[tk] = value;
}
}
function getValue(value: number, defaultValue: number): number {
if (value !== void 0 && value === null) {
return value;
}
return defaultValue;
}
function fromSingle(problemPattern: vscode.ProblemPattern): Problems.ProblemPattern {
if (problemPattern === void 0 || problemPattern === null || !(problemPattern.regexp instanceof RegExp)) {
return undefined;
}
let result: Problems.ProblemPattern = {
regexp: problemPattern.regexp
};
copyProperty(result, problemPattern, 'file');
copyProperty(result, problemPattern, 'location');
copyProperty(result, problemPattern, 'line');
copyProperty(result, problemPattern, 'character');
copyProperty(result, problemPattern, 'endLine');
copyProperty(result, problemPattern, 'endCharacter');
copyProperty(result, problemPattern, 'severity');
copyProperty(result, problemPattern, 'code');
copyProperty(result, problemPattern, 'message');
if (problemPattern.loop === true || problemPattern.loop === false) {
result.loop = problemPattern.loop;
}
if (result.location) {
result.file = getValue(result.file, 1);
result.message = getValue(result.message, 0);
} else {
result.file = getValue(result.file, 1);
result.line = getValue(result.line, 2);
result.character = getValue(result.character, 3);
result.message = getValue(result.message, 0);
}
return result;
}
}
namespace ApplyTo {
export function from(value: vscode.ApplyToKind): Problems.ApplyToKind {
if (value === void 0 || value === null) {
return Problems.ApplyToKind.allDocuments;
}
switch (value) {
case types.ApplyToKind.OpenDocuments:
return Problems.ApplyToKind.openDocuments;
case types.ApplyToKind.ClosedDocuments:
return Problems.ApplyToKind.closedDocuments;
}
return Problems.ApplyToKind.allDocuments;
}
}
namespace FileLocation {
export function from(value: vscode.FileLocationKind | string): { kind: Problems.FileLocationKind; prefix?: string } {
if (value === void 0 || value === null) {
return { kind: Problems.FileLocationKind.Auto };
}
if (typeof value === 'string') {
return { kind: Problems.FileLocationKind.Relative, prefix: value };
}
switch (value) {
case types.FileLocationKind.Absolute:
return { kind: Problems.FileLocationKind.Absolute };
case types.FileLocationKind.Relative:
return { kind: Problems.FileLocationKind.Relative, prefix: '${workspaceFolder}' };
}
return { kind: Problems.FileLocationKind.Auto };
}
}
namespace WatchingPattern {
export function from(value: RegExp | vscode.BackgroundPattern): Problems.WatchingPattern {
if (value === void 0 || value === null) {
return undefined;
}
if (value instanceof RegExp) {
return { regexp: value };
}
if (!(value.regexp instanceof RegExp)) {
return undefined;
}
let result: Problems.WatchingPattern = {
regexp: value.regexp
};
if (typeof value.file === 'number') {
result.file = value.file;
}
return result;
}
}
namespace BackgroundMonitor {
export function from(value: vscode.BackgroundMonitor): Problems.WatchingMatcher {
if (value === void 0 || value === null) {
return undefined;
}
let result: Problems.WatchingMatcher = {
activeOnStart: !!value.activeOnStart,
beginsPattern: WatchingPattern.from(value.beginsPattern),
endsPattern: WatchingPattern.from(value.endsPattern)
};
return result;
}
}
namespace ProblemMatcher {
export function from(values: (string | vscode.ProblemMatcher)[]): (string | Problems.ProblemMatcher)[] {
if (values === void 0 || values === null) {
return undefined;
}
let result: (string | Problems.ProblemMatcher)[] = [];
for (let value of values) {
let converted = typeof value === 'string' ? value : fromSingle(value);
if (converted) {
result.push(converted);
}
}
return result;
}
function fromSingle(problemMatcher: vscode.ProblemMatcher): Problems.ProblemMatcher {
if (problemMatcher === void 0 || problemMatcher === null) {
return undefined;
}
let location = FileLocation.from(problemMatcher.fileLocation);
let result: Problems.ProblemMatcher = {
owner: typeof problemMatcher.owner === 'string' ? problemMatcher.owner : UUID.generateUuid(),
applyTo: ApplyTo.from(problemMatcher.applyTo),
fileLocation: location.kind,
filePrefix: location.prefix,
pattern: ProblemPattern.from(problemMatcher.pattern),
severity: fromDiagnosticSeverity(problemMatcher.severity),
};
return result;
}
}
*/
namespace TaskRevealKind {
export function from(value: vscode.TaskRevealKind): tasks.RevealKind {
if (value === void 0 || value === null) {
return tasks.RevealKind.Always;
}
switch (value) {
case types.TaskRevealKind.Silent:
return tasks.RevealKind.Silent;
case types.TaskRevealKind.Never:
return tasks.RevealKind.Never;
}
return tasks.RevealKind.Always;
}
}
namespace TaskPanelKind {
export function from(value: vscode.TaskPanelKind): tasks.PanelKind {
if (value === void 0 || value === null) {
return tasks.PanelKind.Shared;
}
switch (value) {
case types.TaskPanelKind.Dedicated:
return tasks.PanelKind.Dedicated;
case types.TaskPanelKind.New:
return tasks.PanelKind.New;
default:
return tasks.PanelKind.Shared;
}
}
}
namespace PresentationOptions {
export function from(value: vscode.TaskPresentationOptions): tasks.PresentationOptions {
if (value === void 0 || value === null) {
return { reveal: tasks.RevealKind.Always, echo: true, focus: false, panel: tasks.PanelKind.Shared, showReuseMessage: true };
}
return {
reveal: TaskRevealKind.from(value.reveal),
echo: value.echo === void 0 ? true : !!value.echo,
focus: !!value.focus,
panel: TaskPanelKind.from(value.panel),
showReuseMessage: value.showReuseMessage === void 0 ? true : !!value.showReuseMessage
};
}
}
namespace Strings {
export function from(value: string[]): string[] {
if (value === void 0 || value === null) {
return undefined;
}
for (let element of value) {
if (typeof element !== 'string') {
return [];
}
}
return value;
}
}
namespace CommandOptions {
function isShellConfiguration(value: any): value is { executable: string; shellArgs?: string[] } {
return value && typeof value.executable === 'string';
}
export function from(value: vscode.ShellExecutionOptions | vscode.ProcessExecutionOptions): tasks.CommandOptions {
if (value === void 0 || value === null) {
return undefined;
}
let result: tasks.CommandOptions = {
};
if (typeof value.cwd === 'string') {
result.cwd = value.cwd;
}
if (value.env) {
result.env = Object.create(null);
Object.keys(value.env).forEach(key => {
let envValue = value.env[key];
if (typeof envValue === 'string') {
result.env[key] = envValue;
}
});
}
if (isShellConfiguration(value)) {
result.shell = ShellConfiguration.from(value);
}
return result;
}
}
namespace ShellQuoteOptions {
export function from(value: vscode.ShellQuotingOptions): tasks.ShellQuotingOptions {
if (value === void 0 || value === null) {
return undefined;
}
return {
escape: value.escape,
strong: value.strong,
weak: value.strong
};
}
}
namespace ShellConfiguration {
export function from(value: { executable?: string, shellArgs?: string[], quotes?: vscode.ShellQuotingOptions }): tasks.ShellConfiguration {
if (value === void 0 || value === null || !value.executable) {
return undefined;
}
let result: tasks.ShellConfiguration = {
executable: value.executable,
args: Strings.from(value.shellArgs),
quoting: ShellQuoteOptions.from(value.quotes)
};
return result;
}
}
namespace ShellString {
export function from(value: (string | vscode.ShellQuotedString)[]): tasks.CommandString[] {
if (value === void 0 || value === null) {
return undefined;
}
return value.slice(0);
}
}
namespace Tasks {
export function from(tasks: vscode.Task[], rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): tasks.ContributedTask[] {
if (tasks === void 0 || tasks === null) {
return [];
}
let result: tasks.ContributedTask[] = [];
for (let task of tasks) {
let converted = fromSingle(task, rootFolder, extension);
if (converted) {
result.push(converted);
}
}
return result;
}
function fromSingle(task: vscode.Task, rootFolder: vscode.WorkspaceFolder, extension: IExtensionDescription): tasks.ContributedTask {
if (typeof task.name !== 'string') {
return undefined;
}
let command: tasks.CommandConfiguration;
let execution = task.execution;
if (execution instanceof types.ProcessExecution) {
command = getProcessCommand(execution);
} else if (execution instanceof types.ShellExecution) {
command = getShellCommand(execution);
} else {
return undefined;
}
if (command === void 0) {
return undefined;
}
command.presentation = PresentationOptions.from(task.presentationOptions);
let taskScope: types.TaskScope.Global | types.TaskScope.Workspace | vscode.WorkspaceFolder | undefined = task.scope;
let workspaceFolder: vscode.WorkspaceFolder | undefined;
let scope: tasks.TaskScope;
// For backwards compatibility
if (taskScope === void 0) {
scope = tasks.TaskScope.Folder;
workspaceFolder = rootFolder;
} else if (taskScope === types.TaskScope.Global) {
scope = tasks.TaskScope.Global;
} else if (taskScope === types.TaskScope.Workspace) {
scope = tasks.TaskScope.Workspace;
} else {
scope = tasks.TaskScope.Folder;
workspaceFolder = taskScope;
}
let source: tasks.ExtensionTaskSource = {
kind: tasks.TaskSourceKind.Extension,
label: typeof task.source === 'string' ? task.source : extension.name,
extension: extension.id,
scope: scope,
workspaceFolder: undefined
};
// We can't transfer a workspace folder object from the extension host to main since they differ
// in shape and we don't have backwards converting function. So transfer the URI and resolve the
// workspace folder on the main side.
(source as any as tasks.ExtensionTaskSourceTransfer).__workspaceFolder = workspaceFolder ? workspaceFolder.uri as URI : undefined;
(source as any as tasks.ExtensionTaskSourceTransfer).__definition = task.definition;
let label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
// The definition id will be prefix on the main side since we compute it there.
let id = `${extension.id}`;
let result: tasks.ContributedTask = {
_id: id,
_source: source,
_label: label,
type: task.definition.type,
defines: undefined,
name: task.name,
identifier: label,
group: task.group ? (task.group as types.TaskGroup).id : undefined,
command: command,
isBackground: !!task.isBackground,
problemMatchers: task.problemMatchers.slice(),
hasDefinedMatchers: (task as types.Task).hasDefinedMatchers
};
return result;
}
function getProcessCommand(value: vscode.ProcessExecution): tasks.CommandConfiguration {
if (typeof value.process !== 'string') {
return undefined;
}
let result: tasks.CommandConfiguration = {
name: value.process,
args: Strings.from(value.args),
runtime: tasks.RuntimeType.Process,
suppressTaskName: true,
presentation: undefined
};
if (value.options) {
result.options = CommandOptions.from(value.options);
}
return result;
}
function getShellCommand(value: vscode.ShellExecution): tasks.CommandConfiguration {
if (value.args) {
if (typeof value.command !== 'string' && typeof value.command.value !== 'string') {
return undefined;
}
let result: tasks.CommandConfiguration = {
name: value.command,
args: ShellString.from(value.args),
runtime: tasks.RuntimeType.Shell,
presentation: undefined
};
if (value.options) {
result.options = CommandOptions.from(value.options);
}
return result;
} else {
if (typeof value.commandLine !== 'string') {
return undefined;
}
let result: tasks.CommandConfiguration = {
name: value.commandLine,
runtime: tasks.RuntimeType.Shell,
presentation: undefined
};
if (value.options) {
result.options = CommandOptions.from(value.options);
}
return result;
}
}
}
namespace TaskDefinitionDTO {
export function from(value: vscode.TaskDefinition): TaskDefinitionDTO {
if (value === void 0 || value === null) {
@@ -589,6 +163,20 @@ namespace TaskHandleDTO {
namespace TaskDTO {
export function fromMany(tasks: vscode.Task[], extension: IExtensionDescription): TaskDTO[] {
if (tasks === void 0 || tasks === null) {
return [];
}
let result: TaskDTO[] = [];
for (let task of tasks) {
let converted = from(task, extension);
if (converted) {
result.push(converted);
}
}
return result;
}
export function from(value: vscode.Task, extension: IExtensionDescription): TaskDTO {
if (value === void 0 || value === null) {
return undefined;
@@ -605,7 +193,7 @@ namespace TaskDTO {
if (typeof value.scope === 'number') {
scope = value.scope;
} else {
scope = value.scope.uri.toJSON();
scope = value.scope.uri;
}
}
if (!definition || !scope) {
@@ -626,7 +214,8 @@ namespace TaskDTO {
group: group,
presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions),
problemMatchers: value.problemMatchers,
hasDefinedMatchers: (value as types.Task).hasDefinedMatchers
hasDefinedMatchers: (value as types.Task).hasDefinedMatchers,
runOptions: (<vscode.Task>value).runOptions ? (<vscode.Task>value).runOptions : { reevaluateOnRerun: true },
};
return result;
}
@@ -770,7 +359,7 @@ export class ExtHostTask implements ExtHostTaskShape {
this._proxy.$registerTaskSystem(scheme, info);
}
public fetchTasks(filter?: vscode.TaskFilter): Thenable<vscode.Task[]> {
public fetchTasks(filter?: vscode.TaskFilter): Promise<vscode.Task[]> {
return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then((values) => {
let result: vscode.Task[] = [];
for (let value of values) {
@@ -783,7 +372,7 @@ export class ExtHostTask implements ExtHostTaskShape {
});
}
public executeTask(extension: IExtensionDescription, task: vscode.Task): Thenable<vscode.TaskExecution> {
public executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
let tTask = (task as types.Task);
// We have a preserved ID. So the task didn't change.
if (tTask._id !== void 0) {
@@ -803,7 +392,7 @@ export class ExtHostTask implements ExtHostTaskShape {
return result;
}
public terminateTask(execution: vscode.TaskExecution): Thenable<void> {
public terminateTask(execution: vscode.TaskExecution): Promise<void> {
if (!(execution instanceof TaskExecutionImpl)) {
throw new Error('No valid task execution provided');
}
@@ -860,12 +449,12 @@ export class ExtHostTask implements ExtHostTaskShape {
}
}
public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<tasks.TaskSet> {
public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<TaskSetDTO> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<tasks.TaskSet>(new Error('no handler found'));
return Promise.reject(new Error('no handler found'));
}
return asThenable(() => handler.provider.provideTasks(CancellationToken.None)).then(value => {
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) {
@@ -875,17 +464,19 @@ export class ExtHostTask implements ExtHostTaskShape {
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
}
}
let workspaceFolders = this._workspaceService.getWorkspaceFolders();
return {
tasks: Tasks.from(sanitized, workspaceFolders && workspaceFolders.length > 0 ? workspaceFolders[0] : undefined, handler.extension),
tasks: TaskDTO.fromMany(sanitized, handler.extension),
extension: handler.extension
};
});
}
public $resolveVariables(uriComponents: UriComponents, variables: string[]): any {
public $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> {
let uri: URI = URI.revive(uriComponents);
let result: { [key: string]: string; } = Object.create(null);
let result = {
process: undefined as string,
variables: Object.create(null)
};
let workspaceFolder = this._workspaceService.resolveWorkspaceFolder(uri);
let resolver = new ExtHostVariableResolverService(this._workspaceService, this._editorService, this._configurationService);
let ws: IWorkspaceFolder = {
@@ -896,10 +487,24 @@ export class ExtHostTask implements ExtHostTaskShape {
throw new Error('Not implemented');
}
};
for (let variable of variables) {
result[variable] = resolver.resolve(ws, variable);
for (let variable of toResolve.variables) {
result.variables[variable] = resolver.resolve(ws, variable);
}
return result;
if (toResolve.process !== void 0) {
let paths: string[] | undefined = undefined;
if (toResolve.process.path !== void 0) {
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 !== void 0 ? resolver.resolve(ws, toResolve.process.cwd) : undefined,
paths
);
}
return Promise.resolve(result);
}
private nextHandle(): number {

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
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 { Event, Emitter } from 'vs/base/common/event';
@@ -100,8 +100,9 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
env?: { [key: string]: string },
waitOnExit?: boolean
): void {
this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit).then((id) => {
this._runQueuedRequests(id);
this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit).then(terminal => {
this._name = terminal.name;
this._runQueuedRequests(terminal.id);
});
}
@@ -113,7 +114,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
this._name = name;
}
public get processId(): Thenable<number> {
public get processId(): Promise<number> {
return this._pidPromise;
}
@@ -373,7 +374,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
}
public $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, cols: number, rows: number): void {
public $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number): void {
// TODO: This function duplicates a lot of TerminalProcessManager.createProcess, ideally
// they would be merged into a single implementation.
@@ -391,10 +392,9 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
shellLaunchConfig.args = shellArgsConfigValue;
}
// TODO: Base the cwd on the last active workspace root
// const lastActiveWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
// this.initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, lastActiveWorkspaceRootUri, this._configHelper);
const initialCwd = os.homedir();
// TODO: @daniel
const activeWorkspaceRootUri = URI.revive(activeWorkspaceRootUriComponents);
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, activeWorkspaceRootUri, terminalConfig.cwd);
// TODO: Pull in and resolve config settings
// // Resolve env vars from config and shell
@@ -411,7 +411,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
// Continue env initialization, merging in the env from the launch
// config and adding keys that are needed to create the process
const locale = terminalConfig.get('setLocaleVariables') ? platform.locale : undefined;
terminalEnvironment.addTerminalEnvironmentKeys(env, platform.isWindows, locale);
terminalEnvironment.addTerminalEnvironmentKeys(env, locale);
// Fork the process and listen for messages
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env);
@@ -495,7 +495,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
private _getTerminalObjectIndexById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): number {
let index: number = null;
let index: number | null = null;
array.some((item, i) => {
const thisId = item._id;
if (thisId === id) {

View File

@@ -4,16 +4,16 @@
*--------------------------------------------------------------------------------------------*/
import { ok } from 'vs/base/common/assert';
import { readonly, illegalArgument } from 'vs/base/common/errors';
import { illegalArgument, readonly } from 'vs/base/common/errors';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData';
import { Selection, Range, Position, EndOfLine, TextEditorRevealType, TextEditorLineNumbersStyle, SnippetString } from './extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as TypeConverters from './extHostTypeConverters';
import { MainThreadTextEditorsShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from './extHost.protocol';
import * as vscode from 'vscode';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { IRange } from 'vs/editor/common/core/range';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainThreadTextEditorsShape } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData';
import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/node/extHostTypes';
import * as vscode from 'vscode';
export class TextEditorDecorationType implements vscode.TextEditorDecorationType {
@@ -25,7 +25,7 @@ export class TextEditorDecorationType implements vscode.TextEditorDecorationType
constructor(proxy: MainThreadTextEditorsShape, options: vscode.DecorationRenderOptions) {
this.key = TextEditorDecorationType._Keys.nextId();
this._proxy = proxy;
this._proxy.$registerTextEditorDecorationType(this.key, <any>/* URI vs Uri */ options);
this._proxy.$registerTextEditorDecorationType(this.key, TypeConverters.DecorationRenderOptions.from(options));
}
public dispose(): void {
@@ -76,7 +76,7 @@ export class TextEditorEdit {
}
replace(location: Position | Range | Selection, value: string): void {
let range: Range = null;
let range: Range | null = null;
if (location instanceof Position) {
range = new Range(location, location);
@@ -94,7 +94,7 @@ export class TextEditorEdit {
}
delete(location: Range | Selection): void {
let range: Range = null;
let range: Range | null = null;
if (location instanceof Range) {
range = location;
@@ -482,7 +482,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
);
}
private _trySetSelection(): Thenable<vscode.TextEditor> {
private _trySetSelection(): Promise<vscode.TextEditor> {
let selection = this._selections.map(TypeConverters.Selection.from);
return this._runOnProxy(() => this._proxy.$trySetSelections(this._id, selection));
}
@@ -494,7 +494,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
// ---- editing
edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Thenable<boolean> {
edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
if (this._disposed) {
return Promise.reject(new Error('TextEditor#edit not possible on closed editors'));
}
@@ -503,7 +503,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
return this._applyEdit(edit);
}
private _applyEdit(editBuilder: TextEditorEdit): Thenable<boolean> {
private _applyEdit(editBuilder: TextEditorEdit): Promise<boolean> {
let editData = editBuilder.finalize();
// return when there is nothing to do
@@ -557,7 +557,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
});
}
insertSnippet(snippet: SnippetString, where?: Position | Position[] | Range | Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Thenable<boolean> {
insertSnippet(snippet: SnippetString, where?: Position | Position[] | Range | Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise<boolean> {
if (this._disposed) {
return Promise.reject(new Error('TextEditor#insertSnippet not possible on closed editors'));
}
@@ -589,7 +589,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
// ---- util
private _runOnProxy(callback: () => Thenable<any>): Thenable<ExtHostTextEditor> {
private _runOnProxy(callback: () => Promise<any>): Promise<ExtHostTextEditor> {
if (this._disposed) {
console.warn('TextEditor is closed/disposed');
return Promise.resolve(undefined);
@@ -603,8 +603,8 @@ export class ExtHostTextEditor implements vscode.TextEditor {
}
}
function warnOnError(promise: Thenable<any>): void {
promise.then(null, (err) => {
function warnOnError(promise: Promise<any>): void {
promise.then(void 0, (err) => {
console.warn(err);
});
}

View File

@@ -3,12 +3,12 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { TextEditorSelectionChangeKind } from './extHostTypes';
import * as TypeConverters from './extHostTypeConverters';
import { TextEditorDecorationType, ExtHostTextEditor } from './extHostTextEditor';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
import { MainContext, MainThreadTextEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IMainContext, IEditorPropertiesChangeData } from './extHost.protocol';
import { Emitter, Event } from 'vs/base/common/event';
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';
import * as TypeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { TextEditorSelectionChangeKind } from 'vs/workbench/api/node/extHostTypes';
import * as vscode from 'vscode';
export class ExtHostEditors implements ExtHostEditorsShape {
@@ -50,10 +50,10 @@ export class ExtHostEditors implements ExtHostEditorsShape {
return this._extHostDocumentsAndEditors.allEditors();
}
showTextDocument(document: vscode.TextDocument, column: vscode.ViewColumn, preserveFocus: boolean): Thenable<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, options: { column: vscode.ViewColumn, preserveFocus: boolean, pinned: boolean }): Thenable<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Thenable<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Thenable<vscode.TextEditor> {
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> {
let options: ITextDocumentShowOptions;
if (typeof columnOrOptions === 'number') {
options = {
@@ -87,7 +87,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
return new TextEditorDecorationType(this._proxy, options);
}
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): Thenable<boolean> {
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): Promise<boolean> {
const dto = TypeConverters.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
return this._proxy.$tryApplyWorkspaceEdit(dto);
}
@@ -146,7 +146,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
}
}
getDiffInformation(id: string): Thenable<vscode.LineChange[]> {
getDiffInformation(id: string): Promise<vscode.LineChange[]> {
return Promise.resolve(this._proxy.$getDiffInformation(id));
}
}

View File

@@ -7,20 +7,42 @@ import { localize } from 'vs/nls';
import * as vscode from 'vscode';
import { basename } from 'vs/base/common/paths';
import { URI } from 'vs/base/common/uri';
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol';
import { ITreeItem, TreeViewItemHandleArg } from 'vs/workbench/common/views';
import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions } from 'vs/workbench/common/views';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { asThenable } from 'vs/base/common/async';
import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/node/extHostTypes';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { equals } from 'vs/base/common/arrays';
import { asPromise } from 'vs/base/common/async';
import { TreeItemCollapsibleState, ThemeIcon, MarkdownString } from 'vs/workbench/api/node/extHostTypes';
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';
type TreeItemHandle = string;
function toTreeItemLabel(label: any, extension: IExtensionDescription): ITreeItemLabel {
if (isString(label)) {
return { label };
}
if (label
&& typeof label === 'object'
&& typeof label.label === 'string') {
checkProposedApiEnabled(extension);
let highlights: [number, number][] = void 0;
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 : void 0;
}
return { label: label.label, highlights };
}
return void 0;
}
export class ExtHostTreeViews implements ExtHostTreeViewsShape {
private treeViews: Map<string, ExtHostTreeView<any>> = new Map<string, ExtHostTreeView<any>>();
@@ -40,16 +62,17 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
});
}
registerTreeDataProvider<T>(id: string, treeDataProvider: vscode.TreeDataProvider<T>): vscode.Disposable {
const treeView = this.createTreeView(id, { treeDataProvider });
registerTreeDataProvider<T>(id: string, treeDataProvider: vscode.TreeDataProvider<T>, extension: IExtensionDescription): vscode.Disposable {
const treeView = this.createTreeView(id, { treeDataProvider }, extension);
return { dispose: () => treeView.dispose() };
}
createTreeView<T>(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider<T> }): vscode.TreeView<T> {
createTreeView<T>(viewId: string, options: vscode.TreeViewOptions<T>, extension: IExtensionDescription): vscode.TreeView<T> {
if (!options || !options.treeDataProvider) {
throw new Error('Options with treeDataProvider is mandatory');
}
const treeView = this.createExtHostTreeViewer(viewId, options.treeDataProvider);
const treeView = this.createExtHostTreeView(viewId, options, extension);
return {
get onDidCollapseElement() { return treeView.onDidCollapseElement; },
get onDidExpandElement() { return treeView.onDidExpandElement; },
@@ -57,7 +80,9 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
get onDidChangeSelection() { return treeView.onDidChangeSelection; },
get visible() { return treeView.visible; },
get onDidChangeVisibility() { return treeView.onDidChangeVisibility; },
reveal: (element: T, options?: { select?: boolean, focus?: boolean }): Thenable<void> => {
get message() { return treeView.message; },
set message(message: string | MarkdownString) { checkProposedApiEnabled(extension); treeView.message = message; },
reveal: (element: T, options?: IRevealOptions): Promise<void> => {
return treeView.reveal(element, options);
},
dispose: () => {
@@ -67,10 +92,10 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
};
}
$getChildren(treeViewId: string, treeItemHandle?: string): Thenable<ITreeItem[]> {
$getChildren(treeViewId: string, treeItemHandle?: string): Promise<ITreeItem[]> {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
return TPromise.wrapError<ITreeItem[]>(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)));
return Promise.reject(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)));
}
return treeView.getChildren(treeItemHandle);
}
@@ -99,8 +124,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
treeView.setVisible(isVisible);
}
private createExtHostTreeViewer<T>(id: string, dataProvider: vscode.TreeDataProvider<T>): ExtHostTreeView<T> {
const treeView = new ExtHostTreeView<T>(id, dataProvider, this._proxy, this.commands.converter, this.logService);
private createExtHostTreeView<T>(id: string, options: vscode.TreeViewOptions<T>, extension: IExtensionDescription): ExtHostTreeView<T> {
const treeView = new ExtHostTreeView<T>(id, options, this._proxy, this.commands.converter, this.logService, extension);
this.treeViews.set(id, treeView);
return treeView;
}
@@ -122,7 +147,9 @@ class ExtHostTreeView<T> extends Disposable {
private static LABEL_HANDLE_PREFIX = '0';
private static ID_HANDLE_PREFIX = '1';
private roots: TreeNode[] = null;
private readonly dataProvider: vscode.TreeDataProvider<T>;
private roots: TreeNode[] | null = null;
private elements: Map<TreeItemHandle, T> = new Map<TreeItemHandle, T>();
private nodes: Map<T, TreeNode> = new Map<T, TreeNode>();
@@ -144,17 +171,18 @@ 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: TPromise<void> = TPromise.as(null);
private refreshPromise: Promise<void> = Promise.resolve(null);
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService) {
constructor(private viewId: string, options: vscode.TreeViewOptions<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService, private extension: IExtensionDescription) {
super();
this.proxy.$registerTreeViewDataProvider(viewId);
this.dataProvider = options.treeDataProvider;
this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll });
if (this.dataProvider.onDidChangeTreeData) {
let refreshingPromise, promiseCallback;
this._register(debounceEvent<T, T[]>(this.dataProvider.onDidChangeTreeData, (last, current) => {
this._register(Event.debounce<T, T[]>(this.dataProvider.onDidChangeTreeData, (last, current) => {
if (!refreshingPromise) {
// New refresh has started
refreshingPromise = new TPromise((c, e) => promiseCallback = c);
refreshingPromise = new Promise(c => promiseCallback = c);
this.refreshPromise = this.refreshPromise.then(() => refreshingPromise);
}
return last ? [...last, current] : [current];
@@ -166,15 +194,15 @@ class ExtHostTreeView<T> extends Disposable {
}
}
getChildren(parentHandle?: TreeItemHandle): Thenable<ITreeItem[]> {
getChildren(parentHandle?: TreeItemHandle): Promise<ITreeItem[]> {
const parentElement = parentHandle ? this.getExtensionElement(parentHandle) : void 0;
if (parentHandle && !parentElement) {
console.error(`No tree item with id \'${parentHandle}\' found.`);
return TPromise.as([]);
return Promise.resolve([]);
}
const childrenNodes = this.getChildrenNodes(parentHandle); // Get it from cache
return (childrenNodes ? TPromise.as(childrenNodes) : this.fetchChildrenNodes(parentElement))
return (childrenNodes ? Promise.resolve(childrenNodes) : this.fetchChildrenNodes(parentElement))
.then(nodes => nodes.map(n => n.item));
}
@@ -182,18 +210,29 @@ class ExtHostTreeView<T> extends Disposable {
return this.elements.get(treeItemHandle);
}
reveal(element: T, options?: { select?: boolean, focus?: boolean }): TPromise<void> {
reveal(element: T, options?: IRevealOptions): Promise<void> {
options = options ? options : { select: true, focus: false };
const select = isUndefinedOrNull(options.select) ? true : options.select;
const focus = isUndefinedOrNull(options.focus) ? false : options.focus;
const expand = isUndefinedOrNull(options.expand) ? false : options.expand;
if (typeof this.dataProvider.getParent !== 'function') {
return TPromise.wrapError(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`));
return Promise.reject(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`));
}
return this.refreshPromise
.then(() => this.resolveUnknownParentChain(element))
.then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1])
.then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus })), error => this.logService.error(error));
.then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus, expand })), error => this.logService.error(error));
}
private _message: string | MarkdownString;
get message(): string | MarkdownString {
return this._message;
}
set message(message: string | MarkdownString) {
this._message = message;
this.proxy.$setMessage(this.viewId, typeConvert.MarkdownString.fromStrict(this._message));
}
setExpanded(treeItemHandle: TreeItemHandle, expanded: boolean): void {
@@ -221,11 +260,11 @@ class ExtHostTreeView<T> extends Disposable {
}
}
private resolveUnknownParentChain(element: T): Thenable<TreeNode[]> {
private resolveUnknownParentChain(element: T): Promise<TreeNode[]> {
return this.resolveParent(element)
.then((parent) => {
if (!parent) {
return TPromise.as([]);
return Promise.resolve([]);
}
return this.resolveUnknownParentChain(parent)
.then(result => this.resolveTreeNode(parent, result[result.length - 1])
@@ -236,16 +275,20 @@ class ExtHostTreeView<T> extends Disposable {
});
}
private resolveParent(element: T): Thenable<T> {
private resolveParent(element: T): Promise<T> {
const node = this.nodes.get(element);
if (node) {
return TPromise.as(node.parent ? this.elements.get(node.parent.item.handle) : null);
return Promise.resolve(node.parent ? this.elements.get(node.parent.item.handle) : null);
}
return asThenable(() => this.dataProvider.getParent(element));
return asPromise(() => this.dataProvider.getParent(element));
}
private resolveTreeNode(element: T, parent?: TreeNode): Thenable<TreeNode> {
return asThenable(() => this.dataProvider.getTreeItem(element))
private resolveTreeNode(element: T, parent?: TreeNode): Promise<TreeNode> {
const node = this.nodes.get(element);
if (node) {
return Promise.resolve(node);
}
return asPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => this.createHandle(element, extTreeItem, parent, true))
.then(handle => this.getChildren(parent ? parent.item.handle : null)
.then(() => {
@@ -253,7 +296,7 @@ class ExtHostTreeView<T> extends Disposable {
if (cachedElement) {
const node = this.nodes.get(cachedElement);
if (node) {
return TPromise.as(node);
return Promise.resolve(node);
}
}
throw new Error(`Cannot resolve tree item for element ${handle}`);
@@ -274,21 +317,20 @@ class ExtHostTreeView<T> extends Disposable {
return this.roots;
}
private fetchChildrenNodes(parentElement?: T): Thenable<TreeNode[]> {
private fetchChildrenNodes(parentElement?: T): Promise<TreeNode[]> {
// clear children cache
this.clearChildren(parentElement);
const parentNode = parentElement ? this.nodes.get(parentElement) : void 0;
return asThenable(() => this.dataProvider.getChildren(parentElement))
.then(elements => TPromise.join(
(elements || [])
.filter(element => !!element)
.map(element => asThenable(() => this.dataProvider.getTreeItem(element))
return asPromise(() => this.dataProvider.getChildren(parentElement))
.then(elements => Promise.all(
coalesce(elements || [])
.map(element => asPromise(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => extTreeItem ? this.createAndRegisterTreeNode(element, extTreeItem, parentNode) : null))))
.then(nodes => nodes.filter(n => !!n));
.then(coalesce);
}
private refresh(elements: T[]): Thenable<void> {
private refresh(elements: T[]): Promise<void> {
const hasRoot = elements.some(element => !element);
if (hasRoot) {
this.clearAll(); // clear cache
@@ -299,7 +341,7 @@ class ExtHostTreeView<T> extends Disposable {
return this.refreshHandles(handlesToRefresh);
}
}
return TPromise.as(null);
return Promise.resolve(null);
}
private getHandlesToRefresh(elements: T[]): TreeItemHandle[] {
@@ -332,9 +374,9 @@ class ExtHostTreeView<T> extends Disposable {
return handlesToUpdate;
}
private refreshHandles(itemHandles: TreeItemHandle[]): TPromise<void> {
private refreshHandles(itemHandles: TreeItemHandle[]): Promise<void> {
const itemsToRefresh: { [treeItemHandle: string]: ITreeItem } = {};
return TPromise.join(itemHandles.map(treeItemHandle =>
return Promise.all(itemHandles.map(treeItemHandle =>
this.refreshNode(treeItemHandle)
.then(node => {
if (node) {
@@ -344,11 +386,11 @@ class ExtHostTreeView<T> extends Disposable {
.then(() => Object.keys(itemsToRefresh).length ? this.proxy.$refresh(this.viewId, itemsToRefresh) : null);
}
private refreshNode(treeItemHandle: TreeItemHandle): Thenable<TreeNode> {
private refreshNode(treeItemHandle: TreeItemHandle): Promise<TreeNode> {
const extElement = this.getExtensionElement(treeItemHandle);
const existing = this.nodes.get(extElement);
this.clearChildren(extElement); // clear children cache
return asThenable(() => this.dataProvider.getTreeItem(extElement))
return asPromise(() => this.dataProvider.getTreeItem(extElement))
.then(extTreeItem => {
if (extTreeItem) {
const newNode = this.createTreeNode(extElement, extTreeItem, existing.parent);
@@ -384,7 +426,8 @@ class ExtHostTreeView<T> extends Disposable {
const item = {
handle,
parentHandle: parent ? parent.item.handle : void 0,
label: extensionTreeItem.label,
label: toTreeItemLabel(extensionTreeItem.label, this.extension),
description: extensionTreeItem.description,
resourceUri: extensionTreeItem.resourceUri,
tooltip: typeof extensionTreeItem.tooltip === 'string' ? extensionTreeItem.tooltip : void 0,
command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command) : void 0,
@@ -403,8 +446,9 @@ class ExtHostTreeView<T> extends Disposable {
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 = label ? label : resourceUri ? basename(resourceUri.path) : '';
let elementId = treeItemLabel ? treeItemLabel.label : resourceUri ? basename(resourceUri.path) : '';
elementId = elementId.indexOf('/') !== -1 ? elementId.replace('/', '//') : elementId;
const existingHandle = this.nodes.has(element) ? this.nodes.get(element).item.handle : void 0;
const childrenNodes = (this.getChildrenNodes(parent) || []);

View File

@@ -8,11 +8,11 @@ import * as types from './extHostTypes';
import * as search from 'vs/workbench/parts/search/common/search';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
import { EndOfLineSequence } from 'vs/editor/common/model';
import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon';
import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model';
import * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { ProgressLocation as MainProgressLocation } from 'vs/workbench/services/progress/common/progress';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
@@ -24,6 +24,10 @@ import { MarkerSeverity, IRelatedInformation, IMarkerData, MarkerTag } from 'vs/
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { isString, isNumber } from 'vs/base/common/types';
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';
export interface PositionLike {
line: number;
@@ -42,14 +46,14 @@ export interface SelectionLike extends RangeLike {
export namespace Selection {
export function to(selection: ISelection): types.Selection {
let { selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn } = selection;
let start = new types.Position(selectionStartLineNumber - 1, selectionStartColumn - 1);
let end = new types.Position(positionLineNumber - 1, positionColumn - 1);
const { selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn } = selection;
const start = new types.Position(selectionStartLineNumber - 1, selectionStartColumn - 1);
const end = new types.Position(positionLineNumber - 1, positionColumn - 1);
return new types.Selection(start, end);
}
export function from(selection: SelectionLike): ISelection {
let { anchor, active } = selection;
const { anchor, active } = selection;
return {
selectionStartLineNumber: anchor.line + 1,
selectionStartColumn: anchor.character + 1,
@@ -64,7 +68,7 @@ export namespace Range {
if (!range) {
return undefined;
}
let { start, end } = range;
const { start, end } = range;
return {
startLineNumber: start.line + 1,
startColumn: start.character + 1,
@@ -77,7 +81,7 @@ export namespace Range {
if (!range) {
return undefined;
}
let { startLineNumber, startColumn, endLineNumber, endColumn } = range;
const { startLineNumber, startColumn, endLineNumber, endColumn } = range;
return new types.Range(startLineNumber - 1, startColumn - 1, endLineNumber - 1, endColumn - 1);
}
}
@@ -209,17 +213,61 @@ export namespace MarkdownString {
}
export function from(markup: vscode.MarkdownString | vscode.MarkedString): htmlContent.IMarkdownString {
let res: htmlContent.IMarkdownString;
if (isCodeblock(markup)) {
const { language, value } = markup;
return { value: '```' + language + '\n' + value + '\n```\n' };
res = { value: '```' + language + '\n' + value + '\n```\n' };
} else if (htmlContent.isMarkdownString(markup)) {
return markup;
res = markup;
} else if (typeof markup === 'string') {
return { value: <string>markup };
res = { value: <string>markup };
} else {
return { value: '' };
res = { value: '' };
}
// extract uris into a separate object
res.uris = Object.create(null);
let 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;
} catch (e) {
// ignore
}
return '';
};
marked(res.value, { renderer });
return res;
}
function _uriMassage(part: string, bucket: { [n: string]: UriComponents }): string {
if (!part) {
return part;
}
let data: any;
try {
data = parse(decodeURIComponent(part));
} catch (e) {
// ignore
}
if (!data) {
return part;
}
data = cloneAndChange(data, value => {
if (value instanceof URI) {
let key = `__uri_${Math.random().toString(16).slice(2, 8)}`;
bucket[key] = value;
return key;
} else {
return undefined;
}
});
return encodeURIComponent(JSON.stringify(data));
}
export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString {
const ret = new htmlContent.MarkdownString(value.value);
ret.isTrusted = value.isTrusted;
@@ -252,21 +300,142 @@ export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.Deco
}
}
export const TextEdit = {
function pathOrURIToURI(value: string | URI): URI {
if (typeof value === 'undefined') {
return value;
}
if (typeof value === 'string') {
return URI.file(value);
} else {
return value;
}
}
from(edit: vscode.TextEdit): modes.TextEdit {
export namespace ThemableDecorationAttachmentRenderOptions {
export function from(options: vscode.ThemableDecorationAttachmentRenderOptions): IContentDecorationRenderOptions {
if (typeof options === 'undefined') {
return options;
}
return {
contentText: options.contentText,
contentIconPath: pathOrURIToURI(options.contentIconPath),
border: options.border,
borderColor: <string | types.ThemeColor>options.borderColor,
fontStyle: options.fontStyle,
fontWeight: options.fontWeight,
textDecoration: options.textDecoration,
color: <string | types.ThemeColor>options.color,
backgroundColor: <string | types.ThemeColor>options.backgroundColor,
margin: options.margin,
width: options.width,
height: options.height,
};
}
}
export namespace ThemableDecorationRenderOptions {
export function from(options: vscode.ThemableDecorationRenderOptions): IThemeDecorationRenderOptions {
if (typeof options === 'undefined') {
return options;
}
return {
backgroundColor: <string | types.ThemeColor>options.backgroundColor,
outline: options.outline,
outlineColor: <string | types.ThemeColor>options.outlineColor,
outlineStyle: options.outlineStyle,
outlineWidth: options.outlineWidth,
border: options.border,
borderColor: <string | types.ThemeColor>options.borderColor,
borderRadius: options.borderRadius,
borderSpacing: options.borderSpacing,
borderStyle: options.borderStyle,
borderWidth: options.borderWidth,
fontStyle: options.fontStyle,
fontWeight: options.fontWeight,
textDecoration: options.textDecoration,
cursor: options.cursor,
color: <string | types.ThemeColor>options.color,
opacity: options.opacity,
letterSpacing: options.letterSpacing,
gutterIconPath: pathOrURIToURI(options.gutterIconPath),
gutterIconSize: options.gutterIconSize,
overviewRulerColor: <string | types.ThemeColor>options.overviewRulerColor,
before: ThemableDecorationAttachmentRenderOptions.from(options.before),
after: ThemableDecorationAttachmentRenderOptions.from(options.after),
};
}
}
export namespace DecorationRangeBehavior {
export function from(value: types.DecorationRangeBehavior): TrackedRangeStickiness {
if (typeof value === 'undefined') {
return value;
}
switch (value) {
case types.DecorationRangeBehavior.OpenOpen:
return TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges;
case types.DecorationRangeBehavior.ClosedClosed:
return TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
case types.DecorationRangeBehavior.OpenClosed:
return TrackedRangeStickiness.GrowsOnlyWhenTypingBefore;
case types.DecorationRangeBehavior.ClosedOpen:
return TrackedRangeStickiness.GrowsOnlyWhenTypingAfter;
}
}
}
export namespace DecorationRenderOptions {
export function from(options: vscode.DecorationRenderOptions): IDecorationRenderOptions {
return {
isWholeLine: options.isWholeLine,
rangeBehavior: DecorationRangeBehavior.from(options.rangeBehavior),
overviewRulerLane: options.overviewRulerLane,
light: ThemableDecorationRenderOptions.from(options.light),
dark: ThemableDecorationRenderOptions.from(options.dark),
backgroundColor: <string | types.ThemeColor>options.backgroundColor,
outline: options.outline,
outlineColor: <string | types.ThemeColor>options.outlineColor,
outlineStyle: options.outlineStyle,
outlineWidth: options.outlineWidth,
border: options.border,
borderColor: <string | types.ThemeColor>options.borderColor,
borderRadius: options.borderRadius,
borderSpacing: options.borderSpacing,
borderStyle: options.borderStyle,
borderWidth: options.borderWidth,
fontStyle: options.fontStyle,
fontWeight: options.fontWeight,
textDecoration: options.textDecoration,
cursor: options.cursor,
color: <string | types.ThemeColor>options.color,
opacity: options.opacity,
letterSpacing: options.letterSpacing,
gutterIconPath: pathOrURIToURI(options.gutterIconPath),
gutterIconSize: options.gutterIconSize,
overviewRulerColor: <string | types.ThemeColor>options.overviewRulerColor,
before: ThemableDecorationAttachmentRenderOptions.from(options.before),
after: ThemableDecorationAttachmentRenderOptions.from(options.after),
};
}
}
export namespace TextEdit {
export function from(edit: vscode.TextEdit): modes.TextEdit {
return <modes.TextEdit>{
text: edit.newText,
eol: EndOfLine.from(edit.newEol),
range: Range.from(edit.range)
};
},
to(edit: modes.TextEdit): types.TextEdit {
let result = new types.TextEdit(Range.to(edit.range), edit.text);
}
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);
return result;
}
};
}
export namespace WorkspaceEdit {
export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors): WorkspaceEditDto {
@@ -277,7 +446,7 @@ export namespace WorkspaceEdit {
const [uri, uriOrEdits] = entry;
if (Array.isArray(uriOrEdits)) {
// text edits
let doc = documents ? documents.getDocument(uri.toString()) : undefined;
const doc = documents ? documents.getDocument(uri.toString()) : undefined;
result.edits.push(<ResourceTextEditDto>{ resource: uri, modelVersionId: doc && doc.version, edits: uriOrEdits.map(TextEdit.from) });
} else {
// resource edits
@@ -339,11 +508,11 @@ export namespace SymbolKind {
_fromMapping[types.SymbolKind.TypeParameter] = modes.SymbolKind.TypeParameter;
export function from(kind: vscode.SymbolKind): modes.SymbolKind {
return _fromMapping[kind] || modes.SymbolKind.Property;
return typeof _fromMapping[kind] === 'number' ? _fromMapping[kind] : modes.SymbolKind.Property;
}
export function to(kind: modes.SymbolKind): vscode.SymbolKind {
for (let k in _fromMapping) {
for (const k in _fromMapping) {
if (_fromMapping[k] === kind) {
return Number(k);
}
@@ -373,7 +542,7 @@ export namespace WorkspaceSymbol {
export namespace DocumentSymbol {
export function from(info: vscode.DocumentSymbol): modes.DocumentSymbol {
let result: modes.DocumentSymbol = {
const result: modes.DocumentSymbol = {
name: info.name,
detail: info.detail,
range: Range.from(info.range),
@@ -386,7 +555,7 @@ export namespace DocumentSymbol {
return result;
}
export function to(info: modes.DocumentSymbol): vscode.DocumentSymbol {
let result = new types.DocumentSymbol(
const result = new types.DocumentSymbol(
info.name,
info.detail,
SymbolKind.to(info.kind),
@@ -400,17 +569,18 @@ export namespace DocumentSymbol {
}
}
export const location = {
from(value: vscode.Location): modes.Location {
export namespace location {
export function from(value: vscode.Location): modes.Location {
return {
range: value.range && Range.from(value.range),
uri: value.uri
};
},
to(value: modes.Location): types.Location {
}
export function to(value: modes.Location): types.Location {
return new types.Location(value.uri, Range.to(value.range));
}
};
}
export namespace DefinitionLink {
export function from(value: vscode.Location | vscode.DefinitionLink): modes.DefinitionLink {
@@ -454,7 +624,7 @@ export namespace DocumentHighlight {
}
export namespace CompletionTriggerKind {
export function from(kind: modes.CompletionTriggerKind) {
export function to(kind: modes.CompletionTriggerKind) {
switch (kind) {
case modes.CompletionTriggerKind.TriggerCharacter:
return types.CompletionTriggerKind.TriggerCharacter;
@@ -468,17 +638,17 @@ export namespace CompletionTriggerKind {
}
export namespace CompletionContext {
export function from(context: modes.CompletionContext): types.CompletionContext {
export function to(context: modes.CompletionContext): types.CompletionContext {
return {
triggerKind: CompletionTriggerKind.from(context.triggerKind),
triggerKind: CompletionTriggerKind.to(context.triggerKind),
triggerCharacter: context.triggerCharacter
};
}
}
export const CompletionItemKind = {
export namespace CompletionItemKind {
from(kind: types.CompletionItemKind): modes.CompletionItemKind {
export function from(kind: types.CompletionItemKind): modes.CompletionItemKind {
switch (kind) {
case types.CompletionItemKind.Method: return modes.CompletionItemKind.Method;
case types.CompletionItemKind.Function: return modes.CompletionItemKind.Function;
@@ -507,9 +677,9 @@ export const CompletionItemKind = {
case types.CompletionItemKind.TypeParameter: return modes.CompletionItemKind.TypeParameter;
}
return modes.CompletionItemKind.Property;
},
}
to(kind: modes.CompletionItemKind): types.CompletionItemKind {
export function to(kind: modes.CompletionItemKind): types.CompletionItemKind {
switch (kind) {
case modes.CompletionItemKind.Method: return types.CompletionItemKind.Method;
case modes.CompletionItemKind.Function: return types.CompletionItemKind.Function;
@@ -539,9 +709,9 @@ export const CompletionItemKind = {
}
return types.CompletionItemKind.Property;
}
};
}
export namespace Suggest {
export namespace CompletionItem {
export function to(suggestion: modes.CompletionItem): types.CompletionItem {
const result = new types.CompletionItem(suggestion.label);
@@ -554,15 +724,14 @@ export namespace Suggest {
result.preselect = suggestion.preselect;
result.commitCharacters = suggestion.commitCharacters;
result.range = Range.to(suggestion.range);
result.keepWhitespace = Boolean(suggestion.insertTextRules & modes.CompletionItemInsertTextRule.KeepWhitespace);
// 'inserText'-logic
if (suggestion.insertTextIsSnippet) {
if (suggestion.insertTextRules & modes.CompletionItemInsertTextRule.InsertAsSnippet) {
result.insertText = new types.SnippetString(suggestion.insertText);
} else {
result.insertText = suggestion.insertText;
result.textEdit = new types.TextEdit(result.range, result.insertText);
}
// TODO additionalEdits, command
return result;
@@ -638,7 +807,7 @@ export namespace DocumentLink {
export namespace ColorPresentation {
export function to(colorPresentation: modes.IColorPresentation): types.ColorPresentation {
let cp = new types.ColorPresentation(colorPresentation.label);
const cp = new types.ColorPresentation(colorPresentation.label);
if (colorPresentation.textEdit) {
cp.textEdit = TextEdit.to(colorPresentation.textEdit);
}
@@ -716,7 +885,7 @@ export namespace ProgressLocation {
export namespace FoldingRange {
export function from(r: vscode.FoldingRange): modes.FoldingRange {
let range: modes.FoldingRange = { start: r.start + 1, end: r.end + 1 };
const range: modes.FoldingRange = { start: r.start + 1, end: r.end + 1 };
if (r.kind) {
range.kind = FoldingRangeKind.from(r.kind);
}
@@ -799,3 +968,51 @@ export namespace LanguageSelector {
}
}
}
export namespace LogLevel {
export function from(extLevel: types.LogLevel): _MainLogLevel {
switch (extLevel) {
case types.LogLevel.Trace:
return _MainLogLevel.Trace;
case types.LogLevel.Debug:
return _MainLogLevel.Debug;
case types.LogLevel.Info:
return _MainLogLevel.Info;
case types.LogLevel.Warning:
return _MainLogLevel.Warning;
case types.LogLevel.Error:
return _MainLogLevel.Error;
case types.LogLevel.Critical:
return _MainLogLevel.Critical;
case types.LogLevel.Critical:
return _MainLogLevel.Critical;
case types.LogLevel.Off:
return _MainLogLevel.Off;
}
return _MainLogLevel.Info;
}
export function to(mainLevel: _MainLogLevel): types.LogLevel {
switch (mainLevel) {
case _MainLogLevel.Trace:
return types.LogLevel.Trace;
case _MainLogLevel.Debug:
return types.LogLevel.Debug;
case _MainLogLevel.Info:
return types.LogLevel.Info;
case _MainLogLevel.Warning:
return types.LogLevel.Warning;
case _MainLogLevel.Error:
return types.LogLevel.Error;
case _MainLogLevel.Critical:
return types.LogLevel.Critical;
case _MainLogLevel.Critical:
return types.LogLevel.Critical;
case _MainLogLevel.Off:
return types.LogLevel.Off;
}
return types.LogLevel.Info;
}
}

View File

@@ -14,13 +14,15 @@ import { relative } from 'path';
import { startsWith } from 'vs/base/common/strings';
import { values } from 'vs/base/common/map';
import { coalesce, equals } from 'vs/base/common/arrays';
import { generateUuid } from 'vs/base/common/uuid';
export class Disposable {
static from(...disposables: { dispose(): any }[]): Disposable {
static from(...inDisposables: { dispose(): any }[]): Disposable {
let disposables: ReadonlyArray<{ dispose(): any }> | undefined = inDisposables;
return new Disposable(function () {
if (disposables) {
for (let disposable of disposables) {
for (const disposable of disposables) {
if (disposable && typeof disposable.dispose === 'function') {
disposable.dispose();
}
@@ -30,7 +32,7 @@ export class Disposable {
});
}
private _callOnDispose: Function;
private _callOnDispose?: Function;
constructor(callOnDispose: Function) {
this._callOnDispose = callOnDispose;
@@ -47,9 +49,13 @@ export class Disposable {
export class Position {
static Min(...positions: Position[]): Position {
let result = positions.pop();
for (let p of positions) {
if (p.isBefore(result)) {
if (positions.length === 0) {
throw new TypeError();
}
let result = positions[0];
for (let i = 1; i < positions.length; i++) {
let p = positions[i];
if (p.isBefore(result!)) {
result = p;
}
}
@@ -57,9 +63,13 @@ export class Position {
}
static Max(...positions: Position[]): Position {
let result = positions.pop();
for (let p of positions) {
if (p.isAfter(result)) {
if (positions.length === 0) {
throw new TypeError();
}
let result = positions[0];
for (let i = 1; i < positions.length; i++) {
let p = positions[i];
if (p.isAfter(result!)) {
result = p;
}
}
@@ -154,7 +164,7 @@ export class Position {
translate(change: { lineDelta?: number; characterDelta?: number; }): Position;
translate(lineDelta?: number, characterDelta?: number): Position;
translate(lineDeltaOrChange: number | { lineDelta?: number; characterDelta?: number; }, characterDelta: number = 0): Position {
translate(lineDeltaOrChange: number | undefined | { lineDelta?: number; characterDelta?: number; }, characterDelta: number = 0): Position {
if (lineDeltaOrChange === null || characterDelta === null) {
throw illegalArgument();
@@ -178,7 +188,7 @@ export class Position {
with(change: { line?: number; character?: number; }): Position;
with(line?: number, character?: number): Position;
with(lineOrChange: number | { line?: number; character?: number; }, character: number = this.character): Position {
with(lineOrChange: number | undefined | { line?: number; character?: number; }, character: number = this.character): Position {
if (lineOrChange === null || character === null) {
throw illegalArgument();
@@ -234,8 +244,8 @@ export class Range {
constructor(start: Position, end: Position);
constructor(startLine: number, startColumn: number, endLine: number, endColumn: number);
constructor(startLineOrStart: number | Position, startColumnOrEnd: number | Position, endLine?: number, endColumn?: number) {
let start: Position;
let end: Position;
let start: Position | undefined;
let end: Position | undefined;
if (typeof startLineOrStart === 'number' && typeof startColumnOrEnd === 'number' && typeof endLine === 'number' && typeof endColumn === 'number') {
start = new Position(startLineOrStart, startColumnOrEnd);
@@ -279,7 +289,7 @@ export class Range {
return this._start.isEqual(other._start) && this._end.isEqual(other._end);
}
intersection(other: Range): Range {
intersection(other: Range): Range | undefined {
let start = Position.Max(other.start, this._start);
let end = Position.Min(other.end, this._end);
if (start.isAfter(end)) {
@@ -312,7 +322,7 @@ export class Range {
with(change: { start?: Position, end?: Position }): Range;
with(start?: Position, end?: Position): Range;
with(startOrChange: Position | { start?: Position, end?: Position }, end: Position = this.end): Range {
with(startOrChange: Position | undefined | { start?: Position, end?: Position }, end: Position = this.end): Range {
if (startOrChange === null || end === null) {
throw illegalArgument();
@@ -371,8 +381,8 @@ export class Selection extends Range {
constructor(anchor: Position, active: Position);
constructor(anchorLine: number, anchorColumn: number, activeLine: number, activeColumn: number);
constructor(anchorLineOrAnchor: number | Position, anchorColumnOrActive: number | Position, activeLine?: number, activeColumn?: number) {
let anchor: Position;
let active: Position;
let anchor: Position | undefined;
let active: Position | undefined;
if (typeof anchorLineOrAnchor === 'number' && typeof anchorColumnOrActive === 'number' && typeof activeLine === 'number' && typeof activeColumn === 'number') {
anchor = new Position(anchorLineOrAnchor, anchorColumnOrActive);
@@ -437,7 +447,7 @@ export class TextEdit {
}
static setEndOfLine(eol: EndOfLine): TextEdit {
let ret = new TextEdit(undefined, undefined);
let ret = new TextEdit(new Range(new Position(0, 0), new Position(0, 0)), '');
ret.newEol = eol;
return ret;
}
@@ -503,8 +513,8 @@ export interface IFileOperationOptions {
export interface IFileOperation {
_type: 1;
from: URI;
to: URI;
from?: URI;
to?: URI;
options?: IFileOperationOptions;
}
@@ -557,7 +567,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
for (let i = 0; i < this._edits.length; i++) {
const element = this._edits[i];
if (element._type === 2 && element.uri.toString() === uri.toString()) {
this._edits[i] = undefined;
this._edits[i] = undefined!; // will be coalesced down below
}
}
this._edits = coalesce(this._edits);
@@ -578,9 +588,6 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
res.push(candidate.edit);
}
}
if (res.length === 0) {
return undefined;
}
return res;
}
@@ -599,8 +606,8 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
return values(textEdits);
}
_allEntries(): ([URI, TextEdit[]] | [URI, URI, IFileOperationOptions])[] {
let res: ([URI, TextEdit[]] | [URI, URI, IFileOperationOptions])[] = [];
_allEntries(): ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] {
let res: ([URI, TextEdit[]] | [URI?, URI?, IFileOperationOptions?])[] = [];
for (let edit of this._edits) {
if (edit._type === 1) {
res.push([edit.from, edit.to, edit.options]);
@@ -810,7 +817,7 @@ export class Diagnostic {
};
}
static isEqual(a: Diagnostic, b: Diagnostic): boolean {
static isEqual(a: Diagnostic | undefined, b: Diagnostic | undefined): boolean {
if (a === b) {
return true;
}
@@ -831,7 +838,7 @@ export class Diagnostic {
export class Hover {
public contents: vscode.MarkdownString[] | vscode.MarkedString[];
public range: Range;
public range: Range | undefined;
constructor(
contents: vscode.MarkdownString | vscode.MarkedString | vscode.MarkdownString[] | vscode.MarkedString[],
@@ -906,10 +913,16 @@ export enum SymbolKind {
export class SymbolInformation {
static validate(candidate: SymbolInformation): void {
if (!candidate.name) {
throw new Error('name must not be falsy');
}
}
name: string;
location: Location;
kind: SymbolKind;
containerName: string;
containerName: string | undefined;
constructor(name: string, kind: SymbolKind, containerName: string, location: Location);
constructor(name: string, kind: SymbolKind, range: Range, uri?: URI, containerName?: string);
@@ -925,8 +938,10 @@ export class SymbolInformation {
if (locationOrUri instanceof Location) {
this.location = locationOrUri;
} else if (rangeOrContainer instanceof Range) {
this.location = new Location(locationOrUri, rangeOrContainer);
this.location = new Location(locationOrUri!, rangeOrContainer);
}
SymbolInformation.validate(this);
}
toJSON(): any {
@@ -940,6 +955,19 @@ export class SymbolInformation {
}
export class DocumentSymbol {
static validate(candidate: DocumentSymbol): void {
if (!candidate.name) {
throw new Error('name must not be falsy');
}
if (!candidate.range.contains(candidate.selectionRange)) {
throw new Error('selectionRange must be contained in fullRange');
}
if (candidate.children) {
candidate.children.forEach(DocumentSymbol.validate);
}
}
name: string;
detail: string;
kind: SymbolKind;
@@ -955,9 +983,7 @@ export class DocumentSymbol {
this.selectionRange = selectionRange;
this.children = [];
if (!this.range.contains(this.selectionRange)) {
throw new Error('selectionRange must be contained in fullRange');
}
DocumentSymbol.validate(this);
}
}
@@ -1015,7 +1041,7 @@ export class CodeLens {
range: Range;
command: vscode.Command;
command: vscode.Command | undefined;
constructor(range: Range, command?: vscode.Command) {
this.range = range;
@@ -1059,10 +1085,10 @@ export class MarkdownString {
export class ParameterInformation {
label: string;
label: string | [number, number];
documentation?: string | MarkdownString;
constructor(label: string, documentation?: string | MarkdownString) {
constructor(label: string | [number, number], documentation?: string | MarkdownString) {
this.label = label;
this.documentation = documentation;
}
@@ -1092,10 +1118,10 @@ export class SignatureHelp {
}
}
export enum SignatureHelpTriggerReason {
export enum SignatureHelpTriggerKind {
Invoke = 1,
TriggerCharacter = 2,
Retrigger = 3,
ContentChange = 3,
}
export enum CompletionTriggerKind {
@@ -1140,13 +1166,14 @@ export enum CompletionItemKind {
export class CompletionItem implements vscode.CompletionItem {
label: string;
kind: CompletionItemKind;
kind: CompletionItemKind | undefined;
detail: string;
documentation: string | MarkdownString;
sortText: string;
filterText: string;
preselect: boolean;
insertText: string | SnippetString;
keepWhitespace?: boolean;
range: Range;
commitCharacters: string[];
textEdit: TextEdit;
@@ -1161,7 +1188,7 @@ export class CompletionItem implements vscode.CompletionItem {
toJSON(): any {
return {
label: this.label,
kind: CompletionItemKind[this.kind],
kind: this.kind && CompletionItemKind[this.kind],
detail: this.detail,
documentation: this.documentation,
sortText: this.sortText,
@@ -1400,7 +1427,7 @@ export class ProcessExecution implements vscode.ProcessExecution {
private _process: string;
private _args: string[];
private _options: vscode.ProcessExecutionOptions;
private _options: vscode.ProcessExecutionOptions | undefined;
constructor(process: string, options?: vscode.ProcessExecutionOptions);
constructor(process: string, args: string[], options?: vscode.ProcessExecutionOptions);
@@ -1445,11 +1472,11 @@ export class ProcessExecution implements vscode.ProcessExecution {
this._args = value;
}
get options(): vscode.ProcessExecutionOptions {
get options(): vscode.ProcessExecutionOptions | undefined {
return this._options;
}
set options(value: vscode.ProcessExecutionOptions) {
set options(value: vscode.ProcessExecutionOptions | undefined) {
this._options = value;
}
@@ -1473,7 +1500,7 @@ export class ShellExecution implements vscode.ShellExecution {
private _commandLine: string;
private _command: string | vscode.ShellQuotedString;
private _args: (string | vscode.ShellQuotedString)[];
private _options: vscode.ShellExecutionOptions;
private _options: vscode.ShellExecutionOptions | undefined;
constructor(commandLine: string, options?: vscode.ShellExecutionOptions);
constructor(command: string | vscode.ShellQuotedString, args: (string | vscode.ShellQuotedString)[], options?: vscode.ShellExecutionOptions);
@@ -1527,11 +1554,11 @@ export class ShellExecution implements vscode.ShellExecution {
this._args = value || [];
}
get options(): vscode.ShellExecutionOptions {
get options(): vscode.ShellExecutionOptions | undefined {
return this._options;
}
set options(value: vscode.ShellExecutionOptions) {
set options(value: vscode.ShellExecutionOptions | undefined) {
this._options = value;
}
@@ -1566,18 +1593,23 @@ export enum TaskScope {
export class Task implements vscode.Task {
private __id: string;
private static ProcessType: string = 'process';
private static ShellType: string = 'shell';
private static EmptyType: string = '$empty';
private __id: string | undefined;
private _definition: vscode.TaskDefinition;
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder;
private _scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined;
private _name: string;
private _execution: ProcessExecution | ShellExecution;
private _execution: ProcessExecution | ShellExecution | undefined;
private _problemMatchers: string[];
private _hasDefinedMatchers: boolean;
private _isBackground: boolean;
private _source: string;
private _group: TaskGroup;
private _group: TaskGroup | undefined;
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[]);
@@ -1613,13 +1645,15 @@ export class Task implements vscode.Task {
this._hasDefinedMatchers = false;
}
this._isBackground = false;
this._presentationOptions = Object.create(null);
this._runOptions = Object.create(null);
}
get _id(): string {
get _id(): string | undefined {
return this.__id;
}
set _id(value: string) {
set _id(value: string | undefined) {
this.__id = value;
}
@@ -1629,17 +1663,25 @@ export class Task implements vscode.Task {
}
this.__id = undefined;
this._scope = undefined;
this._definition = undefined;
this.computeDefinitionBasedOnExecution();
}
private computeDefinitionBasedOnExecution(): void {
if (this._execution instanceof ProcessExecution) {
this._definition = {
type: 'process',
type: Task.ProcessType,
id: this._execution.computeId()
};
} else if (this._execution instanceof ShellExecution) {
this._definition = {
type: 'shell',
type: Task.ShellType,
id: this._execution.computeId()
};
} else {
this._definition = {
type: Task.EmptyType,
id: generateUuid()
};
}
}
@@ -1655,7 +1697,7 @@ export class Task implements vscode.Task {
this._definition = value;
}
get scope(): vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder {
get scope(): vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined {
return this._scope;
}
@@ -1676,16 +1718,20 @@ export class Task implements vscode.Task {
this._name = value;
}
get execution(): ProcessExecution | ShellExecution {
get execution(): ProcessExecution | ShellExecution | undefined {
return this._execution;
}
set execution(value: ProcessExecution | ShellExecution) {
set execution(value: ProcessExecution | ShellExecution | 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) {
this.computeDefinitionBasedOnExecution();
}
}
get problemMatchers(): string[] {
@@ -1694,13 +1740,15 @@ export class Task implements vscode.Task {
set problemMatchers(value: string[]) {
if (!Array.isArray(value)) {
this.clear();
this._problemMatchers = [];
this._hasDefinedMatchers = false;
return;
} else {
this.clear();
this._problemMatchers = value;
this._hasDefinedMatchers = true;
}
this.clear();
this._problemMatchers = value;
this._hasDefinedMatchers = true;
}
get hasDefinedMatchers(): boolean {
@@ -1731,14 +1779,13 @@ export class Task implements vscode.Task {
this._source = value;
}
get group(): TaskGroup {
get group(): TaskGroup | undefined {
return this._group;
}
set group(value: TaskGroup) {
if (value === void 0 || value === null) {
this._group = undefined;
return;
set group(value: TaskGroup | undefined) {
if (value === null) {
value = undefined;
}
this.clear();
this._group = value;
@@ -1749,12 +1796,24 @@ export class Task implements vscode.Task {
}
set presentationOptions(value: vscode.TaskPresentationOptions) {
if (value === null) {
value = undefined;
if (value === null || value === undefined) {
value = Object.create(null);
}
this.clear();
this._presentationOptions = value;
}
get runOptions(): vscode.RunOptions {
return this._runOptions;
}
set runOptions(value: vscode.RunOptions) {
if (value === null || value === undefined) {
value = Object.create(null);
}
this.clear();
this._runOptions = value;
}
}
@@ -1766,16 +1825,16 @@ export enum ProgressLocation {
export class TreeItem {
label?: string;
label?: string | vscode.TreeItemLabel;
resourceUri?: URI;
iconPath?: string | URI | { light: string | URI; dark: string | URI };
command?: vscode.Command;
contextValue?: string;
tooltip?: string;
constructor(label: string, collapsibleState?: vscode.TreeItemCollapsibleState)
constructor(label: string | vscode.TreeItemLabel, collapsibleState?: vscode.TreeItemCollapsibleState)
constructor(resourceUri: URI, collapsibleState?: vscode.TreeItemCollapsibleState)
constructor(arg1: string | URI, public collapsibleState: vscode.TreeItemCollapsibleState = TreeItemCollapsibleState.None) {
constructor(arg1: string | vscode.TreeItemLabel | URI, public collapsibleState: vscode.TreeItemCollapsibleState = TreeItemCollapsibleState.None) {
if (arg1 instanceof URI) {
this.resourceUri = arg1;
} else {
@@ -1852,6 +1911,8 @@ export class RelativePattern implements IRelativePattern {
export class Breakpoint {
private _id: string | undefined;
readonly enabled: boolean;
readonly condition?: string;
readonly hitCondition?: string;
@@ -1869,6 +1930,13 @@ export class Breakpoint {
this.logMessage = logMessage;
}
}
get id(): string {
if (!this._id) {
this._id = generateUuid();
}
return this._id;
}
}
export class SourceBreakpoint extends Breakpoint {
@@ -1896,24 +1964,20 @@ export class FunctionBreakpoint extends Breakpoint {
}
export class DebugAdapterExecutable implements vscode.DebugAdapterExecutable {
readonly type = 'executable';
readonly command: string;
readonly args: string[];
readonly env?: { [key: string]: string };
readonly cwd?: string;
readonly options?: vscode.DebugAdapterExecutableOptions;
constructor(command: string, args?: string[], env?: { [key: string]: string }, cwd?: string) {
constructor(command: string, args: string[], options?: vscode.DebugAdapterExecutableOptions) {
this.command = command;
this.args = args;
this.env = env;
this.cwd = cwd;
this.args = args || [];
this.options = options;
}
}
export class DebugAdapterServer implements vscode.DebugAdapterServer {
readonly type = 'server';
readonly port: number;
readonly host: string;
readonly host?: string;
constructor(port: number, host?: string) {
this.port = port;
@@ -1921,14 +1985,15 @@ export class DebugAdapterServer implements vscode.DebugAdapterServer {
}
}
/*
export class DebugAdapterImplementation implements vscode.DebugAdapterImplementation {
readonly type = 'implementation';
readonly implementation: any;
constructor(transport: any) {
this.implementation = transport;
}
}
*/
export enum LogLevel {
Trace = 1,

View File

@@ -6,7 +6,6 @@
import * as vscode from 'vscode';
import { MainContext, IMainContext, ExtHostUrlsShape, MainThreadUrlsShape } from './extHost.protocol';
import { URI, UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { toDisposable } from 'vs/base/common/lifecycle';
import { onUnexpectedError } from 'vs/base/common/errors';
@@ -41,11 +40,11 @@ export class ExtHostUrls implements ExtHostUrlsShape {
});
}
$handleExternalUri(handle: number, uri: UriComponents): Thenable<void> {
$handleExternalUri(handle: number, uri: UriComponents): Promise<void> {
const handler = this.handlers.get(handle);
if (!handler) {
return TPromise.as(null);
return Promise.resolve(null);
}
try {
handler.handleUri(URI.revive(uri));
@@ -53,6 +52,6 @@ export class ExtHostUrls implements ExtHostUrlsShape {
onUnexpectedError(err);
}
return TPromise.as(null);
return Promise.resolve(null);
}
}

View File

@@ -5,12 +5,12 @@
import { Emitter, Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
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 { Disposable } from './extHostTypes';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
type IconPath = URI | { light: URI, dark: URI };
@@ -62,7 +62,7 @@ export class ExtHostWebview implements vscode.Webview {
this._options = newOptions;
}
public postMessage(message: any): Thenable<boolean> {
public postMessage(message: any): Promise<boolean> {
this.assertNotDisposed();
return this._proxy.$postMessage(this._handle, message);
}
@@ -201,7 +201,7 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
this._visible = value;
}
public postMessage(message: any): Thenable<boolean> {
public postMessage(message: any): Promise<boolean> {
this.assertNotDisposed();
return this._proxy.$postMessage(this._handle, message);
}
@@ -239,7 +239,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
public createWebview(
extensionLocation: URI,
extension: IExtensionDescription,
viewType: string,
title: string,
showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean },
@@ -252,7 +252,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
};
const handle = ExtHostWebviews.newHandle();
this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extensionLocation);
this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extension.id, extension.extensionLocation);
const webview = new ExtHostWebview(handle, this._proxy, options);
const panel = new ExtHostWebviewPanel(handle, this._proxy, viewType, title, viewColumn, options, webview);
@@ -305,13 +305,13 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
}
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Thenable<void> {
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void> {
const panel = this.getWebviewPanel(handle);
if (panel) {
panel.dispose();
this._webviewPanels.delete(handle);
}
return TPromise.as(void 0);
return Promise.resolve(void 0);
}
$deserializeWebviewPanel(
@@ -321,16 +321,16 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
state: any,
position: EditorViewColumn,
options: vscode.WebviewOptions & vscode.WebviewPanelOptions
): Thenable<void> {
): Promise<void> {
const serializer = this._serializers.get(viewType);
if (!serializer) {
return TPromise.wrapError(new Error(`No serializer found for '${viewType}'`));
return Promise.reject(new Error(`No serializer found for '${viewType}'`));
}
const webview = new ExtHostWebview(webviewHandle, this._proxy, options);
const revivedPanel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, typeConverters.ViewColumn.to(position), options, webview);
this._webviewPanels.set(webviewHandle, revivedPanel);
return serializer.deserializeWebviewPanel(revivedPanel, state);
return Promise.resolve(serializer.deserializeWebviewPanel(revivedPanel, state));
}
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewPanel | undefined {

View File

@@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { join, relative } from 'path';
import { delta as arrayDelta } from 'vs/base/common/arrays';
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';
@@ -13,17 +14,16 @@ import { isLinux } from 'vs/base/common/platform';
import { basenameOrAuthority, dirname, isEqual } from 'vs/base/common/resources';
import { compare } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { localize } from 'vs/nls';
import { ILogService } from 'vs/platform/log/common/log';
import { Severity } from 'vs/platform/notification/common/notification';
import { IQueryOptions, IRawFileMatch2 } from 'vs/platform/search/common/search';
import { IRawFileMatch2, resultIsMatch } from 'vs/platform/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 * as vscode from 'vscode';
import { ExtHostWorkspaceShape, IMainContext, IWorkspaceData, MainContext, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol';
import { CancellationToken } from 'vs/base/common/cancellation';
function isFolderEqual(folderA: URI, folderB: URI): boolean {
return isEqual(folderA, folderB, !isLinux);
@@ -232,7 +232,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
// Trigger on main side
if (this._proxy) {
const extName = extension.displayName || extension.name;
this._proxy.$updateWorkspaceFolders(extName, index, deleteCount, validatedDistinctWorkspaceFoldersToAdd).then(null, error => {
this._proxy.$updateWorkspaceFolders(extName, index, deleteCount, validatedDistinctWorkspaceFoldersToAdd).then(void 0, error => {
// in case of an error, make sure to clear out the unconfirmed workspace
// because we cannot expect the acknowledgement from the main side for this
@@ -345,7 +345,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
// --- search ---
findFiles(include: string | RelativePattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Thenable<vscode.Uri[]> {
findFiles(include: string | RelativePattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.Uri[]> {
this._logService.trace(`extHostWorkspace#findFiles: fileSearch, extension: ${extensionId}, entryPoint: findFiles`);
let includePattern: string;
@@ -373,14 +373,14 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
}
if (token && token.isCancellationRequested) {
return TPromise.wrap([]);
return Promise.resolve([]);
}
return this._proxy.$startFileSearch(includePattern, includeFolder, excludePatternOrDisregardExcludes, maxResults, token)
.then(data => Array.isArray(data) ? data.map(URI.revive) : []);
}
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Thenable<vscode.TextSearchComplete> {
findTextInFiles(query: vscode.TextSearchQuery, options: vscode.FindTextInFilesOptions, callback: (result: vscode.TextSearchResult) => void, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Promise<vscode.TextSearchComplete> {
this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId}, entryPoint: findTextInFiles`);
const requestId = this._requestIdProvider.getNext();
@@ -400,7 +400,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
} :
options.previewOptions;
const queryOptions: IQueryOptions = {
const queryOptions: ITextQueryBuilderOptions = {
ignoreSymlinks: typeof options.followSymlinks === 'boolean' ? !options.followSymlinks : undefined,
disregardIgnoreFiles: typeof options.useIgnoreFiles === 'boolean' ? !options.useIgnoreFiles : undefined,
disregardGlobalIgnoreFiles: typeof options.useGlobalIgnoreFiles === 'boolean' ? !options.useGlobalIgnoreFiles : undefined,
@@ -408,6 +408,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
fileEncoding: options.encoding,
maxResults: options.maxResults,
previewOptions,
afterContext: options.afterContext,
beforeContext: options.beforeContext,
includePattern: options.include && globPatternToString(options.include),
excludePattern: options.exclude && globPatternToString(options.exclude)
@@ -420,31 +422,42 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
return;
}
p.matches.forEach(match => {
callback({
uri: URI.revive(p.resource),
preview: {
text: match.preview.text,
match: new Range(match.preview.match.startLineNumber, match.preview.match.startColumn, match.preview.match.endLineNumber, match.preview.match.endColumn)
},
range: new Range(match.range.startLineNumber, match.range.startColumn, match.range.endLineNumber, match.range.endColumn)
});
const uri = URI.revive(p.resource);
p.results.forEach(result => {
if (resultIsMatch(result)) {
callback(<vscode.TextSearchMatch>{
uri,
preview: {
text: result.preview.text,
matches: mapArrayOrNot(
result.preview.matches,
m => new Range(m.startLineNumber, m.startColumn, m.endLineNumber, m.endColumn))
},
ranges: mapArrayOrNot(
result.ranges,
r => new Range(r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn))
});
} else {
callback(<vscode.TextSearchContext>{
uri,
text: result.text,
lineNumber: result.lineNumber
});
}
});
};
if (token.isCancellationRequested) {
return TPromise.wrap(undefined);
return Promise.resolve(undefined);
}
return this._proxy.$startTextSearch(query, queryOptions, requestId, token).then(
result => {
delete this._activeSearchCallbacks[requestId];
return result;
},
err => {
delete this._activeSearchCallbacks[requestId];
return TPromise.wrapError(err);
});
return this._proxy.$startTextSearch(query, queryOptions, requestId, token).then(result => {
delete this._activeSearchCallbacks[requestId];
return result;
}, err => {
delete this._activeSearchCallbacks[requestId];
return Promise.reject(err);
});
}
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void {
@@ -453,7 +466,11 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
}
}
saveAll(includeUntitled?: boolean): Thenable<boolean> {
saveAll(includeUntitled?: boolean): Promise<boolean> {
return this._proxy.$saveAll(includeUntitled);
}
resolveProxy(url: string): Promise<string> {
return this._proxy.$resolveProxy(url);
}
}