Merge remote-tracking branch 'origin/master' into pr/limerickgds/51557

This commit is contained in:
Alex Dima
2018-09-11 15:30:40 +02:00
10817 changed files with 109048 additions and 208700 deletions

View File

@@ -4,12 +4,14 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { isMalformedFileUri } from 'vs/base/common/resources';
import * as vscode from 'vscode';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
// -----------------------------------------------------------------
// The following commands are registered on both sides separately.
@@ -47,8 +49,14 @@ export class OpenFolderAPICommand {
if (!uri) {
return executor.executeCommand('_files.pickFolderAndOpen', forceNewWindow);
}
let correctedUri = isMalformedFileUri(uri);
if (correctedUri) {
// workaround for #55916 and #55891, will be removed in 1.28
console.warn(`'vscode.openFolder' command invoked with an invalid URI (file:// scheme missing): '${uri}'. Converted to a 'file://' URI: ${correctedUri}`);
uri = correctedUri;
}
return executor.executeCommand('_files.windowOpen', [uri.fsPath], forceNewWindow);
return executor.executeCommand('_files.windowOpen', [uri], forceNewWindow);
}
}
CommandsRegistry.registerCommand(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute));
@@ -98,3 +106,11 @@ export class RemoveFromRecentlyOpenedAPICommand {
}
}
CommandsRegistry.registerCommand(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute));
export class SetEditorLayoutAPICommand {
public static ID = 'vscode.setEditorLayout';
public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Thenable<any> {
return executor.executeCommand('layoutEditorGroups', layout);
}
}
CommandsRegistry.registerCommand(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute));

View File

@@ -37,7 +37,7 @@ 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 { 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';
@@ -46,7 +46,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as vscode from 'vscode';
import * as paths from 'vs/base/common/paths';
import * as files from 'vs/platform/files/common/files';
import { MainContext, ExtHostContext, IInitData, IExtHostContext } from './extHost.protocol';
import { 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';
@@ -61,6 +61,7 @@ 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';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
@@ -80,7 +81,7 @@ function proposedApiFunction<T>(extension: IExtensionDescription, fn: T): T {
if (extension.enableProposedApi) {
return fn;
} else {
return <any>throwProposedApiError;
return throwProposedApiError.bind(null, extension);
}
}
@@ -89,7 +90,7 @@ function proposedApiFunction<T>(extension: IExtensionDescription, fn: T): T {
*/
export function createApiFactory(
initData: IInitData,
rpcProtocol: IExtHostContext,
rpcProtocol: IMainContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
extensionService: ExtHostExtensionService,
@@ -110,16 +111,16 @@ export function createApiFactory(
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadTextEditors)));
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
const extHostCommands = rpcProtocol.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(rpcProtocol, extHostHeapService, extHostLogService));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands));
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace);
const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration));
rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration);
const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, schemeTransformer, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, schemeTransformer, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics, extHostLogService));
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService());
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors));
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService));
const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer));
const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration));
@@ -136,9 +137,12 @@ export function createApiFactory(
const extHostMessageService = new ExtHostMessageService(rpcProtocol);
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol);
const extHostOutputService = new ExtHostOutputService(rpcProtocol);
const extHostOutputService = new ExtHostOutputService(initData.logsLocation, rpcProtocol);
const extHostLanguages = new ExtHostLanguages(rpcProtocol);
// Register an output channel for exthost log
extHostOutputService.createOutputChannelFromLogFile(localize('extensionsLog', "Extension Host"), extHostLogService.logFile);
// Register API-ish commands
ExtHostApiCommands.register(extHostCommands);
@@ -226,8 +230,15 @@ export function createApiFactory(
get sessionId() { return initData.telemetryInfo.sessionId; },
get language() { return platform.language; },
get appName() { return product.nameLong; },
get appRoot() { return initData.environment.appRoot; },
get logLevel() { return extHostLogService.getLevel(); }
get appRoot() { return initData.environment.appRoot.fsPath; },
get logLevel() {
checkProposedApiEnabled(extension);
return extHostLogService.getLevel();
},
get onDidChangeLogLevel() {
checkProposedApiEnabled(extension);
return extHostLogService.onDidChangeLogLevel;
}
});
// namespace: extensions
@@ -255,14 +266,18 @@ export function createApiFactory(
getDiagnostics: (resource?: vscode.Uri) => {
return <any>extHostDiagnostics.getDiagnostics(resource);
},
getLanguages(): TPromise<string[]> {
getLanguages(): Thenable<string[]> {
return extHostLanguages.getLanguages();
},
changeLanguage(document: vscode.TextDocument, languageId: string): Thenable<void> {
checkProposedApiEnabled(extension);
return extHostLanguages.changeLanguage(document.uri, languageId);
},
match(selector: vscode.DocumentSelector, document: vscode.TextDocument): number {
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, metadata);
return extHostLanguageFeatures.registerCodeActionProvider(checkSelector(selector), provider, extension, metadata);
},
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCodeLensProvider(checkSelector(selector), provider);
@@ -289,7 +304,7 @@ export function createApiFactory(
return extHostLanguageFeatures.registerRenameProvider(checkSelector(selector), provider);
},
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDocumentSymbolProvider(checkSelector(selector), provider, extension.id);
return extHostLanguageFeatures.registerDocumentSymbolProvider(checkSelector(selector), provider, extension);
},
registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable {
return extHostLanguageFeatures.registerWorkspaceSymbolProvider(provider);
@@ -331,10 +346,13 @@ export function createApiFactory(
get visibleTextEditors() {
return extHostEditors.getVisibleTextEditors();
},
get terminals() {
return proposedApiFunction(extension, extHostTerminalService.terminals);
get activeTerminal() {
return proposedApiFunction(extension, extHostTerminalService.activeTerminal);
},
showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): TPromise<vscode.TextEditor> {
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>;
if (URI.isUri(documentOrUri)) {
documentPromise = TPromise.wrap(workspace.openTextDocument(documentOrUri));
@@ -369,8 +387,11 @@ export function createApiFactory(
onDidCloseTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidCloseTerminal(listener, thisArg, disposables);
},
onDidOpenTerminal: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
onDidOpenTerminal(listener, thisArg?, disposables?) {
return extHostTerminalService.onDidOpenTerminal(listener, thisArg, disposables);
},
onDidChangeActiveTerminal: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostTerminalService.onDidChangeActiveTerminal(listener, thisArg, disposables);
}),
get state() {
return extHostWindow.state;
@@ -388,13 +409,13 @@ export function createApiFactory(
return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest);
},
showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken): any {
return extHostQuickOpen.showQuickPick(undefined, items, options, token);
return extHostQuickOpen.showQuickPick(items, options, token);
},
showWorkspaceFolderPick(options: vscode.WorkspaceFolderPickOptions) {
return extHostQuickOpen.showWorkspaceFolderPick(options);
},
showInputBox(options?: vscode.InputBoxOptions, token?: vscode.CancellationToken) {
return extHostQuickOpen.showInput(undefined, options, token);
return extHostQuickOpen.showInput(options, token);
},
showOpenDialog(options) {
return extHostDialogs.showOpenDialog(options);
@@ -415,11 +436,11 @@ export function createApiFactory(
withProgress<R>(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; worked?: number }>, token: vscode.CancellationToken) => Thenable<R>) {
return extHostProgress.withProgress(extension, options, task);
},
createOutputChannel(name: string): vscode.OutputChannel {
return extHostOutputService.createOutputChannel(name);
createOutputChannel(name: string, push?: boolean): vscode.OutputChannel {
return extHostOutputService.createOutputChannel(name, push);
},
createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
return extHostWebviews.createWebview(viewType, title, showOptions, options, extension.extensionLocation);
return extHostWebviews.createWebview(extension.extensionLocation, viewType, title, showOptions, options);
},
createTerminal(nameOrOptions: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
if (typeof nameOrOptions === 'object') {
@@ -427,12 +448,18 @@ export function createApiFactory(
}
return extHostTerminalService.createTerminal(<string>nameOrOptions, shellPath, shellArgs);
},
createTerminalRenderer: proposedApiFunction(extension, (name: string) => {
return extHostTerminalService.createTerminalRenderer(name);
}),
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider);
},
createTreeView(viewId: string, options: { treeDataProvider: vscode.TreeDataProvider<any> }): vscode.TreeView<any> {
return extHostTreeViews.createTreeView(viewId, options);
},
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!', {}, []);
@@ -440,12 +467,15 @@ export function createApiFactory(
registerDecorationProvider: proposedApiFunction(extension, (provider: vscode.DecorationProvider) => {
return extHostDecorations.registerDecorationProvider(provider, extension.id);
}),
registerWebviewPanelSerializer: proposedApiFunction(extension, (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
return extHostWebviews.registerWebviewPanelSerializer(viewType, serializer);
}),
registerProtocolHandler: proposedApiFunction(extension, (handler: vscode.ProtocolHandler) => {
return extHostUrls.registerProtocolHandler(extension.id, handler);
})
registerUriHandler(handler: vscode.UriHandler) {
return extHostUrls.registerUriHandler(extension.id, handler);
},
createQuickPick<T extends vscode.QuickPickItem>(): vscode.QuickPick<T> {
return extHostQuickOpen.createQuickPick(extension.id);
},
createInputBox(): vscode.InputBox {
return extHostQuickOpen.createInputBox(extension.id);
},
};
// namespace: workspace
@@ -463,7 +493,7 @@ export function createApiFactory(
return extHostWorkspace.getWorkspaceFolders();
},
get name() {
return extHostWorkspace.workspace ? extHostWorkspace.workspace.name : undefined;
return extHostWorkspace.name;
},
set name(value) {
throw errors.readonly();
@@ -480,10 +510,25 @@ export function createApiFactory(
findFiles: (include, exclude, maxResults?, token?) => {
return extHostWorkspace.findFiles(typeConverters.GlobPattern.from(include), typeConverters.GlobPattern.from(exclude), maxResults, extension.id, token);
},
findTextInFiles: (query: vscode.TextSearchQuery, optionsOrCallback, callbackOrToken?, token?: vscode.CancellationToken) => {
let options: vscode.FindTextInFilesOptions;
let callback: (result: vscode.TextSearchResult) => void;
if (typeof optionsOrCallback === 'object') {
options = optionsOrCallback;
callback = callbackOrToken;
} else {
options = {};
callback = optionsOrCallback;
token = callbackOrToken;
}
return extHostWorkspace.findTextInFiles(query, options || {}, callback, extension.id, token);
},
saveAll: (includeUntitled?) => {
return extHostWorkspace.saveAll(includeUntitled);
},
applyEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
applyEdit(edit: vscode.WorkspaceEdit): Thenable<boolean> {
return extHostEditors.applyWorkspaceEdit(edit);
},
createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): vscode.FileSystemWatcher => {
@@ -496,7 +541,7 @@ export function createApiFactory(
throw errors.readonly();
},
openTextDocument(uriOrFileNameOrOptions?: vscode.Uri | string | { language?: string; content?: string; }) {
let uriPromise: TPromise<URI>;
let uriPromise: Thenable<URI>;
let options = uriOrFileNameOrOptions as { language?: string; content?: string; };
if (typeof uriOrFileNameOrOptions === 'string') {
@@ -547,8 +592,18 @@ export function createApiFactory(
registerFileSystemProvider(scheme, provider, options) {
return extHostFileSystem.registerFileSystemProvider(scheme, provider, options);
},
registerSearchProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostSearch.registerSearchProvider(scheme, provider);
registerFileSearchProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostSearch.registerFileSearchProvider(scheme, provider);
}),
registerSearchProvider: proposedApiFunction(extension, () => {
// Temp for live share in Insiders
return { dispose: () => { } };
}),
registerTextSearchProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostSearch.registerTextSearchProvider(scheme, provider);
}),
registerFileIndexProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostSearch.registerFileIndexProvider(scheme, provider);
}),
registerDocumentCommentProvider: proposedApiFunction(extension, (provider: vscode.DocumentCommentProvider) => {
return exthostCommentProviders.registerDocumentCommentProvider(provider);
@@ -556,8 +611,11 @@ export function createApiFactory(
registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => {
return exthostCommentProviders.registerWorkspaceCommentProvider(provider);
}),
onDidRenameResource: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostDocuments.onDidRenameResource(listener, thisArg, disposables);
onDidRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostFileSystemEvent.onDidRenameFile(listener, thisArg, disposables);
}),
onWillRenameFile: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables);
})
};
@@ -642,39 +700,48 @@ export function createApiFactory(
version: pkg.version,
// namespaces
commands,
debug,
env,
extensions,
languages,
scm,
tasks,
window,
workspace,
scm,
debug,
tasks,
// types
Breakpoint: extHostTypes.Breakpoint,
CancellationTokenSource: CancellationTokenSource,
CodeAction: extHostTypes.CodeAction,
CodeActionKind: extHostTypes.CodeActionKind,
CodeActionTrigger: extHostTypes.CodeActionTrigger,
CodeLens: extHostTypes.CodeLens,
Color: extHostTypes.Color,
ColorPresentation: extHostTypes.ColorPresentation,
ColorInformation: extHostTypes.ColorInformation,
CodeActionTrigger: extHostTypes.CodeActionTrigger,
EndOfLine: extHostTypes.EndOfLine,
ColorPresentation: extHostTypes.ColorPresentation,
CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState,
CompletionItem: extHostTypes.CompletionItem,
CompletionItemKind: extHostTypes.CompletionItemKind,
CompletionList: extHostTypes.CompletionList,
CompletionTriggerKind: extHostTypes.CompletionTriggerKind,
ConfigurationTarget: extHostTypes.ConfigurationTarget,
DebugAdapterExecutable: extHostTypes.DebugAdapterExecutable,
DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior,
Diagnostic: extHostTypes.Diagnostic,
DiagnosticRelatedInformation: extHostTypes.DiagnosticRelatedInformation,
DiagnosticTag: extHostTypes.DiagnosticTag,
DiagnosticSeverity: extHostTypes.DiagnosticSeverity,
DiagnosticTag: extHostTypes.DiagnosticTag,
Disposable: extHostTypes.Disposable,
DocumentHighlight: extHostTypes.DocumentHighlight,
DocumentHighlightKind: extHostTypes.DocumentHighlightKind,
DocumentLink: extHostTypes.DocumentLink,
DocumentSymbol: extHostTypes.DocumentSymbol,
EndOfLine: extHostTypes.EndOfLine,
EventEmitter: Emitter,
FileChangeType: extHostTypes.FileChangeType,
FileSystemError: extHostTypes.FileSystemError,
FileType: files.FileType,
FoldingRange: extHostTypes.FoldingRange,
FoldingRangeKind: extHostTypes.FoldingRangeKind,
FunctionBreakpoint: extHostTypes.FunctionBreakpoint,
Hover: extHostTypes.Hover,
IndentAction: languageConfiguration.IndentAction,
@@ -684,60 +751,58 @@ export function createApiFactory(
OverviewRulerLane: OverviewRulerLane,
ParameterInformation: extHostTypes.ParameterInformation,
Position: extHostTypes.Position,
ProcessExecution: extHostTypes.ProcessExecution,
ProgressLocation: extHostTypes.ProgressLocation,
QuickInputButtons: extHostTypes.QuickInputButtons,
Range: extHostTypes.Range,
RelativePattern: extHostTypes.RelativePattern,
Selection: extHostTypes.Selection,
ShellExecution: extHostTypes.ShellExecution,
ShellQuoting: extHostTypes.ShellQuoting,
SignatureHelpTriggerReason: extHostTypes.SignatureHelpTriggerReason,
SignatureHelp: extHostTypes.SignatureHelp,
SignatureInformation: extHostTypes.SignatureInformation,
SnippetString: extHostTypes.SnippetString,
SourceBreakpoint: extHostTypes.SourceBreakpoint,
SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType,
StatusBarAlignment: extHostTypes.StatusBarAlignment,
SymbolInformation: extHostTypes.SymbolInformation,
SymbolInformation2: class extends extHostTypes.SymbolInformation2 {
constructor(name, kind, containerName, location) {
checkProposedApiEnabled(extension);
super(name, kind, containerName, location);
}
},
SymbolKind: extHostTypes.SymbolKind,
SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType,
Task: extHostTypes.Task,
TaskGroup: extHostTypes.TaskGroup,
TaskPanelKind: extHostTypes.TaskPanelKind,
TaskRevealKind: extHostTypes.TaskRevealKind,
TaskScope: extHostTypes.TaskScope,
TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason,
TextEdit: extHostTypes.TextEdit,
TextEditorCursorStyle: TextEditorCursorStyle,
TextEditorLineNumbersStyle: extHostTypes.TextEditorLineNumbersStyle,
TextEditorRevealType: extHostTypes.TextEditorRevealType,
TextEditorSelectionChangeKind: extHostTypes.TextEditorSelectionChangeKind,
DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior,
ThemeColor: extHostTypes.ThemeColor,
ThemeIcon: extHostTypes.ThemeIcon,
TreeItem: extHostTypes.TreeItem,
TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState,
Uri: URI,
ViewColumn: extHostTypes.ViewColumn,
WorkspaceEdit: extHostTypes.WorkspaceEdit,
ProgressLocation: extHostTypes.ProgressLocation,
TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState,
ThemeIcon: extHostTypes.ThemeIcon,
TreeItem: extHostTypes.TreeItem,
ThemeColor: extHostTypes.ThemeColor,
// functions
TaskRevealKind: extHostTypes.TaskRevealKind,
TaskPanelKind: extHostTypes.TaskPanelKind,
TaskGroup: extHostTypes.TaskGroup,
ProcessExecution: extHostTypes.ProcessExecution,
ShellExecution: extHostTypes.ShellExecution,
ShellQuoting: extHostTypes.ShellQuoting,
TaskScope: extHostTypes.TaskScope,
Task: extHostTypes.Task,
ConfigurationTarget: extHostTypes.ConfigurationTarget,
RelativePattern: extHostTypes.RelativePattern,
FileChangeType: extHostTypes.FileChangeType,
FileType: files.FileType,
FileSystemError: extHostTypes.FileSystemError,
FoldingRange: extHostTypes.FoldingRange,
FoldingRangeKind: extHostTypes.FoldingRangeKind,
CommentThreadCollapsibleState: extHostTypes.CommentThreadCollapsibleState
};
};
}
/**
* Returns the original fs path (using the original casing for the drive letter)
*/
export function originalFSPath(uri: URI): string {
const result = uri.fsPath;
if (/^[a-zA-Z]:/.test(result) && uri.path.charAt(1).toLowerCase() === result.charAt(0)) {
// Restore original drive letter casing
return uri.path.charAt(1) + result.substr(1);
}
return result;
}
class Extension<T> implements vscode.Extension<T> {
private _extensionService: ExtHostExtensionService;
@@ -749,7 +814,7 @@ class Extension<T> implements vscode.Extension<T> {
constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription) {
this._extensionService = extensionService;
this.id = description.id;
this.extensionPath = paths.normalize(description.extensionLocation.fsPath, true);
this.extensionPath = paths.normalize(originalFSPath(description.extensionLocation), true);
this.packageJSON = description;
}
@@ -796,7 +861,9 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT
// fall back to a default implementation
if (!defaultApiImpl) {
console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}`);
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);
}
return defaultApiImpl;

View File

@@ -4,58 +4,50 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createMainContextProxyIdentifier as createMainId, createExtHostContextProxyIdentifier as createExtId, ProxyIdentifier, IRPCProtocol } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import * as vscode from 'vscode';
import URI, { UriComponents } from 'vs/base/common/uri';
import { SerializedError } from 'vs/base/common/errors';
import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as modes from 'vs/editor/common/modes';
import { IConfigurationData, ConfigurationTarget, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { IConfig, IAdapterExecutable, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug';
import { IPickOpenEntry, IPickOptions } from 'vs/platform/quickinput/common/quickInput';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { URI, UriComponents } from 'vs/base/common/uri';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { ITreeItem } from 'vs/workbench/common/views';
import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { SerializedError } from 'vs/base/common/errors';
import { IStat, FileChangeType, IWatchOptions, FileSystemProviderCapabilities, FileWriteOptions, FileType, FileOverwriteOptions } from 'vs/platform/files/common/files';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { CommentRule, CharacterPair, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { IPatternInfo, IRawSearchQuery, IRawFileMatch2, ISearchCompleteStats } from 'vs/platform/search/common/search';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import * as modes from 'vs/editor/common/modes';
import { CharacterPair, CommentRule, EnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ConfigurationTarget, IConfigurationData, IConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IStat, IWatchOptions } from 'vs/platform/files/common/files';
import { LabelRules } from 'vs/platform/label/common/label';
import { LogLevel } from 'vs/platform/log/common/log';
import { TaskExecutionDTO, TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO } from 'vs/workbench/api/shared/tasks';
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 { 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 { IAdapterExecutable, IConfig, ITerminalSettings } from 'vs/workbench/parts/debug/common/debug';
import { TaskSet } from 'vs/workbench/parts/tasks/common/tasks';
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 { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
import { CancellationToken } from 'vs/base/common/cancellation';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
appRoot: string;
appSettingsHome: string;
disableExtensions: boolean;
extensionDevelopmentPath: string;
appRoot: URI;
appSettingsHome: URI;
extensionDevelopmentLocationURI: URI;
extensionTestsPath: string;
}
@@ -73,9 +65,8 @@ export interface IInitData {
extensions: IExtensionDescription[];
configuration: IConfigurationInitData;
telemetryInfo: ITelemetryInfo;
windowId: number;
logLevel: LogLevel;
logsPath: string;
logsLocation: URI;
}
export interface IConfigurationInitData extends IConfigurationData {
@@ -111,8 +102,8 @@ export interface MainThreadCommentsShape extends IDisposable {
}
export interface MainThreadConfigurationShape extends IDisposable {
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: UriComponents): TPromise<void>;
$removeConfigurationOption(target: ConfigurationTarget, key: string, resource: UriComponents): TPromise<void>;
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: UriComponents): Thenable<void>;
$removeConfigurationOption(target: ConfigurationTarget, key: string, resource: UriComponents): Thenable<void>;
}
export interface MainThreadDiagnosticsShape extends IDisposable {
@@ -153,9 +144,9 @@ export interface MainThreadDocumentContentProvidersShape extends IDisposable {
}
export interface MainThreadDocumentsShape extends IDisposable {
$tryCreateDocument(options?: { language?: string; content?: string; }): TPromise<UriComponents>;
$tryOpenDocument(uri: UriComponents): TPromise<void>;
$trySaveDocument(uri: UriComponents): TPromise<boolean>;
$tryCreateDocument(options?: { language?: string; content?: string; }): Thenable<UriComponents>;
$tryOpenDocument(uri: UriComponents): Thenable<void>;
$trySaveDocument(uri: UriComponents): Thenable<boolean>;
}
export interface ITextEditorConfigurationUpdate {
@@ -196,26 +187,26 @@ export interface ITextDocumentShowOptions {
}
export interface MainThreadTextEditorsShape extends IDisposable {
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): TPromise<string>;
$tryShowTextDocument(resource: UriComponents, options: ITextDocumentShowOptions): Thenable<string>;
$registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void;
$removeTextEditorDecorationType(key: string): void;
$tryShowEditor(id: string, position: EditorViewColumn): TPromise<void>;
$tryHideEditor(id: string): TPromise<void>;
$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): TPromise<void>;
$trySetDecorations(id: string, key: string, ranges: editorCommon.IDecorationOptions[]): TPromise<void>;
$trySetDecorationsFast(id: string, key: string, ranges: number[]): TPromise<void>;
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<void>;
$trySetSelections(id: string, selections: ISelection[]): TPromise<void>;
$tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): TPromise<boolean>;
$tryApplyWorkspaceEdit(workspaceEditDto: WorkspaceEditDto): TPromise<boolean>;
$tryInsertSnippet(id: string, template: string, selections: IRange[], opts: IUndoStopOptions): TPromise<boolean>;
$getDiffInformation(id: string): TPromise<editorCommon.ILineChange[]>;
$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[]>;
}
export interface MainThreadTreeViewsShape extends IDisposable {
$registerTreeViewDataProvider(treeViewId: string): void;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): TPromise<void>;
$reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options?: { select?: boolean }): TPromise<void>;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Thenable<void>;
$reveal(treeViewId: string, treeItem: ITreeItem, parentChain: ITreeItem[], options: { select: boolean, focus: boolean }): Thenable<void>;
}
export interface MainThreadErrorsShape extends IDisposable {
@@ -296,7 +287,8 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
}
export interface MainThreadLanguagesShape extends IDisposable {
$getLanguages(): TPromise<string[]>;
$getLanguages(): Thenable<string[]>;
$changeLanguage(resource: UriComponents, languageId: string): Thenable<void>;
}
export interface MainThreadMessageOptions {
@@ -309,11 +301,12 @@ export interface MainThreadMessageServiceShape extends IDisposable {
}
export interface MainThreadOutputServiceShape extends IDisposable {
$append(channelId: string, label: string, value: string): TPromise<void>;
$clear(channelId: string, label: string): TPromise<void>;
$dispose(channelId: string, label: string): TPromise<void>;
$reveal(channelId: string, label: string, preserveFocus: boolean): TPromise<void>;
$close(channelId: string): TPromise<void>;
$register(label: string, log: boolean, file?: UriComponents): Thenable<string>;
$append(channelId: string, value: string): Thenable<void>;
$clear(channelId: string): Thenable<void>;
$reveal(channelId: string, preserveFocus: boolean): Thenable<void>;
$close(channelId: string): Thenable<void>;
$dispose(channelId: string): Thenable<void>;
}
export interface MainThreadProgressShape extends IDisposable {
@@ -324,28 +317,99 @@ 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): TPromise<number>;
$createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean): Thenable<number>;
$createTerminalRenderer(name: string): Thenable<number>;
$dispose(terminalId: number): void;
$hide(terminalId: number): void;
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
$show(terminalId: number, preserveFocus: boolean): void;
$registerOnDataListener(terminalId: number): void;
// Process
$sendProcessTitle(terminalId: number, title: string): void;
$sendProcessData(terminalId: number, data: string): void;
$sendProcessPid(terminalId: number, pid: number): void;
$sendProcessExit(terminalId: number, exitCode: number): void;
// Renderer
$terminalRendererSetName(terminalId: number, name: string): void;
$terminalRendererSetDimensions(terminalId: number, dimensions: ITerminalDimensions): void;
$terminalRendererWrite(terminalId: number, text: string): void;
$terminalRendererRegisterOnInputListener(terminalId: number): void;
}
export interface MyQuickPickItems extends IPickOpenEntry {
export interface TransferQuickPickItems extends IQuickPickItem {
handle: number;
}
export interface TransferQuickInputButton extends IQuickInputButton {
handle: number;
}
export type TransferQuickInput = TransferQuickPick | TransferInputBox;
export interface BaseTransferQuickInput {
id: number;
type?: 'quickPick' | 'inputBox';
enabled?: boolean;
busy?: boolean;
visible?: boolean;
}
export interface TransferQuickPick extends BaseTransferQuickInput {
type?: 'quickPick';
value?: string;
placeholder?: string;
buttons?: TransferQuickInputButton[];
items?: TransferQuickPickItems[];
activeItems?: number[];
selectedItems?: number[];
canSelectMany?: boolean;
ignoreFocusOut?: boolean;
matchOnDescription?: boolean;
matchOnDetail?: boolean;
}
export interface TransferInputBox extends BaseTransferQuickInput {
type?: 'inputBox';
value?: string;
placeholder?: string;
password?: boolean;
buttons?: TransferQuickInputButton[];
prompt?: string;
validationMessage?: string;
}
export interface MainThreadQuickOpenShape extends IDisposable {
$show(multiStepHandle: number | undefined, options: IPickOptions): TPromise<number | number[]>;
$setItems(items: MyQuickPickItems[]): TPromise<any>;
$setError(error: Error): TPromise<any>;
$input(multiStepHandle: number | undefined, options: vscode.InputBoxOptions, validateInput: boolean): TPromise<string>;
$multiStep(handle: number): TPromise<never>;
$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>;
}
export interface MainThreadStatusBarShape extends IDisposable {
@@ -354,8 +418,8 @@ export interface MainThreadStatusBarShape extends IDisposable {
}
export interface MainThreadStorageShape extends IDisposable {
$getValue<T>(shared: boolean, key: string): TPromise<T>;
$setValue(shared: boolean, key: string, value: any): TPromise<void>;
$getValue<T>(shared: boolean, key: string): Thenable<T>;
$setValue(shared: boolean, key: string, value: any): Thenable<void>;
}
export interface MainThreadTelemetryShape extends IDisposable {
@@ -364,37 +428,51 @@ export interface MainThreadTelemetryShape extends IDisposable {
export type WebviewPanelHandle = string;
export interface WebviewPanelShowOptions {
readonly viewColumn?: EditorViewColumn;
readonly preserveFocus?: boolean;
}
export interface MainThreadWebviewsShape extends IDisposable {
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, viewOptions: { viewColumn: EditorViewColumn, preserveFocus: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionLocation: UriComponents): void;
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: vscode.WebviewPanelOptions & vscode.WebviewOptions, extensionLocation: UriComponents): void;
$disposeWebview(handle: WebviewPanelHandle): void;
$reveal(handle: WebviewPanelHandle, viewColumn: EditorViewColumn | null, preserveFocus: boolean): 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>;
$registerSerializer(viewType: string): void;
$unregisterSerializer(viewType: string): void;
}
export interface WebviewPanelViewState {
readonly active: boolean;
readonly visible: boolean;
readonly position: EditorViewColumn;
}
export interface ExtHostWebviewsShape {
$onMessage(handle: WebviewPanelHandle, message: any): void;
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, active: boolean, position: EditorViewColumn): 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>;
}
export interface MainThreadUrlsShape extends IDisposable {
$registerProtocolHandler(handle: number, extensionId: string): TPromise<void>;
$unregisterProtocolHandler(handle: number): TPromise<void>;
$registerUriHandler(handle: number, extensionId: string): Thenable<void>;
$unregisterUriHandler(handle: number): Thenable<void>;
}
export interface ExtHostUrlsShape {
$handleExternalUri(handle: number, uri: UriComponents): TPromise<void>;
$handleExternalUri(handle: number, uri: UriComponents): Thenable<void>;
}
export interface MainThreadWorkspaceShape extends IDisposable {
$startSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, requestId: number): Thenable<UriComponents[]>;
$cancelSearch(requestId: number): Thenable<boolean>;
$startFileSearch(includePattern: string, includeFolder: string, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Thenable<UriComponents[]>;
$startTextSearch(query: IPatternInfo, options: IQueryOptions, requestId: number, token: CancellationToken): Thenable<void>;
$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>;
}
@@ -407,22 +485,26 @@ export interface IFileChangeDto {
export interface MainThreadFileSystemShape extends IDisposable {
$registerFileSystemProvider(handle: number, scheme: string, capabilities: FileSystemProviderCapabilities): void;
$unregisterProvider(handle: number): void;
$setUriFormatter(scheme: string, formatter: LabelRules): void;
$onFileSystemChange(handle: number, resource: IFileChangeDto[]): void;
}
export interface MainThreadSearchShape extends IDisposable {
$registerSearchProvider(handle: number, scheme: string): void;
$registerFileSearchProvider(handle: number, scheme: string): void;
$registerTextSearchProvider(handle: number, scheme: string): void;
$registerFileIndexProvider(handle: number, scheme: string): void;
$unregisterProvider(handle: number): void;
$handleFindMatch(handle: number, session: number, data: UriComponents | IRawFileMatch2[]): void;
$handleFileMatch(handle: number, session: number, data: UriComponents[]): void;
$handleTextMatch(handle: number, session: number, data: IRawFileMatch2[]): void;
$handleTelemetry(eventName: string, data: any): void;
}
export interface MainThreadTaskShape extends IDisposable {
$registerTaskProvider(handle: number): TPromise<void>;
$unregisterTaskProvider(handle: number): TPromise<void>;
$fetchTasks(filter?: TaskFilterDTO): TPromise<TaskDTO[]>;
$executeTask(task: TaskHandleDTO | TaskDTO): TPromise<TaskExecutionDTO>;
$terminateTask(id: string): TPromise<void>;
$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>;
$registerTaskSystem(scheme: string, info: TaskSystemInfoDTO): void;
}
@@ -494,18 +576,18 @@ 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, hasDebugAdapterExecutable: boolean, handle: number): TPromise<any>;
$unregisterDebugConfigurationProvider(handle: number): TPromise<any>;
$startDebugging(folder: UriComponents | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise<boolean>;
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): TPromise<any>;
$appendDebugConsole(value: string): TPromise<any>;
$startBreakpointEvents(): TPromise<any>;
$registerBreakpoints(breakpoints: (ISourceMultiBreakpointDto | IFunctionBreakpointDto)[]): TPromise<void>;
$unregisterBreakpoints(breakpointIds: string[], functionBreakpointIds: string[]): TPromise<void>;
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, hasDebugAdapterExecutable: 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>;
}
export interface MainThreadWindowShape extends IDisposable {
$getWindowVisibility(): TPromise<boolean>;
$getWindowVisibility(): Thenable<boolean>;
}
// -- extension host
@@ -524,7 +606,7 @@ export interface ExtHostDiagnosticsShape {
}
export interface ExtHostDocumentContentProvidersShape {
$provideTextDocumentContent(handle: number, uri: UriComponents): TPromise<string>;
$provideTextDocumentContent(handle: number, uri: UriComponents): Promise<string>;
}
export interface IModelAddedData {
@@ -540,7 +622,6 @@ export interface ExtHostDocumentsShape {
$acceptModelSaved(strURL: UriComponents): void;
$acceptDirtyStateChanged(strURL: UriComponents, isDirty: boolean): void;
$acceptModelChanged(strURL: UriComponents, e: IModelChangedEvent, isDirty: boolean): void;
$onDidRename(oldURL: UriComponents, newURL: UriComponents): void;
}
export interface ExtHostDocumentSaveParticipantShape {
@@ -586,35 +667,42 @@ export interface ExtHostDocumentsAndEditorsShape {
}
export interface ExtHostTreeViewsShape {
$getChildren(treeViewId: string, treeItemHandle?: string): TPromise<ITreeItem[]>;
$getChildren(treeViewId: string, treeItemHandle?: string): Thenable<ITreeItem[]>;
$setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void;
$setSelection(treeViewId: string, treeItemHandles: string[]): void;
$setVisible(treeViewId: string, visible: boolean): void;
}
export interface ExtHostWorkspaceShape {
$acceptWorkspaceData(workspace: IWorkspaceData): void;
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void;
}
export interface ExtHostFileSystemShape {
$stat(handle: number, resource: UriComponents): TPromise<IStat>;
$readdir(handle: number, resource: UriComponents): TPromise<[string, FileType][]>;
$readFile(handle: number, resource: UriComponents): TPromise<string>;
$writeFile(handle: number, resource: UriComponents, base64Encoded: string, opts: FileWriteOptions): TPromise<void>;
$rename(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise<void>;
$copy(handle: number, resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): TPromise<void>;
$mkdir(handle: number, resource: UriComponents): TPromise<void>;
$delete(handle: number, resource: UriComponents): TPromise<void>;
$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>;
$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>;
}
export interface ExtHostSearchShape {
$provideFileSearchResults(handle: number, session: number, query: IRawSearchQuery): TPromise<ISearchCompleteStats>;
$provideTextSearchResults(handle: number, session: number, pattern: IPatternInfo, query: IRawSearchQuery): TPromise<ISearchCompleteStats>;
$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>;
}
export interface ExtHostExtensionServiceShape {
$activateByEvent(activationEvent: string): TPromise<void>;
$activateByEvent(activationEvent: string): Thenable<void>;
}
export interface FileSystemEvents {
@@ -624,6 +712,8 @@ export interface FileSystemEvents {
}
export interface ExtHostFileSystemEventServiceShape {
$onFileEvent(events: FileSystemEvents): void;
$onFileRename(oldUri: UriComponents, newUri: UriComponents): void;
$onWillRename(oldUri: UriComponents, newUri: UriComponents): Thenable<any>;
}
export interface ObjectIdentifier {
@@ -673,22 +763,28 @@ export interface LocationDto {
range: IRange;
}
export interface SymbolInformationDto extends IdObject {
export interface DefinitionLinkDto {
origin?: IRange;
uri: UriComponents;
range: IRange;
selectionRange?: IRange;
}
export interface WorkspaceSymbolDto extends IdObject {
name: string;
containerName?: string;
kind: modes.SymbolKind;
location: LocationDto;
definingRange: IRange;
children?: SymbolInformationDto[];
}
export interface WorkspaceSymbolsDto extends IdObject {
symbols: SymbolInformationDto[];
symbols: WorkspaceSymbolDto[];
}
export interface ResourceFileEditDto {
oldUri: UriComponents;
newUri: UriComponents;
options: IFileOperationOptions;
}
export interface ResourceTextEditDto {
@@ -727,38 +823,44 @@ export interface CodeActionDto {
}
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents): TPromise<SymbolInformationDto[]>;
$provideCodeLenses(handle: number, resource: UriComponents): TPromise<modes.ICodeLensSymbol[]>;
$resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol): TPromise<modes.ICodeLensSymbol>;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<LocationDto | LocationDto[]>;
$provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise<LocationDto | LocationDto[]>;
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<LocationDto | LocationDto[]>;
$provideHover(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.Hover>;
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.DocumentHighlight[]>;
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext): TPromise<LocationDto[]>;
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext): TPromise<CodeActionDto[]>;
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]>;
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]>;
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]>;
$provideWorkspaceSymbols(handle: number, search: string): TPromise<WorkspaceSymbolsDto>;
$resolveWorkspaceSymbol(handle: number, symbol: SymbolInformationDto): TPromise<SymbolInformationDto>;
$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>;
$releaseWorkspaceSymbols(handle: number, id: number): void;
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string): TPromise<WorkspaceEditDto>;
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.RenameLocation>;
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.SuggestContext): TPromise<SuggestResultDto>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.ISuggestion): TPromise<modes.ISuggestion>;
$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.SuggestContext, token: CancellationToken): Thenable<SuggestResultDto>;
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.ISuggestion, token: CancellationToken): Thenable<modes.ISuggestion>;
$releaseCompletionItems(handle: number, id: number): void;
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.SignatureHelp>;
$provideDocumentLinks(handle: number, resource: UriComponents): TPromise<modes.ILink[]>;
$resolveDocumentLink(handle: number, link: modes.ILink): TPromise<modes.ILink>;
$provideDocumentColors(handle: number, resource: UriComponents): TPromise<IRawColorInfo[]>;
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo): TPromise<modes.IColorPresentation[]>;
$provideFoldingRanges(handle: number, resource: UriComponents, context: modes.FoldingContext): TPromise<modes.FoldingRange[]>;
$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[]>;
}
export interface ExtHostQuickOpenShape {
$onItemSelected(handle: number): void;
$validateInput(input: string): TPromise<string>;
$validateInput(input: string): Thenable<string>;
$onDidChangeActive(sessionId: number, handles: number[]): void;
$onDidChangeSelection(sessionId: number, handles: number[]): void;
$onDidAccept(sessionId: number): void;
$onDidChangeValue(sessionId: number, value: string): void;
$onDidTriggerButton(sessionId: number, handle: number): void;
$onDidHide(sessionId: number): void;
}
export interface ShellLaunchConfigDto {
@@ -772,28 +874,32 @@ export interface ShellLaunchConfigDto {
export interface ExtHostTerminalServiceShape {
$acceptTerminalClosed(id: number): void;
$acceptTerminalOpened(id: number, name: string): void;
$acceptActiveTerminalChanged(id: number | null): void;
$acceptTerminalProcessId(id: number, processId: number): void;
$acceptTerminalProcessData(id: number, data: string): void;
$acceptTerminalRendererInput(id: number, data: string): void;
$acceptTerminalRendererDimensions(id: number, cols: number, rows: number): void;
$createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, cols: number, rows: number): void;
$acceptProcessInput(id: number, data: string): void;
$acceptProcessResize(id: number, cols: number, rows: number): void;
$acceptProcessShutdown(id: number): void;
$acceptProcessShutdown(id: number, immediate: boolean): void;
}
export interface ExtHostSCMShape {
$provideOriginalResource(sourceControlHandle: number, uri: UriComponents): TPromise<UriComponents>;
$onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise<void>;
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): TPromise<void>;
$validateInput(sourceControlHandle: number, value: string, cursorPosition: number): TPromise<[string, number] | undefined>;
$provideOriginalResource(sourceControlHandle: number, uri: UriComponents, token: CancellationToken): Thenable<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>;
}
export interface ExtHostTaskShape {
$provideTasks(handle: number): TPromise<TaskSet>;
$provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<TaskSet>;
$onDidStartTask(execution: TaskExecutionDTO): void;
$onDidStartTaskProcess(value: TaskProcessStartedDTO): void;
$onDidEndTaskProcess(value: TaskProcessEndedDTO): void;
$OnDidEndTask(execution: TaskExecutionDTO): void;
$resolveVariables(workspaceFolder: URI, variables: string[]): TPromise<any>;
$resolveVariables(workspaceFolder: UriComponents, variables: string[]): Thenable<any>;
}
export interface IBreakpointDto {
@@ -838,16 +944,14 @@ export interface ISourceMultiBreakpointDto {
}
export interface ExtHostDebugServiceShape {
$substituteVariables(folder: UriComponents | undefined, config: IConfig): TPromise<IConfig>;
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<void>;
$isTerminalBusy(processId: number): TPromise<boolean>;
$prepareCommandForTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<any>;
$startDASession(handle: number, debugType: string, adapterExecutableInfo: IAdapterExecutable | null, debugPort: number): TPromise<void>;
$stopDASession(handle: number): TPromise<void>;
$sendDAMessage(handle: number, message: DebugProtocol.ProtocolMessage): TPromise<void>;
$resolveDebugConfiguration(handle: number, folder: UriComponents | undefined, debugConfiguration: IConfig): TPromise<IConfig>;
$provideDebugConfigurations(handle: number, folder: UriComponents | undefined): TPromise<IConfig[]>;
$debugAdapterExecutable(handle: number, folder: UriComponents | undefined): TPromise<IAdapterExecutable>;
$substituteVariables(folder: UriComponents | undefined, config: IConfig): Thenable<IConfig>;
$runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): Thenable<void>;
$startDASession(handle: number, debugType: string, adapterExecutableInfo: IAdapterExecutable | null, debugPort: number): Thenable<void>;
$stopDASession(handle: number): Thenable<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[]>;
$debugAdapterExecutable(handle: number, folder: UriComponents | undefined): Thenable<IAdapterExecutable>;
$acceptDebugSessionStarted(id: DebugSessionUUID, type: string, name: string): void;
$acceptDebugSessionTerminated(id: DebugSessionUUID, type: string, name: string): void;
$acceptDebugSessionActiveChanged(id: DebugSessionUUID | undefined, type?: string, name?: string): void;
@@ -866,7 +970,7 @@ export type DecorationData = [number, boolean, string, string, ThemeColor, strin
export type DecorationReply = { [id: number]: DecorationData };
export interface ExtHostDecorationsShape {
$provideDecorations(requests: DecorationRequest[]): TPromise<DecorationReply>;
$provideDecorations(requests: DecorationRequest[], token: CancellationToken): Thenable<DecorationReply>;
}
export interface ExtHostWindowShape {
@@ -882,10 +986,10 @@ export interface ExtHostProgressShape {
}
export interface ExtHostCommentsShape {
$provideDocumentComments(handle: number, document: UriComponents): TPromise<modes.CommentInfo>;
$createNewCommentThread?(handle: number, document: UriComponents, range: IRange, text: string): TPromise<modes.CommentThread>;
$replyToCommentThread?(handle: number, document: UriComponents, range: IRange, commentThread: modes.CommentThread, text: string): TPromise<modes.CommentThread>;
$provideWorkspaceComments(handle: number): TPromise<modes.CommentThread[]>;
$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>;
$provideWorkspaceComments(handle: number): Thenable<modes.CommentThread[]>;
}
// --- proxy identifiers

View File

@@ -4,21 +4,22 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as vscode from 'vscode';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import * as types from 'vs/workbench/api/node/extHostTypes';
import { IRawColorInfo } from 'vs/workbench/api/node/extHost.protocol';
import { IRawColorInfo, WorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import * as search from 'vs/workbench/parts/search/common/search';
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { IWorkspaceSymbolProvider } from 'vs/workbench/parts/search/common/search';
import { CustomCodeAction } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { ICommandsExecutor, PreviewHTMLAPICommand, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand } from './apiCommands';
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';
export class ExtHostApiCommands {
@@ -111,7 +112,7 @@ export class ExtHostApiCommands {
args: [
{ name: 'uri', description: 'Uri of a text document', constraint: URI }
],
returns: 'A promise that resolves to an array of SymbolInformation-instances.'
returns: 'A promise that resolves to an array of SymbolInformation and DocumentSymbol instances.'
});
this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider, {
description: 'Execute completion item provider.',
@@ -250,6 +251,13 @@ export class ExtHostApiCommands {
{ name: 'path', description: 'Path to remove from recently opened.', constraint: (value: any) => typeof value === 'string' }
]
});
this._register(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute), {
description: 'Sets the editor layout. The layout is described as object with an initial (optional) orientation (0 = horizontal, 1 = vertical) and an array of editor groups within. Each editor group can have a size and another array of editor groups that will be laid out orthogonal to the orientation. If editor group sizes are provided, their sum must be 1 to be applied per row or column. Example for a 2x2 grid: `{ orientation: 0, groups: [{ groups: [{}, {}], size: 0.5 }, { groups: [{}, {}], size: 0.5 }] }`',
args: [
{ name: 'layout', description: 'The editor layout to set.', constraint: (value: EditorGroupLayout) => typeof value === 'object' && Array.isArray(value.groups) }
]
});
}
// --- command impl
@@ -266,11 +274,11 @@ export class ExtHostApiCommands {
* @return A promise that resolves to an array of symbol information.
*/
private _executeWorkspaceSymbolProvider(query: string): Thenable<types.SymbolInformation[]> {
return this._commands.executeCommand<[IWorkspaceSymbolProvider, modes.SymbolInformation[]][]>('_executeWorkspaceSymbolProvider', { query }).then(value => {
return this._commands.executeCommand<[search.IWorkspaceSymbolProvider, search.IWorkspaceSymbol[]][]>('_executeWorkspaceSymbolProvider', { query }).then(value => {
const result: types.SymbolInformation[] = [];
if (Array.isArray(value)) {
for (let tuple of value) {
result.push(...tuple[1].map(typeConverters.SymbolInformation.to));
result.push(...tuple[1].map(typeConverters.WorkspaceSymbol.to));
}
}
return result;
@@ -337,7 +345,7 @@ export class ExtHostApiCommands {
position: position && typeConverters.Position.from(position),
newName
};
return this._commands.executeCommand<modes.WorkspaceEdit>('_executeDocumentRenameProvider', args).then(value => {
return this._commands.executeCommand<WorkspaceEditDto>('_executeDocumentRenameProvider', args).then(value => {
if (!value) {
return undefined;
}
@@ -404,15 +412,35 @@ export class ExtHostApiCommands {
});
}
private _executeDocumentSymbolProvider(resource: URI): Thenable<types.SymbolInformation[]> {
private _executeDocumentSymbolProvider(resource: URI): Thenable<vscode.SymbolInformation[]> {
const args = {
resource
};
return this._commands.executeCommand<modes.IOutline>('_executeDocumentSymbolProvider', args).then(value => {
if (value && Array.isArray(value.entries)) {
return value.entries.map(typeConverters.SymbolInformation.to);
return this._commands.executeCommand<modes.DocumentSymbol[]>('_executeDocumentSymbolProvider', args).then(value => {
if (isFalsyOrEmpty(value)) {
return undefined;
}
return undefined;
class MergedInfo extends types.SymbolInformation implements vscode.DocumentSymbol {
static to(symbol: modes.DocumentSymbol): MergedInfo {
let res = new MergedInfo(
symbol.name,
typeConverters.SymbolKind.to(symbol.kind),
symbol.containerName,
new types.Location(resource, typeConverters.Range.to(symbol.range))
);
res.detail = symbol.detail;
res.range = res.location.range;
res.selectionRange = typeConverters.Range.to(symbol.selectionRange);
res.children = symbol.children && symbol.children.map(MergedInfo.to);
return res;
}
detail: string;
range: vscode.Range;
selectionRange: vscode.Range;
children: vscode.DocumentSymbol[];
}
return value.map(MergedInfo.to);
});
}

View File

@@ -131,6 +131,8 @@ export class ExtHostCommands implements ExtHostCommandsShape {
}
$executeContributedCommand<T>(id: string, ...args: any[]): Thenable<T> {
this._logService.trace('ExtHostCommands#$executeContributedCommand', id);
if (!this._commands.has(id)) {
return Promise.reject(new Error(`Contributed command '${id}' does not exist.`));
} else {

View File

@@ -5,8 +5,8 @@
'use strict';
import { asWinJsPromise } from 'vs/base/common/async';
import URI, { UriComponents } from 'vs/base/common/uri';
import { asThenable } 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';
@@ -15,6 +15,7 @@ import * as vscode from 'vscode';
import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShape } from './extHost.protocol';
import { CommandsConverter } from './extHostCommands';
import { IRange } from 'vs/editor/common/core/range';
import { CancellationToken } from 'vs/base/common/cancellation';
export class ExtHostComments implements ExtHostCommentsShape {
private static handlePool = 0;
@@ -64,7 +65,7 @@ export class ExtHostComments implements ExtHostCommentsShape {
};
}
$createNewCommentThread(handle: number, uri: UriComponents, range: IRange, text: string): TPromise<modes.CommentThread> {
$createNewCommentThread(handle: number, uri: UriComponents, range: IRange, text: string): Thenable<modes.CommentThread> {
const data = this._documents.getDocumentData(URI.revive(uri));
const ran = <vscode.Range>extHostTypeConverter.Range.to(range);
@@ -72,13 +73,13 @@ export class ExtHostComments implements ExtHostCommentsShape {
return TPromise.as(null);
}
return asWinJsPromise(token => {
return asThenable(() => {
let provider = this._documentProviders.get(handle);
return provider.createNewCommentThread(data.document, ran, text, token);
return provider.createNewCommentThread(data.document, ran, text, CancellationToken.None);
}).then(commentThread => commentThread ? convertToCommentThread(commentThread, this._commandsConverter) : null);
}
$replyToCommentThread(handle: number, uri: UriComponents, range: IRange, thread: modes.CommentThread, text: string): TPromise<modes.CommentThread> {
$replyToCommentThread(handle: number, uri: UriComponents, range: IRange, thread: modes.CommentThread, text: string): Thenable<modes.CommentThread> {
const data = this._documents.getDocumentData(URI.revive(uri));
const ran = <vscode.Range>extHostTypeConverter.Range.to(range);
@@ -86,33 +87,33 @@ export class ExtHostComments implements ExtHostCommentsShape {
return TPromise.as(null);
}
return asWinJsPromise(token => {
return asThenable(() => {
let provider = this._documentProviders.get(handle);
return provider.replyToCommentThread(data.document, ran, convertFromCommentThread(thread), text, token);
return provider.replyToCommentThread(data.document, ran, convertFromCommentThread(thread), text, CancellationToken.None);
}).then(commentThread => commentThread ? convertToCommentThread(commentThread, this._commandsConverter) : null);
}
$provideDocumentComments(handle: number, uri: UriComponents): TPromise<modes.CommentInfo> {
$provideDocumentComments(handle: number, uri: UriComponents): Thenable<modes.CommentInfo> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
return TPromise.as(null);
}
return asWinJsPromise(token => {
return asThenable(() => {
let provider = this._documentProviders.get(handle);
return provider.provideDocumentComments(data.document, token);
return provider.provideDocumentComments(data.document, CancellationToken.None);
})
.then(commentInfo => commentInfo ? convertCommentInfo(handle, commentInfo, this._commandsConverter) : null);
}
$provideWorkspaceComments(handle: number): TPromise<modes.CommentThread[]> {
$provideWorkspaceComments(handle: number): Thenable<modes.CommentThread[]> {
const provider = this._workspaceProviders.get(handle);
if (!provider) {
return TPromise.as(null);
}
return asWinJsPromise(token => {
return provider.provideWorkspaceComments(token);
return asThenable(() => {
return provider.provideWorkspaceComments(CancellationToken.None);
}).then(comments =>
comments.map(x => convertToCommentThread(x, this._commandsConverter)
));

View File

@@ -5,7 +5,7 @@
'use strict';
import { mixin, deepClone } from 'vs/base/common/objects';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import * as vscode from 'vscode';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';

View File

@@ -6,10 +6,11 @@
import * as paths from 'vs/base/common/paths';
import { Schemas } from 'vs/base/common/network';
import URI, { UriComponents } from 'vs/base/common/uri';
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 { asWinJsPromise } from 'vs/base/common/async';
import { asThenable } from 'vs/base/common/async';
import * as nls from 'vs/nls';
import {
MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID,
IMainContext, IBreakpointsDeltaDto, ISourceMultiBreakpointDto, IFunctionBreakpointDto
@@ -24,11 +25,13 @@ import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumen
import { IAdapterExecutable, ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter } 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 { VariableResolver } from 'vs/workbench/services/configurationResolver/node/variableResolver';
import { IStringDictionary } from 'vs/base/common/collections';
import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/node/variableResolver';
import { ExtHostConfiguration } from './extHostConfiguration';
import { convertToVSCPaths, convertToDAPaths } 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';
export class ExtHostDebugService implements ExtHostDebugServiceShape {
@@ -66,12 +69,16 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private _variableResolver: IConfigurationResolverService;
private _integratedTerminalInstance: vscode.Terminal;
private _terminalDisposedListener: IDisposable;
constructor(mainContext: IMainContext,
private _workspaceService: ExtHostWorkspace,
private _extensionService: ExtHostExtensionService,
private _editorsService: ExtHostDocumentsAndEditors,
private _configurationService: ExtHostConfiguration
private _configurationService: ExtHostConfiguration,
private _terminalService: ExtHostTerminalService
) {
this._handleCounter = 0;
@@ -118,35 +125,72 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
public $runInTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<void> {
const terminalLauncher = getTerminalLauncher();
if (terminalLauncher) {
return terminalLauncher.runInTerminal(args, config);
if (args.kind === 'integrated') {
if (!this._terminalDisposedListener) {
// React on terminal disposed and check if that is the debug terminal #12956
this._terminalDisposedListener = this._terminalService.onDidCloseTerminal(terminal => {
if (this._integratedTerminalInstance && this._integratedTerminalInstance === terminal) {
this._integratedTerminalInstance = null;
}
});
}
return new TPromise(resolve => {
if (this._integratedTerminalInstance) {
this._integratedTerminalInstance.processId.then(pid => {
resolve(hasChildprocesses(pid));
}, err => {
resolve(true);
});
} else {
resolve(true);
}
}).then(needNewTerminal => {
if (needNewTerminal) {
this._integratedTerminalInstance = this._terminalService.createTerminal(args.title || nls.localize('debug.terminal.title', "debuggee"));
}
this._integratedTerminalInstance.show();
return new TPromise((resolve, error) => {
setTimeout(_ => {
const command = prepareCommand(args, config);
this._integratedTerminalInstance.sendText(command, true);
resolve(void 0);
}, 500);
});
});
} else if (args.kind === 'external') {
const terminalLauncher = getTerminalLauncher();
if (terminalLauncher) {
return terminalLauncher.runInTerminal(args, config);
}
}
return void 0;
}
public $isTerminalBusy(processId: number): TPromise<boolean> {
return asWinJsPromise(token => hasChildprocesses(processId));
}
public $prepareCommandForTerminal(args: DebugProtocol.RunInTerminalRequestArguments, config: ITerminalSettings): TPromise<any> {
return asWinJsPromise(token => prepareCommand(args, config));
}
public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): TPromise<IConfig> {
if (!this._variableResolver) {
this._variableResolver = new ExtHostVariableResolverService(this._workspaceService, this._editorsService, this._configurationService);
}
let ws: IWorkspaceFolder;
const folder = this.getFolder(folderUri);
let ws: IWorkspaceFolder = {
uri: folder.uri,
name: folder.name,
index: folder.index,
toResource: () => {
throw new Error('Not implemented');
}
};
return asWinJsPromise(token => DebugAdapter.substituteVariables(ws, config, this._variableResolver));
if (folder) {
ws = {
uri: folder.uri,
name: folder.name,
index: folder.index,
toResource: () => {
throw new Error('Not implemented');
}
};
}
return TPromise.wrap(this._variableResolver.resolveAny(ws, config));
}
public $startDASession(handle: number, debugType: string, adpaterExecutable: IAdapterExecutable | null, debugPort: number): TPromise<void> {
@@ -292,7 +336,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this.fireBreakpointChanges(a, r, c);
}
public addBreakpoints(breakpoints0: vscode.Breakpoint[]): TPromise<void> {
public addBreakpoints(breakpoints0: vscode.Breakpoint[]): Thenable<void> {
this.startBreakpoints();
@@ -358,7 +402,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
return this._debugServiceProxy.$registerBreakpoints(dtos);
}
public removeBreakpoints(breakpoints0: vscode.Breakpoint[]): TPromise<void> {
public removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Thenable<void> {
this.startBreakpoints();
@@ -383,9 +427,9 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
private fireBreakpointChanges(added: vscode.Breakpoint[], removed: vscode.Breakpoint[], changed: vscode.Breakpoint[]) {
if (added.length > 0 || removed.length > 0 || changed.length > 0) {
this._onDidChangeBreakpoints.fire(Object.freeze({
added: Object.freeze<vscode.Breakpoint[]>(added),
removed: Object.freeze<vscode.Breakpoint[]>(removed),
changed: Object.freeze<vscode.Breakpoint[]>(changed)
added,
removed,
changed,
}));
}
}
@@ -408,7 +452,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
});
}
public $provideDebugConfigurations(handle: number, folderUri: UriComponents | undefined): TPromise<vscode.DebugConfiguration[]> {
public $provideDebugConfigurations(handle: number, folderUri: UriComponents | undefined): Thenable<vscode.DebugConfiguration[]> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('no handler found'));
@@ -416,10 +460,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
if (!handler.provideDebugConfigurations) {
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('handler has no method provideDebugConfigurations'));
}
return asWinJsPromise(token => handler.provideDebugConfigurations(this.getFolder(folderUri), token));
return asThenable(() => handler.provideDebugConfigurations(this.getFolder(folderUri), CancellationToken.None));
}
public $resolveDebugConfiguration(handle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): TPromise<vscode.DebugConfiguration> {
public $resolveDebugConfiguration(handle: number, folderUri: UriComponents | undefined, debugConfiguration: vscode.DebugConfiguration): Thenable<vscode.DebugConfiguration> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<vscode.DebugConfiguration>(new Error('no handler found'));
@@ -427,10 +471,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
if (!handler.resolveDebugConfiguration) {
return TPromise.wrapError<vscode.DebugConfiguration>(new Error('handler has no method resolveDebugConfiguration'));
}
return asWinJsPromise(token => handler.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, token));
return asThenable(() => handler.resolveDebugConfiguration(this.getFolder(folderUri), debugConfiguration, CancellationToken.None));
}
public $debugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): TPromise<vscode.DebugAdapterExecutable> {
public $debugAdapterExecutable(handle: number, folderUri: UriComponents | undefined): Thenable<vscode.DebugAdapterExecutable> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<vscode.DebugAdapterExecutable>(new Error('no handler found'));
@@ -438,10 +482,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
if (!handler.debugAdapterExecutable) {
return TPromise.wrapError<vscode.DebugAdapterExecutable>(new Error('handler has no method debugAdapterExecutable'));
}
return asWinJsPromise(token => handler.debugAdapterExecutable(this.getFolder(folderUri), token));
return asThenable(() => handler.debugAdapterExecutable(this.getFolder(folderUri), CancellationToken.None));
}
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise<boolean> {
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration): Thenable<boolean> {
return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig);
}
@@ -495,7 +539,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._onDidReceiveDebugSessionCustomEvent.fire(ee);
}
private getFolder(_folderUri: UriComponents | undefined): vscode.WorkspaceFolder {
private getFolder(_folderUri: UriComponents | undefined): vscode.WorkspaceFolder | undefined {
if (_folderUri) {
const folderURI = URI.revive(_folderUri);
return this._workspaceService.resolveWorkspaceFolder(folderURI);
@@ -558,15 +602,12 @@ export class ExtHostDebugConsole implements vscode.DebugConsole {
}
}
export class ExtHostVariableResolverService implements IConfigurationResolverService {
export class ExtHostVariableResolverService extends AbstractVariableResolverService {
_serviceBrand: any;
_variableResolver: VariableResolver;
constructor(workspace: ExtHostWorkspace, editors: ExtHostDocumentsAndEditors, configuration: ExtHostConfiguration) {
this._variableResolver = new VariableResolver({
constructor(workspaceService: ExtHostWorkspace, editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfiguration) {
super({
getFolderUri: (folderName: string): URI => {
const folders = workspace.getWorkspaceFolders();
const folders = workspaceService.getWorkspaceFolders();
const found = folders.filter(f => f.name === folderName);
if (found && found.length > 0) {
return found[0].uri;
@@ -574,16 +615,16 @@ export class ExtHostVariableResolverService implements IConfigurationResolverSer
return undefined;
},
getWorkspaceFolderCount: (): number => {
return workspace.getWorkspaceFolders().length;
return workspaceService.getWorkspaceFolders().length;
},
getConfigurationValue: (folderUri: URI, section: string) => {
return configuration.getConfiguration(undefined, folderUri).get<string>(section);
return configurationService.getConfiguration(undefined, folderUri).get<string>(section);
},
getExecPath: (): string | undefined => {
return undefined; // does not exist in EH
},
getFilePath: (): string | undefined => {
const activeEditor = editors.activeEditor();
const activeEditor = editorService.activeEditor();
if (activeEditor) {
const resource = activeEditor.document.uri;
if (resource.scheme === Schemas.file) {
@@ -593,34 +634,19 @@ export class ExtHostVariableResolverService implements IConfigurationResolverSer
return undefined;
},
getSelectedText: (): string | undefined => {
const activeEditor = editors.activeEditor();
const activeEditor = editorService.activeEditor();
if (activeEditor && !activeEditor.selection.isEmpty) {
return activeEditor.document.getText(activeEditor.selection);
}
return undefined;
},
getLineNumber: (): string => {
const activeEditor = editors.activeEditor();
const activeEditor = editorService.activeEditor();
if (activeEditor) {
return String(activeEditor.selection.end.line + 1);
}
return undefined;
}
}, process.env);
}
public resolve(root: IWorkspaceFolder, value: string): string;
public resolve(root: IWorkspaceFolder, value: string[]): string[];
public resolve(root: IWorkspaceFolder, value: IStringDictionary<string>): IStringDictionary<string>;
public resolve(root: IWorkspaceFolder, value: any): any {
return this._variableResolver.resolveAny(root ? root.uri : undefined, value);
}
public resolveAny<T>(root: IWorkspaceFolder, value: T, commandMapping?: IStringDictionary<string>): T {
return this._variableResolver.resolveAny(root ? root.uri : undefined, value, commandMapping);
}
public executeCommandVariables(configuration: any, variables: IStringDictionary<string>): TPromise<IStringDictionary<string>> {
throw new Error('findAndExecuteCommandVariables not implemented.');
});
}
}

View File

@@ -5,27 +5,32 @@
'use strict';
import * as vscode from 'vscode';
import URI from 'vs/base/common/uri';
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 { asWinJsPromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
interface ProviderData {
provider: vscode.DecorationProvider;
extensionId: string;
}
export class ExtHostDecorations implements ExtHostDecorationsShape {
private static _handlePool = 0;
private readonly _provider = new Map<number, vscode.DecorationProvider>();
private readonly _provider = new Map<number, ProviderData>();
private readonly _proxy: MainThreadDecorationsShape;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadDecorations);
}
registerDecorationProvider(provider: vscode.DecorationProvider, label: string): vscode.Disposable {
registerDecorationProvider(provider: vscode.DecorationProvider, extensionId: string): vscode.Disposable {
const handle = ExtHostDecorations._handlePool++;
this._provider.set(handle, provider);
this._proxy.$registerDecorationProvider(handle, label);
this._provider.set(handle, { provider, extensionId });
this._proxy.$registerDecorationProvider(handle, extensionId);
const listener = provider.onDidChangeDecorations(e => {
this._proxy.$onDidChange(handle, !e ? null : Array.isArray(e) ? e : [e]);
@@ -38,17 +43,20 @@ export class ExtHostDecorations implements ExtHostDecorationsShape {
});
}
$provideDecorations(requests: DecorationRequest[]): TPromise<DecorationReply> {
$provideDecorations(requests: DecorationRequest[], token: CancellationToken): Thenable<DecorationReply> {
const result: DecorationReply = Object.create(null);
return TPromise.join(requests.map(request => {
const { handle, uri, id } = request;
const provider = this._provider.get(handle);
if (!provider) {
if (!this._provider.has(handle)) {
// might have been unregistered in the meantime
return void 0;
}
return asWinJsPromise(token => provider.provideDecoration(URI.revive(uri), token)).then(data => {
result[id] = data && <DecorationData>[data.priority, data.bubble, data.title, data.abbreviation, data.color, data.source];
const { provider, extensionId } = this._provider.get(handle);
return Promise.resolve(provider.provideDecoration(URI.revive(uri), token)).then(data => {
if (data && data.letter && data.letter.length !== 1) {
console.warn(`INVALID decoration from extension '${extensionId}'. The 'letter' must be set and be one character, not '${data.letter}'.`);
}
result[id] = data && <DecorationData>[data.priority, data.bubble, data.title, data.letter, data.color, data.source];
}, err => {
console.error(err);
});

View File

@@ -6,27 +6,29 @@
import { localize } from 'vs/nls';
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import * as vscode from 'vscode';
import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol';
import { DiagnosticSeverity } from './extHostTypes';
import { DiagnosticSeverity, Diagnostic } from './extHostTypes';
import * as converter from './extHostTypeConverters';
import { mergeSort } from 'vs/base/common/arrays';
import { mergeSort, equals } from 'vs/base/common/arrays';
import { Event, Emitter, debounceEvent, mapEvent } from 'vs/base/common/event';
import { keys } from 'vs/base/common/map';
export class DiagnosticCollection implements vscode.DiagnosticCollection {
private readonly _name: string;
private readonly _owner: string;
private readonly _maxDiagnosticsPerFile: number;
private readonly _onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>;
private readonly _proxy: MainThreadDiagnosticsShape;
private _proxy: MainThreadDiagnosticsShape;
private _isDisposed = false;
private _data = new Map<string, vscode.Diagnostic[]>();
constructor(name: string, maxDiagnosticsPerFile: number, proxy: MainThreadDiagnosticsShape, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) {
constructor(name: string, owner: string, maxDiagnosticsPerFile: number, proxy: MainThreadDiagnosticsShape, onDidChangeDiagnostics: Emitter<(vscode.Uri | string)[]>) {
this._name = name;
this._owner = owner;
this._maxDiagnosticsPerFile = maxDiagnosticsPerFile;
this._proxy = proxy;
this._onDidChangeDiagnostics = onDidChangeDiagnostics;
@@ -35,8 +37,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
dispose(): void {
if (!this._isDisposed) {
this._onDidChangeDiagnostics.fire(keys(this._data));
this._proxy.$clear(this.name);
this._proxy = undefined;
this._proxy.$clear(this._owner);
this._data = undefined;
this._isDisposed = true;
}
@@ -61,9 +62,13 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
this._checkDisposed();
let toSync: vscode.Uri[];
let hasChanged = true;
if (first instanceof URI) {
// check if this has actually changed
hasChanged = hasChanged && !equals(diagnostics, this.get(first), Diagnostic.isEqual);
if (!diagnostics) {
// remove this entry
this.delete(first);
@@ -102,6 +107,17 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
}
}
// send event for extensions
this._onDidChangeDiagnostics.fire(toSync);
// if nothing has changed then there is nothing else to do
// we have updated the diagnostics but we don't send a message
// to the renderer. tho we have still send an event for other
// extensions because the diagnostic might carry more information
// than known to us
if (!hasChanged) {
return;
}
// compute change and send to main side
const entries: [URI, IMarkerData[]][] = [];
for (let uri of toSync) {
@@ -141,22 +157,21 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
entries.push([uri, marker]);
}
this._onDidChangeDiagnostics.fire(toSync);
this._proxy.$changeMany(this.name, entries);
this._proxy.$changeMany(this._owner, entries);
}
delete(uri: vscode.Uri): void {
this._checkDisposed();
this._onDidChangeDiagnostics.fire([uri]);
this._data.delete(uri.toString());
this._proxy.$changeMany(this.name, [[uri, undefined]]);
this._proxy.$changeMany(this._owner, [[uri, undefined]]);
}
clear(): void {
this._checkDisposed();
this._onDidChangeDiagnostics.fire(keys(this._data));
this._data.clear();
this._proxy.$clear(this.name);
this._proxy.$clear(this._owner);
}
forEach(callback: (uri: URI, diagnostics: vscode.Diagnostic[], collection: DiagnosticCollection) => any, thisArg?: any): void {
@@ -204,7 +219,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
private static readonly _maxDiagnosticsPerFile: number = 1000;
private readonly _proxy: MainThreadDiagnosticsShape;
private readonly _collections: DiagnosticCollection[] = [];
private readonly _collections = new Map<string, DiagnosticCollection>();
private readonly _onDidChangeDiagnostics = new Emitter<(vscode.Uri | string)[]>();
static _debouncer(last: (vscode.Uri | string)[], current: (vscode.Uri | string)[]): (vscode.Uri | string)[] {
@@ -242,22 +257,28 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
}
createDiagnosticCollection(name: string): vscode.DiagnosticCollection {
let { _collections, _proxy, _onDidChangeDiagnostics } = this;
let owner: string;
if (!name) {
name = '_generated_diagnostic_collection_name_#' + ExtHostDiagnostics._idPool++;
owner = name;
} else if (!_collections.has(name)) {
owner = name;
} else {
console.warn(`DiagnosticCollection with name '${name}' does already exist.`);
do {
owner = name + ExtHostDiagnostics._idPool++;
} while (_collections.has(owner));
}
const { _collections, _proxy, _onDidChangeDiagnostics } = this;
const result = new class extends DiagnosticCollection {
constructor() {
super(name, ExtHostDiagnostics._maxDiagnosticsPerFile, _proxy, _onDidChangeDiagnostics);
_collections.push(this);
super(name, owner, ExtHostDiagnostics._maxDiagnosticsPerFile, _proxy, _onDidChangeDiagnostics);
_collections.set(owner, this);
}
dispose() {
super.dispose();
let idx = _collections.indexOf(this);
if (idx !== -1) {
_collections.splice(idx, 1);
}
_collections.delete(owner);
}
};
@@ -272,7 +293,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
} else {
let index = new Map<string, number>();
let res: [vscode.Uri, vscode.Diagnostic[]][] = [];
for (const collection of this._collections) {
this._collections.forEach(collection => {
collection.forEach((uri, diagnostics) => {
let idx = index.get(uri.toString());
if (typeof idx === 'undefined') {
@@ -282,18 +303,18 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
}
res[idx][1] = res[idx][1].concat(...diagnostics);
});
}
});
return res;
}
}
private _getDiagnostics(resource: vscode.Uri): vscode.Diagnostic[] {
let res: vscode.Diagnostic[] = [];
for (const collection of this._collections) {
this._collections.forEach(collection => {
if (collection.has(resource)) {
res = res.concat(collection.get(resource));
}
}
});
return res;
}
}

View File

@@ -5,7 +5,7 @@
'use strict';
import * as vscode from 'vscode';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { MainContext, MainThreadDiaglogsShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
export class ExtHostDialogs {

View File

@@ -5,16 +5,15 @@
'use strict';
import { onUnexpectedError } from 'vs/base/common/errors';
import URI, { UriComponents } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { TPromise } from 'vs/base/common/winjs.base';
import * as vscode from 'vscode';
import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, ExtHostDocumentContentProvidersShape, MainThreadDocumentContentProvidersShape, IMainContext } from './extHost.protocol';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
import { Schemas } from 'vs/base/common/network';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
export class ExtHostDocumentContentProvider implements ExtHostDocumentContentProvidersShape {
@@ -86,11 +85,11 @@ export class ExtHostDocumentContentProvider implements ExtHostDocumentContentPro
});
}
$provideTextDocumentContent(handle: number, uri: UriComponents): TPromise<string> {
$provideTextDocumentContent(handle: number, uri: UriComponents): Promise<string> {
const provider = this._documentContentProviders.get(handle);
if (!provider) {
return TPromise.wrapError<string>(new Error(`unsupported uri-scheme: ${uri.scheme}`));
return Promise.reject(new Error(`unsupported uri-scheme: ${uri.scheme}`));
}
return asWinJsPromise(token => provider.provideTextDocumentContent(URI.revive(uri), token));
return Promise.resolve(provider.provideTextDocumentContent(URI.revive(uri), CancellationToken.None));
}
}

View File

@@ -7,7 +7,7 @@
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 { 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';
@@ -99,7 +99,7 @@ export class ExtHostDocumentData extends MirrorTextModel {
this._isDirty = isDirty;
}
private _save(): TPromise<boolean> {
private _save(): Thenable<boolean> {
if (this._isDisposed) {
return TPromise.wrapError<boolean>(new Error('Document has been closed'));
}

View File

@@ -5,7 +5,7 @@
'use strict';
import { Event } from 'vs/base/common/event';
import URI, { UriComponents } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { sequence, always } from 'vs/base/common/async';
import { illegalState } from 'vs/base/common/errors';
import { ExtHostDocumentSaveParticipantShape, MainThreadTextEditorsShape, ResourceTextEditDto } from 'vs/workbench/api/node/extHost.protocol';

View File

@@ -5,7 +5,7 @@
'use strict';
import { Event, Emitter } from 'vs/base/common/event';
import URI, { UriComponents } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as TypeConverters from './extHostTypeConverters';
import { TPromise } from 'vs/base/common/winjs.base';
@@ -21,18 +21,16 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
private _onDidRemoveDocument = new Emitter<vscode.TextDocument>();
private _onDidChangeDocument = new Emitter<vscode.TextDocumentChangeEvent>();
private _onDidSaveDocument = new Emitter<vscode.TextDocument>();
private _onDidRenameResource = new Emitter<vscode.ResourceRenamedEvent>();
readonly onDidAddDocument: Event<vscode.TextDocument> = this._onDidAddDocument.event;
readonly onDidRemoveDocument: Event<vscode.TextDocument> = this._onDidRemoveDocument.event;
readonly onDidChangeDocument: Event<vscode.TextDocumentChangeEvent> = this._onDidChangeDocument.event;
readonly onDidSaveDocument: Event<vscode.TextDocument> = this._onDidSaveDocument.event;
readonly onDidRenameResource: Event<vscode.ResourceRenamedEvent> = this._onDidRenameResource.event;
private _toDispose: IDisposable[];
private _proxy: MainThreadDocumentsShape;
private _documentsAndEditors: ExtHostDocumentsAndEditors;
private _documentLoader = new Map<string, TPromise<ExtHostDocumentData>>();
private _documentLoader = new Map<string, Thenable<ExtHostDocumentData>>();
constructor(mainContext: IMainContext, documentsAndEditors: ExtHostDocumentsAndEditors) {
this._proxy = mainContext.getProxy(MainContext.MainThreadDocuments);
@@ -71,7 +69,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
return undefined;
}
public ensureDocumentData(uri: URI): TPromise<ExtHostDocumentData> {
public ensureDocumentData(uri: URI): Thenable<ExtHostDocumentData> {
let cached = this._documentsAndEditors.getDocument(uri.toString());
if (cached) {
@@ -93,7 +91,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
return promise;
}
public createDocumentData(options?: { language?: string; content?: string }): TPromise<URI> {
public createDocumentData(options?: { language?: string; content?: string }): Thenable<URI> {
return this._proxy.$tryCreateDocument(options).then(data => URI.revive(data));
}
@@ -150,11 +148,4 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
public setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void {
setWordDefinitionFor(modeId, wordDefinition);
}
public $onDidRename(oldURL: UriComponents, newURL: UriComponents): void {
const oldResource = URI.revive(oldURL);
const newResource = URI.revive(newURL);
this._onDidRenameResource.fire({ oldResource, newResource });
}
}

View File

@@ -11,7 +11,7 @@ import { ExtHostDocumentData } from './extHostDocumentData';
import { ExtHostTextEditor } from './extHostTextEditor';
import * as assert from 'assert';
import * as typeConverters from './extHostTypeConverters';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { Disposable } from './extHostTypes';
export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape {

View File

@@ -10,7 +10,6 @@ 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 { ExtHostLogger } from 'vs/workbench/api/node/extHostLogService';
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = TPromise.wrap<void>(void 0);
@@ -27,8 +26,7 @@ export interface IExtensionContext {
extensionPath: string;
storagePath: string;
asAbsolutePath(relativePath: string): string;
logger: ExtHostLogger;
readonly logDirectory: string;
readonly logPath: string;
}
/**

View File

@@ -12,8 +12,8 @@ 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, checkProposedApiEnabled } from 'vs/workbench/api/node/extHost.api.impl';
import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape, IExtHostContext } from './extHost.protocol';
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 } from 'vs/workbench/api/node/extHostExtensionActivator';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
@@ -21,7 +21,7 @@ import { TernarySearchTree } from 'vs/base/common/map';
import { Barrier } from 'vs/base/common/async';
import { ILogService } from 'vs/platform/log/common/log';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
class ExtensionMemento implements IExtensionMemento {
@@ -29,7 +29,7 @@ class ExtensionMemento implements IExtensionMemento {
private readonly _shared: boolean;
private readonly _storage: ExtHostStorage;
private readonly _init: TPromise<ExtensionMemento>;
private readonly _init: Thenable<ExtensionMemento>;
private _value: { [n: string]: any; };
constructor(id: string, global: boolean, storage: ExtHostStorage) {
@@ -43,7 +43,7 @@ class ExtensionMemento implements IExtensionMemento {
});
}
get whenReady(): TPromise<ExtensionMemento> {
get whenReady(): Thenable<ExtensionMemento> {
return this._init;
}
@@ -68,7 +68,7 @@ class ExtensionStoragePath {
private readonly _workspace: IWorkspaceData;
private readonly _environment: IEnvironment;
private readonly _ready: TPromise<string>;
private readonly _ready: Promise<string>;
private _value: string;
constructor(workspace: IWorkspaceData, environment: IEnvironment) {
@@ -77,7 +77,7 @@ class ExtensionStoragePath {
this._ready = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
}
get whenReady(): TPromise<any> {
get whenReady(): Promise<any> {
return this._ready;
}
@@ -88,13 +88,13 @@ class ExtensionStoragePath {
return undefined;
}
private async _getOrCreateWorkspaceStoragePath(): TPromise<string> {
private async _getOrCreateWorkspaceStoragePath(): Promise<string> {
if (!this._workspace) {
return TPromise.as(undefined);
}
const storageName = this._workspace.id;
const storagePath = join(this._environment.appSettingsHome, 'workspaceStorage', storageName);
const storagePath = join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName);
const exists = await dirExists(storagePath);
@@ -136,7 +136,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
*/
constructor(initData: IInitData,
extHostContext: IExtHostContext,
extHostContext: IMainContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
extHostLogService: ExtHostLogService
@@ -361,14 +361,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
storagePath: this._storagePath.value(extensionDescription),
asAbsolutePath: (relativePath: string) => { return join(extensionDescription.extensionLocation.fsPath, relativePath); },
get logger() {
checkProposedApiEnabled(extensionDescription);
return that._extHostLogService.getExtLogger(extensionDescription.id);
},
get logDirectory() {
checkProposedApiEnabled(extensionDescription);
return that._extHostLogService.getLogDirectory(extensionDescription.id);
}
logPath: that._extHostLogService.getLogDirectory(extensionDescription.id)
});
});
}
@@ -409,7 +402,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
// -- called by main thread
public $activateByEvent(activationEvent: string): TPromise<void> {
public $activateByEvent(activationEvent: string): Thenable<void> {
return this.activateByEvent(activationEvent, false);
}
}

View File

@@ -4,17 +4,16 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
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 } from 'vs/base/common/lifecycle';
import { asWinJsPromise } from 'vs/base/common/async';
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 { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { Schemas } from 'vs/base/common/network';
import { LabelRules } from 'vs/platform/label/common/label';
class FsLinkProvider implements vscode.DocumentLinkProvider {
@@ -83,7 +82,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
extHostLanguageFeatures.registerDocumentLinkProvider('*', this._linkProvider);
}
registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean } = {}) {
registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean, isReadonly?: boolean } = {}) {
if (this._usedSchemes.has(scheme)) {
throw new Error(`a provider for the scheme '${scheme}' is already registered`);
@@ -98,9 +97,17 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
if (options.isCaseSensitive) {
capabilites += files.FileSystemProviderCapabilities.PathCaseSensitive;
}
if (options.isReadonly) {
capabilites += files.FileSystemProviderCapabilities.Readonly;
}
if (typeof provider.copy === 'function') {
capabilites += files.FileSystemProviderCapabilities.FileFolderCopy;
}
if (typeof provider.open === 'function' && typeof provider.close === 'function'
&& typeof provider.read === 'function' && typeof provider.write === 'function'
) {
capabilites += files.FileSystemProviderCapabilities.FileOpenReadWriteClose;
}
this._proxy.$registerFileSystemProvider(handle, scheme, capabilites);
@@ -129,15 +136,17 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
this._proxy.$onFileSystemChange(handle, mapped);
});
return {
dispose: () => {
subscription.dispose();
this._linkProvider.delete(scheme);
this._usedSchemes.delete(scheme);
this._fsProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
}
};
return toDisposable(() => {
subscription.dispose();
this._linkProvider.delete(scheme);
this._usedSchemes.delete(scheme);
this._fsProvider.delete(handle);
this._proxy.$unregisterProvider(handle);
});
}
setUriFormatter(scheme: string, formatter: LabelRules): void {
this._proxy.$setUriFormatter(scheme, formatter);
}
private static _asIStat(stat: vscode.FileStat): files.IStat {
@@ -145,47 +154,43 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
return { type, ctime, mtime, size };
}
$stat(handle: number, resource: UriComponents): TPromise<files.IStat, any> {
return asWinJsPromise(() => this._fsProvider.get(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat);
$stat(handle: number, resource: UriComponents): Promise<files.IStat> {
return Promise.resolve(this._fsProvider.get(handle).stat(URI.revive(resource))).then(ExtHostFileSystem._asIStat);
}
$readdir(handle: number, resource: UriComponents): TPromise<[string, files.FileType][], any> {
return asWinJsPromise(() => this._fsProvider.get(handle).readDirectory(URI.revive(resource)));
$readdir(handle: number, resource: UriComponents): Promise<[string, files.FileType][]> {
return Promise.resolve(this._fsProvider.get(handle).readDirectory(URI.revive(resource)));
}
$readFile(handle: number, resource: UriComponents): TPromise<string> {
return asWinJsPromise(() => {
return this._fsProvider.get(handle).readFile(URI.revive(resource));
}).then(data => {
return Buffer.isBuffer(data) ? data.toString('base64') : Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString('base64');
$readFile(handle: number, resource: UriComponents): Promise<Buffer> {
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, base64Content: string, opts: files.FileWriteOptions): TPromise<void, any> {
return asWinJsPromise(() => this._fsProvider.get(handle).writeFile(URI.revive(resource), Buffer.from(base64Content, 'base64'), opts));
$writeFile(handle: number, resource: UriComponents, content: Buffer, opts: files.FileWriteOptions): Promise<void> {
return Promise.resolve(this._fsProvider.get(handle).writeFile(URI.revive(resource), content, opts));
}
$delete(handle: number, resource: UriComponents): TPromise<void, any> {
return asWinJsPromise(() => this._fsProvider.get(handle).delete(URI.revive(resource), { recursive: true }));
$delete(handle: number, resource: UriComponents, opts: files.FileDeleteOptions): Promise<void> {
return Promise.resolve(this._fsProvider.get(handle).delete(URI.revive(resource), opts));
}
$rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): TPromise<void, any> {
return asWinJsPromise(() => this._fsProvider.get(handle).rename(URI.revive(oldUri), URI.revive(newUri), opts));
$rename(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): Promise<void> {
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): TPromise<void, any> {
return asWinJsPromise(() => this._fsProvider.get(handle).copy(URI.revive(oldUri), URI.revive(newUri), opts));
$copy(handle: number, oldUri: UriComponents, newUri: UriComponents, opts: files.FileOverwriteOptions): Promise<void> {
return Promise.resolve(this._fsProvider.get(handle).copy(URI.revive(oldUri), URI.revive(newUri), opts));
}
$mkdir(handle: number, resource: UriComponents): TPromise<void, any> {
return asWinJsPromise(() => this._fsProvider.get(handle).createDirectory(URI.revive(resource)));
$mkdir(handle: number, resource: UriComponents): Promise<void> {
return Promise.resolve(this._fsProvider.get(handle).createDirectory(URI.revive(resource)));
}
$watch(handle: number, session: number, resource: UriComponents, opts: files.IWatchOptions): void {
asWinJsPromise(() => {
let subscription = this._fsProvider.get(handle).watch(URI.revive(resource), opts);
this._watches.set(session, subscription);
});
let subscription = this._fsProvider.get(handle).watch(URI.revive(resource), opts);
this._watches.set(session, subscription);
}
$unwatch(session: number): void {
@@ -195,4 +200,21 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
this._watches.delete(session);
}
}
$open(handle: number, resource: UriComponents): Promise<number> {
return Promise.resolve(this._fsProvider.get(handle).open(URI.revive(resource)));
}
$close(handle: number, fd: number): Promise<void> {
return Promise.resolve(this._fsProvider.get(handle).close(fd));
}
$read(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Promise<number> {
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> {
return Promise.resolve(this._fsProvider.get(handle).write(fd, pos, data, offset, length));
}
}

View File

@@ -4,18 +4,22 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from './extHostTypes';
import { parse, IRelativePattern } from 'vs/base/common/glob';
import { Uri, FileSystemWatcher as _FileSystemWatcher } from 'vscode';
import { FileSystemEvents, ExtHostFileSystemEventServiceShape } from './extHost.protocol';
import URI from 'vs/base/common/uri';
import { flatten } from 'vs/base/common/arrays';
import { AsyncEmitter, Emitter, Event } from 'vs/base/common/event';
import { IRelativePattern, parse } from 'vs/base/common/glob';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import * as vscode from 'vscode';
import { ExtHostFileSystemEventServiceShape, FileSystemEvents, IMainContext, MainContext, ResourceFileEditDto, ResourceTextEditDto, MainThreadTextEditorsShape } from './extHost.protocol';
import * as typeConverter from './extHostTypeConverters';
import { Disposable, WorkspaceEdit } from './extHostTypes';
class FileSystemWatcher implements _FileSystemWatcher {
class FileSystemWatcher implements vscode.FileSystemWatcher {
private _onDidCreate = new Emitter<Uri>();
private _onDidChange = new Emitter<Uri>();
private _onDidDelete = new Emitter<Uri>();
private _onDidCreate = new Emitter<vscode.Uri>();
private _onDidChange = new Emitter<vscode.Uri>();
private _onDidDelete = new Emitter<vscode.Uri>();
private _disposable: Disposable;
private _config: number;
@@ -80,31 +84,100 @@ class FileSystemWatcher implements _FileSystemWatcher {
this._disposable.dispose();
}
get onDidCreate(): Event<Uri> {
get onDidCreate(): Event<vscode.Uri> {
return this._onDidCreate.event;
}
get onDidChange(): Event<Uri> {
get onDidChange(): Event<vscode.Uri> {
return this._onDidChange.event;
}
get onDidDelete(): Event<Uri> {
get onDidDelete(): Event<vscode.Uri> {
return this._onDidDelete.event;
}
}
interface WillRenameListener {
extension: IExtensionDescription;
(e: vscode.FileWillRenameEvent): any;
}
export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServiceShape {
private _emitter = new Emitter<FileSystemEvents>();
private readonly _onFileEvent = new Emitter<FileSystemEvents>();
private readonly _onDidRenameFile = new Emitter<vscode.FileRenameEvent>();
private readonly _onWillRenameFile = new AsyncEmitter<vscode.FileWillRenameEvent>();
constructor() {
readonly onDidRenameFile: Event<vscode.FileRenameEvent> = this._onDidRenameFile.event;
constructor(
mainContext: IMainContext,
private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _mainThreadTextEditors: MainThreadTextEditorsShape = mainContext.getProxy(MainContext.MainThreadTextEditors)
) {
//
}
public createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): _FileSystemWatcher {
return new FileSystemWatcher(this._emitter.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
public createFileSystemWatcher(globPattern: string | IRelativePattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): vscode.FileSystemWatcher {
return new FileSystemWatcher(this._onFileEvent.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
}
$onFileEvent(events: FileSystemEvents) {
this._emitter.fire(events);
this._onFileEvent.fire(events);
}
$onFileRename(oldUri: UriComponents, newUri: UriComponents) {
this._onDidRenameFile.fire(Object.freeze({ oldUri: URI.revive(oldUri), newUri: URI.revive(newUri) }));
}
getOnWillRenameFileEvent(extension: IExtensionDescription): Event<vscode.FileWillRenameEvent> {
return (listener, thisArg, disposables) => {
let wrappedListener = <WillRenameListener><any>function () {
listener.apply(thisArg, arguments);
};
wrappedListener.extension = extension;
return this._onWillRenameFile.event(wrappedListener, undefined, disposables);
};
}
$onWillRename(oldUriDto: UriComponents, newUriDto: UriComponents): Thenable<any> {
const oldUri = URI.revive(oldUriDto);
const newUri = URI.revive(newUriDto);
const edits: WorkspaceEdit[] = [];
return Promise.resolve(this._onWillRenameFile.fireAsync((bucket, _listener) => {
return {
oldUri,
newUri,
waitUntil: (thenable: Thenable<vscode.WorkspaceEdit>): void => {
if (Object.isFrozen(bucket)) {
throw new TypeError('waitUntil cannot be called async');
}
const index = bucket.length;
const wrappedThenable = Promise.resolve(thenable).then(result => {
// ignore all results except for WorkspaceEdits. Those
// are stored in a spare array
if (result instanceof WorkspaceEdit) {
edits[index] = result;
}
});
bucket.push(wrappedThenable);
}
};
}).then(() => {
if (edits.length === 0) {
return undefined;
}
// flatten all WorkspaceEdits collected via waitUntil-call
// and apply them in one go.
let allEdits = new Array<(ResourceFileEditDto | ResourceTextEditDto)[]>();
for (let edit of edits) {
if (edit) { // sparse array
let { edits } = typeConverter.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
allEdits.push(edits);
}
}
return this._mainThreadTextEditors.$tryApplyWorkspaceEdit({ edits: flatten(allEdits) });
}));
}
}

View File

@@ -4,26 +4,29 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI, { UriComponents } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { mixin } from 'vs/base/common/objects';
import * as vscode from 'vscode';
import * as typeConvert from 'vs/workbench/api/node/extHostTypeConverters';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, SymbolInformation2 } from 'vs/workbench/api/node/extHostTypes';
import { Range, Disposable, CompletionList, SnippetString, CodeActionKind, SymbolInformation, DocumentSymbol } from 'vs/workbench/api/node/extHostTypes';
import { ISingleEditOperation } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { asWinJsPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, SymbolInformationDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter } from './extHost.protocol';
import { asThenable } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, SuggestionDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto } from './extHost.protocol';
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
import { isFalsyOrEmpty } 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';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
// --- adapter
@@ -37,21 +40,21 @@ class OutlineAdapter {
this._provider = provider;
}
provideDocumentSymbols(resource: URI): TPromise<SymbolInformationDto[]> {
provideDocumentSymbols(resource: URI, token: CancellationToken): Thenable<modes.DocumentSymbol[]> {
let doc = this._documents.getDocumentData(resource).document;
return asWinJsPromise(token => this._provider.provideDocumentSymbols(doc, token)).then(value => {
return asThenable(() => this._provider.provideDocumentSymbols(doc, token)).then(value => {
if (isFalsyOrEmpty(value)) {
return undefined;
}
let [probe] = value;
if (!(probe instanceof SymbolInformation2)) {
value = OutlineAdapter._asSymbolHierarchy(resource, <SymbolInformation[]>value);
if (value[0] instanceof DocumentSymbol) {
return (<DocumentSymbol[]>value).map(typeConvert.DocumentSymbol.from);
} else {
return OutlineAdapter._asDocumentSymbolTree(resource, <SymbolInformation[]>value);
}
return (<SymbolInformation2[]>value).map(typeConvert.SymbolInformation2.from);
});
}
private static _asSymbolHierarchy(resource: URI, info: SymbolInformation[]): vscode.SymbolInformation2[] {
private static _asDocumentSymbolTree(resource: URI, info: SymbolInformation[]): modes.DocumentSymbol[] {
// first sort by start (and end) and then loop over all elements
// and build a tree based on containment.
info = info.slice(0).sort((a, b) => {
@@ -61,13 +64,17 @@ class OutlineAdapter {
}
return res;
});
let res: SymbolInformation2[] = [];
let parentStack: SymbolInformation2[] = [];
let res: modes.DocumentSymbol[] = [];
let parentStack: modes.DocumentSymbol[] = [];
for (let i = 0; i < info.length; i++) {
let element = new SymbolInformation2(info[i].name, info[i].kind, '', info[i].location);
element.containerName = info[i].containerName;
element.location = info[i].location; // todo@joh make this proper
element.location.uri = element.location.uri || resource;
let element = <modes.DocumentSymbol>{
name: info[i].name,
kind: typeConvert.SymbolKind.from(info[i].kind),
containerName: info[i].containerName,
range: typeConvert.Range.from(info[i].location.range),
selectionRange: typeConvert.Range.from(info[i].location.range),
children: []
};
while (true) {
if (parentStack.length === 0) {
@@ -76,7 +83,7 @@ class OutlineAdapter {
break;
}
let parent = parentStack[parentStack.length - 1];
if (parent.definingRange.contains(element.definingRange) && !parent.definingRange.isEqual(element.definingRange)) {
if (EditorRange.containsRange(parent.range, element.range) && !EditorRange.equalsRange(parent.range, element.range)) {
parent.children.push(element);
parentStack.push(element);
break;
@@ -99,10 +106,10 @@ class CodeLensAdapter {
private readonly _provider: vscode.CodeLensProvider
) { }
provideCodeLenses(resource: URI): TPromise<modes.ICodeLensSymbol[]> {
provideCodeLenses(resource: URI, token: CancellationToken): Thenable<modes.ICodeLensSymbol[]> {
const doc = this._documents.getDocumentData(resource).document;
return asWinJsPromise(token => this._provider.provideCodeLenses(doc, token)).then(lenses => {
return asThenable(() => this._provider.provideCodeLenses(doc, token)).then(lenses => {
if (Array.isArray(lenses)) {
return lenses.map(lens => {
const id = this._heapService.keep(lens);
@@ -116,18 +123,18 @@ class CodeLensAdapter {
});
}
resolveCodeLens(resource: URI, symbol: modes.ICodeLensSymbol): TPromise<modes.ICodeLensSymbol> {
resolveCodeLens(resource: URI, symbol: modes.ICodeLensSymbol, token: CancellationToken): Thenable<modes.ICodeLensSymbol> {
const lens = this._heapService.get<vscode.CodeLens>(ObjectIdentifier.of(symbol));
if (!lens) {
return undefined;
}
let resolve: TPromise<vscode.CodeLens>;
let resolve: Thenable<vscode.CodeLens>;
if (typeof this._provider.resolveCodeLens !== 'function' || lens.isResolved) {
resolve = TPromise.as(lens);
} else {
resolve = asWinJsPromise(token => this._provider.resolveCodeLens(lens, token));
resolve = asThenable(() => this._provider.resolveCodeLens(lens, token));
}
return resolve.then(newLens => {
@@ -138,6 +145,15 @@ class CodeLensAdapter {
}
}
function convertToDefinitionLinks(value: vscode.Definition): modes.DefinitionLink[] {
if (Array.isArray(value)) {
return (value as (vscode.DefinitionLink | vscode.Location)[]).map(typeConvert.DefinitionLink.from);
} else if (value) {
return [typeConvert.DefinitionLink.from(value)];
}
return undefined;
}
class DefinitionAdapter {
constructor(
@@ -145,17 +161,10 @@ class DefinitionAdapter {
private readonly _provider: vscode.DefinitionProvider
) { }
provideDefinition(resource: URI, position: IPosition): TPromise<modes.Definition> {
provideDefinition(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideDefinition(doc, pos, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.location.from);
} else if (value) {
return typeConvert.location.from(value);
}
return undefined;
});
return asThenable(() => this._provider.provideDefinition(doc, pos, token)).then(convertToDefinitionLinks);
}
}
@@ -166,17 +175,10 @@ class ImplementationAdapter {
private readonly _provider: vscode.ImplementationProvider
) { }
provideImplementation(resource: URI, position: IPosition): TPromise<modes.Definition> {
provideImplementation(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideImplementation(doc, pos, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.location.from);
} else if (value) {
return typeConvert.location.from(value);
}
return undefined;
});
return asThenable(() => this._provider.provideImplementation(doc, pos, token)).then(convertToDefinitionLinks);
}
}
@@ -187,17 +189,10 @@ class TypeDefinitionAdapter {
private readonly _provider: vscode.TypeDefinitionProvider
) { }
provideTypeDefinition(resource: URI, position: IPosition): TPromise<modes.Definition> {
provideTypeDefinition(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
const doc = this._documents.getDocumentData(resource).document;
const pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideTypeDefinition(doc, pos, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.location.from);
} else if (value) {
return typeConvert.location.from(value);
}
return undefined;
});
return asThenable(() => this._provider.provideTypeDefinition(doc, pos, token)).then(convertToDefinitionLinks);
}
}
@@ -208,12 +203,12 @@ class HoverAdapter {
private readonly _provider: vscode.HoverProvider,
) { }
public provideHover(resource: URI, position: IPosition): TPromise<modes.Hover> {
public provideHover(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.Hover> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideHover(doc, pos, token)).then(value => {
return asThenable(() => this._provider.provideHover(doc, pos, token)).then(value => {
if (!value || isFalsyOrEmpty(value.contents)) {
return undefined;
}
@@ -236,12 +231,12 @@ class DocumentHighlightAdapter {
private readonly _provider: vscode.DocumentHighlightProvider
) { }
provideDocumentHighlights(resource: URI, position: IPosition): TPromise<modes.DocumentHighlight[]> {
provideDocumentHighlights(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.DocumentHighlight[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideDocumentHighlights(doc, pos, token)).then(value => {
return asThenable(() => this._provider.provideDocumentHighlights(doc, pos, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.DocumentHighlight.from);
}
@@ -257,11 +252,11 @@ class ReferenceAdapter {
private readonly _provider: vscode.ReferenceProvider
) { }
provideReferences(resource: URI, position: IPosition, context: modes.ReferenceContext): TPromise<modes.Location[]> {
provideReferences(resource: URI, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Thenable<modes.Location[]> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideReferences(doc, pos, context, token)).then(value => {
return asThenable(() => this._provider.provideReferences(doc, pos, context, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.location.from);
}
@@ -280,10 +275,12 @@ class CodeActionAdapter {
private readonly _documents: ExtHostDocuments,
private readonly _commands: CommandsConverter,
private readonly _diagnostics: ExtHostDiagnostics,
private readonly _provider: vscode.CodeActionProvider
private readonly _provider: vscode.CodeActionProvider,
private readonly _logService: ILogService,
private readonly _extensionId: string
) { }
provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext): TPromise<CodeActionDto[]> {
provideCodeActions(resource: URI, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Thenable<CodeActionDto[]> {
const doc = this._documents.getDocumentData(resource).document;
const ran = Selection.isISelection(rangeOrSelection)
@@ -299,13 +296,10 @@ class CodeActionAdapter {
const codeActionContext: vscode.CodeActionContext = {
diagnostics: allDiagnostics,
only: context.only ? new CodeActionKind(context.only) : undefined,
triggerKind: context.trigger,
only: context.only ? new CodeActionKind(context.only) : undefined
};
return asWinJsPromise(token =>
this._provider.provideCodeActions(doc, ran, codeActionContext, token)
).then(commandsOrActions => {
return asThenable(() => this._provider.provideCodeActions(doc, ran, codeActionContext, token)).then(commandsOrActions => {
if (isFalsyOrEmpty(commandsOrActions)) {
return undefined;
}
@@ -322,6 +316,14 @@ class CodeActionAdapter {
command: this._commands.toInternal(candidate),
});
} else {
if (codeActionContext.only) {
if (!candidate.kind) {
this._logService.warn(`${this._extensionId} - Code actions of kind '${codeActionContext.only.value} 'requested but returned code action does not have a 'kind'. Code action will be dropped. Please set 'CodeAction.kind'.`);
} else if (!codeActionContext.only.contains(candidate.kind)) {
this._logService.warn(`${this._extensionId} -Code actions of kind '${codeActionContext.only.value} 'requested but returned code action is of kind '${candidate.kind.value}'. Code action will be dropped. Please check 'CodeActionContext.only' to only return requested code actions.`);
}
}
// new school: convert code action
result.push({
title: candidate.title,
@@ -349,11 +351,11 @@ class DocumentFormattingAdapter {
private readonly _provider: vscode.DocumentFormattingEditProvider
) { }
provideDocumentFormattingEdits(resource: URI, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]> {
provideDocumentFormattingEdits(resource: URI, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> {
const { document } = this._documents.getDocumentData(resource);
return asWinJsPromise(token => this._provider.provideDocumentFormattingEdits(document, <any>options, token)).then(value => {
return asThenable(() => this._provider.provideDocumentFormattingEdits(document, <any>options, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.TextEdit.from);
}
@@ -369,12 +371,12 @@ class RangeFormattingAdapter {
private readonly _provider: vscode.DocumentRangeFormattingEditProvider
) { }
provideDocumentRangeFormattingEdits(resource: URI, range: IRange, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]> {
provideDocumentRangeFormattingEdits(resource: URI, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> {
const { document } = this._documents.getDocumentData(resource);
const ran = typeConvert.Range.to(range);
return asWinJsPromise(token => this._provider.provideDocumentRangeFormattingEdits(document, ran, <any>options, token)).then(value => {
return asThenable(() => this._provider.provideDocumentRangeFormattingEdits(document, ran, <any>options, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.TextEdit.from);
}
@@ -392,12 +394,12 @@ class OnTypeFormattingAdapter {
autoFormatTriggerCharacters: string[] = []; // not here
provideOnTypeFormattingEdits(resource: URI, position: IPosition, ch: string, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]> {
provideOnTypeFormattingEdits(resource: URI, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> {
const { document } = this._documents.getDocumentData(resource);
const pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideOnTypeFormattingEdits(document, pos, ch, <any>options, token)).then(value => {
return asThenable(() => this._provider.provideOnTypeFormattingEdits(document, pos, ch, <any>options, token)).then(value => {
if (Array.isArray(value)) {
return value.map(typeConvert.TextEdit.from);
}
@@ -416,9 +418,9 @@ class NavigateTypeAdapter {
this._provider = provider;
}
provideWorkspaceSymbols(search: string): TPromise<WorkspaceSymbolsDto> {
provideWorkspaceSymbols(search: string, token: CancellationToken): Thenable<WorkspaceSymbolsDto> {
const result: WorkspaceSymbolsDto = IdObject.mixin({ symbols: [] });
return asWinJsPromise(token => this._provider.provideWorkspaceSymbols(search, token)).then(value => {
return asThenable(() => this._provider.provideWorkspaceSymbols(search, token)).then(value => {
if (!isFalsyOrEmpty(value)) {
for (const item of value) {
if (!item) {
@@ -429,7 +431,7 @@ class NavigateTypeAdapter {
console.warn('INVALID SymbolInformation, lacks name', item);
continue;
}
const symbol = IdObject.mixin(typeConvert.SymbolInformation.from(item));
const symbol = IdObject.mixin(typeConvert.WorkspaceSymbol.from(item));
this._symbolCache[symbol._id] = item;
result.symbols.push(symbol);
}
@@ -442,7 +444,7 @@ class NavigateTypeAdapter {
});
}
resolveWorkspaceSymbol(symbol: SymbolInformationDto): TPromise<SymbolInformationDto> {
resolveWorkspaceSymbol(symbol: WorkspaceSymbolDto, token: CancellationToken): Thenable<WorkspaceSymbolDto> {
if (typeof this._provider.resolveWorkspaceSymbol !== 'function') {
return TPromise.as(symbol);
@@ -450,8 +452,8 @@ class NavigateTypeAdapter {
const item = this._symbolCache[symbol._id];
if (item) {
return asWinJsPromise(token => this._provider.resolveWorkspaceSymbol(item, token)).then(value => {
return value && mixin(symbol, typeConvert.SymbolInformation.from(value), true);
return asThenable(() => this._provider.resolveWorkspaceSymbol(item, token)).then(value => {
return value && mixin(symbol, typeConvert.WorkspaceSymbol.from(value), true);
});
}
return undefined;
@@ -479,35 +481,28 @@ class RenameAdapter {
private readonly _provider: vscode.RenameProvider
) { }
provideRenameEdits(resource: URI, position: IPosition, newName: string): TPromise<modes.WorkspaceEdit> {
provideRenameEdits(resource: URI, position: IPosition, newName: string, token: CancellationToken): Thenable<WorkspaceEditDto> {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideRenameEdits(doc, pos, newName, token)).then(value => {
return asThenable(() => this._provider.provideRenameEdits(doc, pos, newName, token)).then(value => {
if (!value) {
return undefined;
}
return typeConvert.WorkspaceEdit.from(value);
}, err => {
if (typeof err === 'string') {
return <modes.WorkspaceEdit>{
edits: undefined,
rejectReason: err
};
} else if (err instanceof Error && typeof err.message === 'string') {
return <modes.WorkspaceEdit>{
edits: undefined,
rejectReason: err.message
};
let rejectReason = RenameAdapter._asMessage(err);
if (rejectReason) {
return <WorkspaceEditDto>{ rejectReason, edits: undefined };
} else {
// generic error
return TPromise.wrapError<modes.WorkspaceEdit>(err);
return Promise.reject<WorkspaceEditDto>(err);
}
});
}
resolveRenameLocation(resource: URI, position: IPosition): TPromise<modes.RenameLocation> {
resolveRenameLocation(resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.RenameLocation & modes.Rejection> {
if (typeof this._provider.prepareRename !== 'function') {
return TPromise.as(undefined);
}
@@ -515,7 +510,7 @@ class RenameAdapter {
let doc = this._documents.getDocumentData(resource).document;
let pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.prepareRename(doc, pos, token)).then(rangeOrLocation => {
return asThenable(() => this._provider.prepareRename(doc, pos, token)).then(rangeOrLocation => {
let range: vscode.Range;
let text: string;
@@ -537,8 +532,25 @@ class RenameAdapter {
return undefined;
}
return { range: typeConvert.Range.from(range), text };
}, err => {
let rejectReason = RenameAdapter._asMessage(err);
if (rejectReason) {
return <modes.RenameLocation & modes.Rejection>{ rejectReason, range: undefined, text: undefined };
} else {
return Promise.reject(err);
}
});
}
private static _asMessage(err: any): string {
if (typeof err === 'string') {
return err;
} else if (err instanceof Error && typeof err.message === 'string') {
return err.message;
} else {
return undefined;
}
}
}
class SuggestAdapter {
@@ -560,14 +572,14 @@ class SuggestAdapter {
this._provider = provider;
}
provideCompletionItems(resource: URI, position: IPosition, context: modes.SuggestContext): TPromise<SuggestResultDto> {
provideCompletionItems(resource: URI, position: IPosition, context: modes.SuggestContext, token: CancellationToken): Thenable<SuggestResultDto> {
const doc = this._documents.getDocumentData(resource).document;
const pos = typeConvert.Position.to(position);
return asWinJsPromise<vscode.CompletionItem[] | vscode.CompletionList>(token => {
return this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.from(context));
}).then(value => {
return asThenable<vscode.CompletionItem[] | vscode.CompletionList>(
() => this._provider.provideCompletionItems(doc, pos, token, typeConvert.CompletionContext.from(context))
).then(value => {
const _id = this._idPool++;
@@ -607,7 +619,7 @@ class SuggestAdapter {
});
}
resolveCompletionItem(resource: URI, position: IPosition, suggestion: modes.ISuggestion): TPromise<modes.ISuggestion> {
resolveCompletionItem(resource: URI, position: IPosition, suggestion: modes.ISuggestion, token: CancellationToken): Thenable<modes.ISuggestion> {
if (typeof this._provider.resolveCompletionItem !== 'function') {
return TPromise.as(suggestion);
@@ -619,7 +631,7 @@ class SuggestAdapter {
return TPromise.as(suggestion);
}
return asWinJsPromise(token => this._provider.resolveCompletionItem(item, token)).then(resolvedItem => {
return asThenable(() => this._provider.resolveCompletionItem(item, token)).then(resolvedItem => {
if (!resolvedItem) {
return suggestion;
@@ -658,6 +670,7 @@ class SuggestAdapter {
documentation: item.documentation,
filterText: item.filterText,
sortText: item.sortText,
preselect: item.preselect,
//
insertText: undefined,
additionalTextEdits: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
@@ -711,12 +724,12 @@ class SignatureHelpAdapter {
private readonly _provider: vscode.SignatureHelpProvider
) { }
provideSignatureHelp(resource: URI, position: IPosition): TPromise<modes.SignatureHelp> {
provideSignatureHelp(resource: URI, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Thenable<modes.SignatureHelp> {
const doc = this._documents.getDocumentData(resource).document;
const pos = typeConvert.Position.to(position);
return asWinJsPromise(token => this._provider.provideSignatureHelp(doc, pos, token)).then(value => {
return asThenable(() => this._provider.provideSignatureHelp(doc, pos, token, context)).then(value => {
if (value) {
return typeConvert.SignatureHelp.from(value);
}
@@ -733,10 +746,10 @@ class LinkProviderAdapter {
private readonly _provider: vscode.DocumentLinkProvider
) { }
provideLinks(resource: URI): TPromise<modes.ILink[]> {
provideLinks(resource: URI, token: CancellationToken): Thenable<modes.ILink[]> {
const doc = this._documents.getDocumentData(resource).document;
return asWinJsPromise(token => this._provider.provideDocumentLinks(doc, token)).then(links => {
return asThenable(() => this._provider.provideDocumentLinks(doc, token)).then(links => {
if (!Array.isArray(links)) {
return undefined;
}
@@ -751,7 +764,7 @@ class LinkProviderAdapter {
});
}
resolveLink(link: modes.ILink): TPromise<modes.ILink> {
resolveLink(link: modes.ILink, token: CancellationToken): Thenable<modes.ILink> {
if (typeof this._provider.resolveDocumentLink !== 'function') {
return undefined;
}
@@ -762,7 +775,7 @@ class LinkProviderAdapter {
return undefined;
}
return asWinJsPromise(token => this._provider.resolveDocumentLink(item, token)).then(value => {
return asThenable(() => this._provider.resolveDocumentLink(item, token)).then(value => {
if (value) {
return typeConvert.DocumentLink.from(value);
}
@@ -778,9 +791,9 @@ class ColorProviderAdapter {
private _provider: vscode.DocumentColorProvider
) { }
provideColors(resource: URI): TPromise<IRawColorInfo[]> {
provideColors(resource: URI, token: CancellationToken): Thenable<IRawColorInfo[]> {
const doc = this._documents.getDocumentData(resource).document;
return asWinJsPromise(token => this._provider.provideDocumentColors(doc, token)).then(colors => {
return asThenable(() => this._provider.provideDocumentColors(doc, token)).then(colors => {
if (!Array.isArray(colors)) {
return [];
}
@@ -796,11 +809,11 @@ class ColorProviderAdapter {
});
}
provideColorPresentations(resource: URI, raw: IRawColorInfo): TPromise<modes.IColorPresentation[]> {
provideColorPresentations(resource: URI, raw: IRawColorInfo, token: CancellationToken): Thenable<modes.IColorPresentation[]> {
const document = this._documents.getDocumentData(resource).document;
const range = typeConvert.Range.to(raw.range);
const color = typeConvert.Color.to(raw.color);
return asWinJsPromise(token => this._provider.provideColorPresentations(color, { document, range }, token)).then(value => {
return asThenable(() => this._provider.provideColorPresentations(color, { document, range }, token)).then(value => {
return value.map(typeConvert.ColorPresentation.from);
});
}
@@ -813,9 +826,9 @@ class FoldingProviderAdapter {
private _provider: vscode.FoldingRangeProvider
) { }
provideFoldingRanges(resource: URI, context: modes.FoldingContext): TPromise<modes.FoldingRange[]> {
provideFoldingRanges(resource: URI, context: modes.FoldingContext, token: CancellationToken): Thenable<modes.FoldingRange[]> {
const doc = this._documents.getDocumentData(resource).document;
return asWinJsPromise(token => this._provider.provideFoldingRanges(doc, context, token)).then(ranges => {
return asThenable(() => this._provider.provideFoldingRanges(doc, context, token)).then(ranges => {
if (!Array.isArray(ranges)) {
return void 0;
}
@@ -845,6 +858,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
private _heapService: ExtHostHeapService;
private _diagnostics: ExtHostDiagnostics;
private _adapter = new Map<number, Adapter>();
private readonly _logService: ILogService;
constructor(
mainContext: IMainContext,
@@ -852,7 +866,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
documents: ExtHostDocuments,
commands: ExtHostCommands,
heapMonitor: ExtHostHeapService,
diagnostics: ExtHostDiagnostics
diagnostics: ExtHostDiagnostics,
logService: ILogService
) {
this._schemeTransformer = schemeTransformer;
this._proxy = mainContext.getProxy(MainContext.MainThreadLanguageFeatures);
@@ -860,6 +875,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
this._commands = commands;
this._heapService = heapMonitor;
this._diagnostics = diagnostics;
this._logService = logService;
}
private _transformDocumentSelector(selector: vscode.DocumentSelector): ISerializedDocumentFilter[] {
@@ -909,7 +925,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return ExtHostLanguageFeatures._handlePool++;
}
private _withAdapter<A, R>(handle: number, ctor: { new(...args: any[]): A }, callback: (adapter: A) => TPromise<R>): TPromise<R> {
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 TPromise.wrapError<R>(new Error('no adapter found'));
@@ -925,14 +941,14 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
// --- outline
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, extensionId?: string): vscode.Disposable {
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), extensionId);
this._proxy.$registerOutlineSupport(handle, this._transformDocumentSelector(selector), extension ? extension.displayName || extension.name : undefined);
return this._createDisposable(handle);
}
$provideDocumentSymbols(handle: number, resource: UriComponents): TPromise<SymbolInformationDto[]> {
return this._withAdapter(handle, OutlineAdapter, adapter => adapter.provideDocumentSymbols(URI.revive(resource)));
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Thenable<modes.DocumentSymbol[]> {
return this._withAdapter(handle, OutlineAdapter, adapter => adapter.provideDocumentSymbols(URI.revive(resource), token));
}
// --- code lens
@@ -953,12 +969,12 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return result;
}
$provideCodeLenses(handle: number, resource: UriComponents): TPromise<modes.ICodeLensSymbol[]> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource)));
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Thenable<modes.ICodeLensSymbol[]> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token));
}
$resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol): TPromise<modes.ICodeLensSymbol> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(URI.revive(resource), symbol));
$resolveCodeLens(handle: number, resource: UriComponents, symbol: modes.ICodeLensSymbol, token: CancellationToken): Thenable<modes.ICodeLensSymbol> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(URI.revive(resource), symbol, token));
}
// --- declaration
@@ -969,8 +985,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.Definition> {
return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position));
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position, token));
}
registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable {
@@ -979,8 +995,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideImplementation(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.Definition> {
return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position));
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position, token));
}
registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable {
@@ -989,8 +1005,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.Definition> {
return this._withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position));
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.DefinitionLink[]> {
return this._withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position, token));
}
// --- extra info
@@ -1001,8 +1017,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideHover(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.Hover> {
return this._withAdapter(handle, HoverAdapter, adpater => adpater.provideHover(URI.revive(resource), position));
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.Hover> {
return this._withAdapter(handle, HoverAdapter, adapter => adapter.provideHover(URI.revive(resource), position, token));
}
// --- occurrences
@@ -1013,8 +1029,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.DocumentHighlight[]> {
return this._withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position));
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Thenable<modes.DocumentHighlight[]> {
return this._withAdapter(handle, DocumentHighlightAdapter, adapter => adapter.provideDocumentHighlights(URI.revive(resource), position, token));
}
// --- references
@@ -1025,21 +1041,21 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext): TPromise<modes.Location[]> {
return this._withAdapter(handle, ReferenceAdapter, adapter => adapter.provideReferences(URI.revive(resource), position, context));
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Thenable<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, metadata?: vscode.CodeActionProviderMetadata): vscode.Disposable {
const handle = this._addNewAdapter(new CodeActionAdapter(this._documents, this._commands.converter, this._diagnostics, provider));
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 : ''));
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): TPromise<CodeActionDto[]> {
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), rangeOrSelection, context));
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Thenable<CodeActionDto[]> {
return this._withAdapter(handle, CodeActionAdapter, adapter => adapter.provideCodeActions(URI.revive(resource), rangeOrSelection, context, token));
}
// --- formatting
@@ -1050,8 +1066,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]> {
return this._withAdapter(handle, DocumentFormattingAdapter, adapter => adapter.provideDocumentFormattingEdits(URI.revive(resource), options));
$provideDocumentFormattingEdits(handle: number, resource: UriComponents, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> {
return this._withAdapter(handle, DocumentFormattingAdapter, adapter => adapter.provideDocumentFormattingEdits(URI.revive(resource), options, token));
}
registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable {
@@ -1060,8 +1076,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]> {
return this._withAdapter(handle, RangeFormattingAdapter, adapter => adapter.provideDocumentRangeFormattingEdits(URI.revive(resource), range, options));
$provideDocumentRangeFormattingEdits(handle: number, resource: UriComponents, range: IRange, options: modes.FormattingOptions, token: CancellationToken): Thenable<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 {
@@ -1070,8 +1086,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions): TPromise<ISingleEditOperation[]> {
return this._withAdapter(handle, OnTypeFormattingAdapter, adapter => adapter.provideOnTypeFormattingEdits(URI.revive(resource), position, ch, options));
$provideOnTypeFormattingEdits(handle: number, resource: UriComponents, position: IPosition, ch: string, options: modes.FormattingOptions, token: CancellationToken): Thenable<ISingleEditOperation[]> {
return this._withAdapter(handle, OnTypeFormattingAdapter, adapter => adapter.provideOnTypeFormattingEdits(URI.revive(resource), position, ch, options, token));
}
// --- navigate types
@@ -1082,15 +1098,15 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideWorkspaceSymbols(handle: number, search: string): TPromise<WorkspaceSymbolsDto> {
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.provideWorkspaceSymbols(search));
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Thenable<WorkspaceSymbolsDto> {
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.provideWorkspaceSymbols(search, token));
}
$resolveWorkspaceSymbol(handle: number, symbol: SymbolInformationDto): TPromise<SymbolInformationDto> {
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.resolveWorkspaceSymbol(symbol));
$resolveWorkspaceSymbol(handle: number, symbol: WorkspaceSymbolDto, token: CancellationToken): Thenable<WorkspaceSymbolDto> {
return this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.resolveWorkspaceSymbol(symbol, token));
}
$releaseWorkspaceSymbols(handle: number, id: number) {
$releaseWorkspaceSymbols(handle: number, id: number): void {
this._withAdapter(handle, NavigateTypeAdapter, adapter => adapter.releaseWorkspaceSymbols(id));
}
@@ -1102,12 +1118,12 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string): TPromise<modes.WorkspaceEdit> {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.provideRenameEdits(URI.revive(resource), position, newName));
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Thenable<WorkspaceEditDto> {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.provideRenameEdits(URI.revive(resource), position, newName, token));
}
$resolveRenameLocation(handle: number, resource: URI, position: IPosition): TPromise<modes.RenameLocation> {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.resolveRenameLocation(URI.revive(resource), position));
$resolveRenameLocation(handle: number, resource: URI, position: IPosition, token: CancellationToken): Thenable<modes.RenameLocation> {
return this._withAdapter(handle, RenameAdapter, adapter => adapter.resolveRenameLocation(URI.revive(resource), position, token));
}
// --- suggestion
@@ -1118,12 +1134,12 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.SuggestContext): TPromise<SuggestResultDto> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context));
$provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: modes.SuggestContext, token: CancellationToken): Thenable<SuggestResultDto> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token));
}
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.ISuggestion): TPromise<modes.ISuggestion> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, suggestion));
$resolveCompletionItem(handle: number, resource: UriComponents, position: IPosition, suggestion: modes.ISuggestion, token: CancellationToken): Thenable<modes.ISuggestion> {
return this._withAdapter(handle, SuggestAdapter, adapter => adapter.resolveCompletionItem(URI.revive(resource), position, suggestion, token));
}
$releaseCompletionItems(handle: number, id: number): void {
@@ -1138,8 +1154,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition): TPromise<modes.SignatureHelp> {
return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position));
$provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: modes.SignatureHelpContext, token: CancellationToken): Thenable<modes.SignatureHelp> {
return this._withAdapter(handle, SignatureHelpAdapter, adapter => adapter.provideSignatureHelp(URI.revive(resource), position, context, token));
}
// --- links
@@ -1150,12 +1166,12 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideDocumentLinks(handle: number, resource: UriComponents): TPromise<modes.ILink[]> {
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(URI.revive(resource)));
$provideDocumentLinks(handle: number, resource: UriComponents, token: CancellationToken): Thenable<modes.ILink[]> {
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.provideLinks(URI.revive(resource), token));
}
$resolveDocumentLink(handle: number, link: modes.ILink): TPromise<modes.ILink> {
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link));
$resolveDocumentLink(handle: number, link: modes.ILink, token: CancellationToken): Thenable<modes.ILink> {
return this._withAdapter(handle, LinkProviderAdapter, adapter => adapter.resolveLink(link, token));
}
registerColorProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider): vscode.Disposable {
@@ -1164,12 +1180,12 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideDocumentColors(handle: number, resource: UriComponents): TPromise<IRawColorInfo[]> {
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColors(URI.revive(resource)));
$provideDocumentColors(handle: number, resource: UriComponents, token: CancellationToken): Thenable<IRawColorInfo[]> {
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColors(URI.revive(resource), token));
}
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo): TPromise<modes.IColorPresentation[]> {
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo));
$provideColorPresentations(handle: number, resource: UriComponents, colorInfo: IRawColorInfo, token: CancellationToken): Thenable<modes.IColorPresentation[]> {
return this._withAdapter(handle, ColorProviderAdapter, adapter => adapter.provideColorPresentations(URI.revive(resource), colorInfo, token));
}
registerFoldingRangeProvider(selector: vscode.DocumentSelector, provider: vscode.FoldingRangeProvider): vscode.Disposable {
@@ -1178,8 +1194,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return this._createDisposable(handle);
}
$provideFoldingRanges(handle: number, resource: UriComponents, context: vscode.FoldingContext): TPromise<modes.FoldingRange[]> {
return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource), context));
$provideFoldingRanges(handle: number, resource: UriComponents, context: vscode.FoldingContext, token: CancellationToken): Thenable<modes.FoldingRange[]> {
return this._withAdapter(handle, FoldingProviderAdapter, adapter => adapter.provideFoldingRanges(URI.revive(resource), context, token));
}
// --- configuration

View File

@@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { MainContext, MainThreadLanguagesShape, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
export class ExtHostLanguages {
@@ -17,7 +17,10 @@ export class ExtHostLanguages {
this._proxy = mainContext.getProxy(MainContext.MainThreadLanguages);
}
getLanguages(): TPromise<string[]> {
getLanguages(): Thenable<string[]> {
return this._proxy.$getLanguages();
}
changeLanguage(documentUri: vscode.Uri, languageId: string): Thenable<void> {
return this._proxy.$changeLanguage(documentUri, languageId);
}
}

View File

@@ -4,78 +4,34 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
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 { 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';
import { URI } from 'vs/base/common/uri';
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
private _loggers: Map<string, ExtHostLogger> = new Map();
private _logsPath: string;
readonly logFile: URI;
constructor(
private _windowId: number,
logLevel: LogLevel,
private _logsPath: string
logsPath: string,
) {
super(createSpdLogService(`exthost${_windowId}`, logLevel, _logsPath));
super(createSpdLogService(ExtensionHostLogFileName, logLevel, logsPath));
this._logsPath = logsPath;
this.logFile = URI.file(join(logsPath, `${ExtensionHostLogFileName}.log`));
}
$setLevel(level: LogLevel): void {
this.setLevel(level);
}
getExtLogger(extensionID: string): ExtHostLogger {
let logger = this._loggers.get(extensionID);
if (!logger) {
logger = this.createLogger(extensionID);
this._loggers.set(extensionID, logger);
}
return logger;
}
getLogDirectory(extensionID: string): string {
return join(this._logsPath, `${extensionID}_${this._windowId}`);
}
private createLogger(extensionID: string): ExtHostLogger {
const logsDirPath = this.getLogDirectory(extensionID);
const logService = createSpdLogService(extensionID, this.getLevel(), logsDirPath);
this._register(this.onDidChangeLogLevel(level => logService.setLevel(level)));
return new ExtHostLogger(logService);
}
}
export class ExtHostLogger implements vscode.Logger {
constructor(
private readonly _logService: ILogService
) { }
trace(message: string, ...args: any[]): void {
return this._logService.trace(message, ...args);
}
debug(message: string, ...args: any[]): void {
return this._logService.debug(message, ...args);
}
info(message: string, ...args: any[]): void {
return this._logService.info(message, ...args);
}
warn(message: string, ...args: any[]): void {
return this._logService.warn(message, ...args);
}
error(message: string | Error, ...args: any[]): void {
return this._logService.error(message, ...args);
}
critical(message: string | Error, ...args: any[]): void {
return this._logService.critical(message, ...args);
return join(this._logsPath, extensionID);
}
}

View File

@@ -6,38 +6,29 @@
import { MainContext, MainThreadOutputServiceShape, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { posix } from 'path';
import { OutputAppender } from 'vs/platform/output/node/outputAppender';
import { toLocalISOString } from 'vs/base/common/date';
export class ExtHostOutputChannel implements vscode.OutputChannel {
export abstract class AbstractExtHostOutputChannel implements vscode.OutputChannel {
private static _idPool = 1;
private _proxy: MainThreadOutputServiceShape;
private _name: string;
private _id: string;
protected readonly _id: Thenable<string>;
private readonly _name: string;
protected readonly _proxy: MainThreadOutputServiceShape;
private _disposed: boolean;
constructor(name: string, proxy: MainThreadOutputServiceShape) {
constructor(name: string, log: boolean, file: URI, proxy: MainThreadOutputServiceShape) {
this._name = name;
this._id = 'extension-output-#' + (ExtHostOutputChannel._idPool++);
this._proxy = proxy;
this._id = proxy.$register(this.name, log, file);
}
get name(): string {
return this._name;
}
dispose(): void {
if (!this._disposed) {
this._proxy.$dispose(this._id, this._name).then(() => {
this._disposed = true;
});
}
}
append(value: string): void {
this.validate();
this._proxy.$append(this._id, this._name, value);
}
abstract append(value: string): void;
appendLine(value: string): void {
this.validate();
@@ -46,44 +37,113 @@ export class ExtHostOutputChannel implements vscode.OutputChannel {
clear(): void {
this.validate();
this._proxy.$clear(this._id, this._name);
this._id.then(id => this._proxy.$clear(id));
}
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
this.validate();
if (typeof columnOrPreserveFocus === 'boolean') {
preserveFocus = columnOrPreserveFocus;
}
this._proxy.$reveal(this._id, this._name, preserveFocus);
this._id.then(id => this._proxy.$reveal(id, typeof columnOrPreserveFocus === 'boolean' ? columnOrPreserveFocus : preserveFocus));
}
hide(): void {
this.validate();
this._proxy.$close(this._id);
this._id.then(id => this._proxy.$close(id));
}
private validate(): void {
protected validate(): void {
if (this._disposed) {
throw new Error('Channel has been closed');
}
}
dispose(): void {
if (!this._disposed) {
this._id
.then(id => this._proxy.$dispose(id))
.then(() => this._disposed = true);
}
}
}
export class ExtHostPushOutputChannel extends AbstractExtHostOutputChannel {
constructor(name: string, proxy: MainThreadOutputServiceShape) {
super(name, false, null, proxy);
}
append(value: string): void {
this.validate();
this._id.then(id => this._proxy.$append(id, value));
}
}
export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChannel {
private static _namePool = 1;
private _appender: OutputAppender;
constructor(name: string, outputDir: string, proxy: MainThreadOutputServiceShape) {
const fileName = `${ExtHostOutputChannelBackedByFile._namePool++}-${name}`;
const file = URI.file(posix.join(outputDir, `${fileName}.log`));
super(name, false, file, proxy);
this._appender = new OutputAppender(fileName, file.fsPath);
}
append(value: string): void {
this.validate();
this._appender.append(value);
}
}
export class ExtHostLogFileOutputChannel extends AbstractExtHostOutputChannel {
constructor(name: string, file: URI, proxy: MainThreadOutputServiceShape) {
super(name, true, file, proxy);
}
append(value: string): void {
throw new Error('Not supported');
}
}
export class ExtHostOutputService {
private _proxy: MainThreadOutputServiceShape;
private _outputDir: string;
constructor(mainContext: IMainContext) {
constructor(logsLocation: URI, mainContext: IMainContext) {
this._outputDir = posix.join(logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
this._proxy = mainContext.getProxy(MainContext.MainThreadOutputService);
}
createOutputChannel(name: string): vscode.OutputChannel {
createOutputChannel(name: string, push: boolean): vscode.OutputChannel {
name = name.trim();
if (!name) {
throw new Error('illegal argument `name`. must not be falsy');
} else {
return new ExtHostOutputChannel(name, this._proxy);
if (push) {
return new ExtHostPushOutputChannel(name, this._proxy);
} else {
// Do not crash if logger cannot be created
try {
return new ExtHostOutputChannelBackedByFile(name, this._outputDir, this._proxy);
} catch (error) {
console.log(error);
return new ExtHostPushOutputChannel(name, this._proxy);
}
}
}
}
createOutputChannelFromLogFile(name: string, file: URI): vscode.OutputChannel {
name = name.trim();
if (!name) {
throw new Error('illegal argument `name`. must not be falsy');
}
if (!file) {
throw new Error('illegal argument `file`. must not be falsy');
}
return new ExtHostLogFileOutputChannel(name, file, this._proxy);
}
}

View File

@@ -8,10 +8,11 @@ import { ProgressOptions } from 'vscode';
import { MainThreadProgressShape, ExtHostProgressShape } from './extHost.protocol';
import { ProgressLocation } from './extHostTypeConverters';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { IProgressStep, Progress } from 'vs/platform/progress/common/progress';
import { Progress } from 'vs/platform/progress/common/progress';
import { localize } from 'vs/nls';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import { debounce } from 'vs/base/common/decorators';
import { IProgressStep } from 'vs/workbench/services/progress/common/progress';
export class ExtHostProgress implements ExtHostProgressShape {
@@ -70,11 +71,14 @@ export class ExtHostProgress implements ExtHostProgressShape {
function mergeProgress(result: IProgressStep, currentValue: IProgressStep): IProgressStep {
result.message = currentValue.message;
if (typeof currentValue.increment === 'number' && typeof result.message === 'number') {
result.increment += currentValue.increment;
} else if (typeof currentValue.increment === 'number') {
result.increment = currentValue.increment;
if (typeof currentValue.increment === 'number') {
if (typeof result.increment === 'number') {
result.increment += currentValue.increment;
} else {
result.increment = currentValue.increment;
}
}
return result;
}

View File

@@ -4,13 +4,18 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { wireCancellationToken, asWinJsPromise } from 'vs/base/common/async';
import { asThenable } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { QuickPickOptions, QuickPickItem, InputBoxOptions, WorkspaceFolderPickOptions, WorkspaceFolder } from 'vscode';
import { MainContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, IMainContext } from './extHost.protocol';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
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';
import { ExtHostQuickOpenShape, IMainContext, MainContext, MainThreadQuickOpenShape, TransferQuickPickItems, TransferQuickInput, TransferQuickInputButton } from './extHost.protocol';
import { URI } from 'vs/base/common/uri';
import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/node/extHostTypes';
import { isPromiseCanceledError } from 'vs/base/common/errors';
export type Item = string | QuickPickItem;
@@ -23,38 +28,40 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
private _onDidSelectItem: (handle: number) => void;
private _validateInput: (input: string) => string | Thenable<string>;
private _sessions = new Map<number, ExtHostQuickInput>();
constructor(mainContext: IMainContext, workspace: ExtHostWorkspace, commands: ExtHostCommands) {
this._proxy = mainContext.getProxy(MainContext.MainThreadQuickOpen);
this._workspace = workspace;
this._commands = commands;
}
showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Thenable<QuickPickItem[] | undefined>;
showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: string[] | Thenable<string[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<string | undefined>;
showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<QuickPickItem | undefined>;
showQuickPick(multiStepHandle: number | undefined, itemsOrItemsPromise: Item[] | Thenable<Item[]>, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable<Item | Item[] | undefined> {
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options: QuickPickOptions & { canPickMany: true; }, token?: CancellationToken): Thenable<QuickPickItem[] | undefined>;
showQuickPick(itemsOrItemsPromise: string[] | Thenable<string[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<string | undefined>;
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<QuickPickItem | undefined>;
showQuickPick(itemsOrItemsPromise: Item[] | Thenable<Item[]>, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable<Item | Item[] | undefined> {
// clear state from last invocation
this._onDidSelectItem = undefined;
const itemsPromise = <TPromise<Item[]>>TPromise.wrap(itemsOrItemsPromise);
const quickPickWidget = this._proxy.$show(multiStepHandle, {
const quickPickWidget = this._proxy.$show({
placeHolder: options && options.placeHolder,
matchOnDescription: options && options.matchOnDescription,
matchOnDetail: options && options.matchOnDetail,
ignoreFocusLost: options && options.ignoreFocusOut,
canPickMany: options && options.canPickMany
});
}, token);
const promise = TPromise.any(<TPromise<number | Item[]>[]>[quickPickWidget, itemsPromise]).then(values => {
return TPromise.any(<TPromise<number | Item[]>[]>[quickPickWidget, itemsPromise]).then(values => {
if (values.key === '0') {
return undefined;
}
return itemsPromise.then(items => {
let pickItems: MyQuickPickItems[] = [];
let pickItems: TransferQuickPickItems[] = [];
for (let handle = 0; handle < items.length; handle++) {
let item = items[handle];
@@ -98,13 +105,16 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
}
return undefined;
});
}, (err) => {
this._proxy.$setError(err);
return TPromise.wrapError(err);
});
}).then(null, err => {
if (isPromiseCanceledError(err)) {
return undefined;
}
this._proxy.$setError(err);
return TPromise.wrapError(err);
});
return wireCancellationToken<Item | Item[]>(token, promise, true);
}
$onItemSelected(handle: number): void {
@@ -115,18 +125,26 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
// ---- input
showInput(multiStepHandle: number | undefined, options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Thenable<string> {
showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Thenable<string> {
// global validate fn used in callback below
this._validateInput = options && options.validateInput;
const promise = this._proxy.$input(multiStepHandle, options, typeof this._validateInput === 'function');
return wireCancellationToken(token, promise, true);
return this._proxy.$input(options, typeof this._validateInput === 'function', token)
.then(null, err => {
if (isPromiseCanceledError(err)) {
return undefined;
}
this._proxy.$setError(err);
return TPromise.wrapError(err);
});
}
$validateInput(input: string): TPromise<string> {
$validateInput(input: string): Thenable<string> {
if (this._validateInput) {
return asWinJsPromise(_ => this._validateInput(input));
return asThenable(() => this._validateInput(input));
}
return undefined;
}
@@ -142,4 +160,443 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
return this._workspace.getWorkspaceFolders().filter(folder => folder.uri.toString() === selectedFolder.uri.toString())[0];
});
}
// ---- QuickInput
createQuickPick<T extends QuickPickItem>(extensionId: string): QuickPick<T> {
const session = new ExtHostQuickPick(this._proxy, extensionId, () => this._sessions.delete(session._id));
this._sessions.set(session._id, session);
return session;
}
createInputBox(extensionId: string): InputBox {
const session = new ExtHostInputBox(this._proxy, extensionId, () => this._sessions.delete(session._id));
this._sessions.set(session._id, session);
return session;
}
$onDidChangeValue(sessionId: number, value: string): void {
const session = this._sessions.get(sessionId);
if (session) {
session._fireDidChangeValue(value);
}
}
$onDidAccept(sessionId: number): void {
const session = this._sessions.get(sessionId);
if (session) {
session._fireDidAccept();
}
}
$onDidChangeActive(sessionId: number, handles: number[]): void {
const session = this._sessions.get(sessionId);
if (session instanceof ExtHostQuickPick) {
session._fireDidChangeActive(handles);
}
}
$onDidChangeSelection(sessionId: number, handles: number[]): void {
const session = this._sessions.get(sessionId);
if (session instanceof ExtHostQuickPick) {
session._fireDidChangeSelection(handles);
}
}
$onDidTriggerButton(sessionId: number, handle: number): void {
const session = this._sessions.get(sessionId);
if (session) {
session._fireDidTriggerButton(handle);
}
}
$onDidHide(sessionId: number): void {
const session = this._sessions.get(sessionId);
if (session) {
session._fireDidHide();
}
}
}
class ExtHostQuickInput implements QuickInput {
private static _nextId = 1;
_id = ExtHostQuickPick._nextId++;
private _title: string;
private _steps: number;
private _totalSteps: number;
private _visible = false;
private _enabled = true;
private _busy = false;
private _ignoreFocusOut = true;
private _value = '';
private _placeholder: string;
private _buttons: QuickInputButton[] = [];
private _handlesToButtons = new Map<number, QuickInputButton>();
private _onDidAcceptEmitter = new Emitter<void>();
private _onDidChangeValueEmitter = new Emitter<string>();
private _onDidTriggerButtonEmitter = new Emitter<QuickInputButton>();
private _onDidHideEmitter = new Emitter<void>();
private _updateTimeout: number;
private _pendingUpdate: TransferQuickInput = { id: this._id };
private _disposed = false;
protected _disposables: IDisposable[] = [
this._onDidTriggerButtonEmitter,
this._onDidHideEmitter,
this._onDidAcceptEmitter,
this._onDidChangeValueEmitter
];
constructor(protected _proxy: MainThreadQuickOpenShape, protected _extensionId: string, private _onDidDispose: () => void) {
}
get title() {
return this._title;
}
set title(title: string) {
this._title = title;
this.update({ title });
}
get step() {
return this._steps;
}
set step(step: number) {
this._steps = step;
this.update({ step });
}
get totalSteps() {
return this._totalSteps;
}
set totalSteps(totalSteps: number) {
this._totalSteps = totalSteps;
this.update({ totalSteps });
}
get enabled() {
return this._enabled;
}
set enabled(enabled: boolean) {
this._enabled = enabled;
this.update({ enabled });
}
get busy() {
return this._busy;
}
set busy(busy: boolean) {
this._busy = busy;
this.update({ busy });
}
get ignoreFocusOut() {
return this._ignoreFocusOut;
}
set ignoreFocusOut(ignoreFocusOut: boolean) {
this._ignoreFocusOut = ignoreFocusOut;
this.update({ ignoreFocusOut });
}
get value() {
return this._value;
}
set value(value: string) {
this._value = value;
this.update({ value });
}
get placeholder() {
return this._placeholder;
}
set placeholder(placeholder: string) {
this._placeholder = placeholder;
this.update({ placeholder });
}
onDidChangeValue = this._onDidChangeValueEmitter.event;
onDidAccept = this._onDidAcceptEmitter.event;
get buttons() {
return this._buttons;
}
set buttons(buttons: QuickInputButton[]) {
this._buttons = buttons.slice();
this._handlesToButtons.clear();
buttons.forEach((button, i) => {
const handle = button === QuickInputButtons.Back ? -1 : i;
this._handlesToButtons.set(handle, button);
});
this.update({
buttons: buttons.map<TransferQuickInputButton>((button, i) => ({
iconPath: getIconUris(button.iconPath),
tooltip: button.tooltip,
handle: button === QuickInputButtons.Back ? -1 : i,
}))
});
}
onDidTriggerButton = this._onDidTriggerButtonEmitter.event;
show(): void {
this._visible = true;
this.update({ visible: true });
}
hide(): void {
this._visible = false;
this.update({ visible: false });
}
onDidHide = this._onDidHideEmitter.event;
_fireDidAccept() {
this._onDidAcceptEmitter.fire();
}
_fireDidChangeValue(value) {
this._value = value;
this._onDidChangeValueEmitter.fire(value);
}
_fireDidTriggerButton(handle: number) {
const button = this._handlesToButtons.get(handle);
this._onDidTriggerButtonEmitter.fire(button);
}
_fireDidHide() {
this._onDidHideEmitter.fire();
}
public dispose(): void {
if (this._disposed) {
return;
}
this._disposed = true;
this._fireDidHide();
this._disposables = dispose(this._disposables);
if (this._updateTimeout) {
clearTimeout(this._updateTimeout);
this._updateTimeout = undefined;
}
this._onDidDispose();
this._proxy.$dispose(this._id);
}
protected update(properties: Record<string, any>): void {
if (this._disposed) {
return;
}
for (const key of Object.keys(properties)) {
const value = properties[key];
this._pendingUpdate[key] = value === undefined ? null : value;
}
if ('visible' in this._pendingUpdate) {
if (this._updateTimeout) {
clearTimeout(this._updateTimeout);
this._updateTimeout = undefined;
}
this.dispatchUpdate();
} else if (this._visible && !this._updateTimeout) {
// Defer the update so that multiple changes to setters dont cause a redraw each
this._updateTimeout = setTimeout(() => {
this._updateTimeout = undefined;
this.dispatchUpdate();
}, 0);
}
}
private dispatchUpdate() {
this._proxy.$createOrUpdate(this._pendingUpdate);
this._pendingUpdate = { id: this._id };
}
}
function getIconUris(iconPath: QuickInputButton['iconPath']) {
const light = getLightIconUri(iconPath);
return { dark: getDarkIconUri(iconPath) || light, light };
}
function getLightIconUri(iconPath: QuickInputButton['iconPath']) {
if (iconPath && !(iconPath instanceof ThemeIcon)) {
if (typeof iconPath === 'string'
|| iconPath instanceof URI) {
return getIconUri(iconPath);
}
return getIconUri(iconPath['light']);
}
return undefined;
}
function getDarkIconUri(iconPath: QuickInputButton['iconPath']) {
if (iconPath && !(iconPath instanceof ThemeIcon) && iconPath['dark']) {
return getIconUri(iconPath['dark']);
}
return undefined;
}
function getIconUri(iconPath: string | URI) {
if (iconPath instanceof URI) {
return iconPath;
}
return URI.file(iconPath);
}
class ExtHostQuickPick<T extends QuickPickItem> extends ExtHostQuickInput implements QuickPick<T> {
private _items: T[] = [];
private _handlesToItems = new Map<number, T>();
private _itemsToHandles = new Map<T, number>();
private _canSelectMany = false;
private _matchOnDescription = true;
private _matchOnDetail = true;
private _activeItems: T[] = [];
private _onDidChangeActiveEmitter = new Emitter<T[]>();
private _selectedItems: T[] = [];
private _onDidChangeSelectionEmitter = new Emitter<T[]>();
constructor(proxy: MainThreadQuickOpenShape, extensionId: string, onDispose: () => void) {
super(proxy, extensionId, onDispose);
this._disposables.push(
this._onDidChangeActiveEmitter,
this._onDidChangeSelectionEmitter,
);
this.update({ type: 'quickPick' });
}
get items() {
return this._items;
}
set items(items: T[]) {
this._items = items.slice();
this._handlesToItems.clear();
this._itemsToHandles.clear();
items.forEach((item, i) => {
this._handlesToItems.set(i, item);
this._itemsToHandles.set(item, i);
});
this.update({
items: items.map((item, i) => ({
label: item.label,
description: item.description,
handle: i,
detail: item.detail,
picked: item.picked
}))
});
}
get canSelectMany() {
return this._canSelectMany;
}
set canSelectMany(canSelectMany: boolean) {
this._canSelectMany = canSelectMany;
this.update({ canSelectMany });
}
get matchOnDescription() {
return this._matchOnDescription;
}
set matchOnDescription(matchOnDescription: boolean) {
this._matchOnDescription = matchOnDescription;
this.update({ matchOnDescription });
}
get matchOnDetail() {
return this._matchOnDetail;
}
set matchOnDetail(matchOnDetail: boolean) {
this._matchOnDetail = matchOnDetail;
this.update({ matchOnDetail });
}
get activeItems() {
return this._activeItems;
}
set activeItems(activeItems: T[]) {
this._activeItems = activeItems.filter(item => this._itemsToHandles.has(item));
this.update({ activeItems: this._activeItems.map(item => this._itemsToHandles.get(item)) });
}
onDidChangeActive = this._onDidChangeActiveEmitter.event;
get selectedItems() {
return this._selectedItems;
}
set selectedItems(selectedItems: T[]) {
this._selectedItems = selectedItems.filter(item => this._itemsToHandles.has(item));
this.update({ selectedItems: this._selectedItems.map(item => this._itemsToHandles.get(item)) });
}
onDidChangeSelection = this._onDidChangeSelectionEmitter.event;
_fireDidChangeActive(handles: number[]) {
const items = handles.map(handle => this._handlesToItems.get(handle));
this._activeItems = items;
this._onDidChangeActiveEmitter.fire(items);
}
_fireDidChangeSelection(handles: number[]) {
const items = handles.map(handle => this._handlesToItems.get(handle));
this._selectedItems = items;
this._onDidChangeSelectionEmitter.fire(items);
}
}
class ExtHostInputBox extends ExtHostQuickInput implements InputBox {
private _password: boolean;
private _prompt: string;
private _validationMessage: string;
constructor(proxy: MainThreadQuickOpenShape, extensionId: string, onDispose: () => void) {
super(proxy, extensionId, onDispose);
this.update({ type: 'inputBox' });
}
get password() {
return this._password;
}
set password(password: boolean) {
this._password = password;
this.update({ password });
}
get prompt() {
return this._prompt;
}
set prompt(prompt: string) {
this._prompt = prompt;
this.update({ prompt });
}
get validationMessage() {
return this._validationMessage;
}
set validationMessage(validationMessage: string) {
this._validationMessage = validationMessage;
this.update({ validationMessage });
}
}

View File

@@ -4,12 +4,12 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI, { UriComponents } from 'vs/base/common/uri';
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 { debounce } from 'vs/base/common/decorators';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { asWinJsPromise } from 'vs/base/common/async';
import { asThenable } 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';
@@ -18,6 +18,7 @@ import { comparePaths } from 'vs/base/common/comparers';
import * as vscode from 'vscode';
import { ISplice } from 'vs/base/common/sequence';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
type ProviderHandle = number;
type GroupHandle = number;
@@ -237,14 +238,14 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
return this._resourceStatesMap.get(handle);
}
async $executeResourceCommand(handle: number): TPromise<void> {
$executeResourceCommand(handle: number): Thenable<void> {
const command = this._resourceStatesCommandsMap.get(handle);
if (!command) {
return;
return Promise.resolve(null);
}
await this._commands.executeCommand(command.command, ...command.arguments);
return asThenable(() => this._commands.executeCommand(command.command, ...command.arguments));
}
_takeResourceStateSnapshot(): SCMRawResourceSplice[] {
@@ -395,6 +396,15 @@ class ExtHostSourceControl implements vscode.SourceControl {
this._proxy.$updateSourceControl(this.handle, { statusBarCommands: internal });
}
private _selected: boolean = false;
get selected(): boolean {
return this._selected;
}
private _onDidChangeSelection = new Emitter<boolean>();
readonly onDidChangeSelection = this._onDidChangeSelection.event;
private handle: number = ExtHostSourceControl._handlePool++;
constructor(
@@ -454,6 +464,11 @@ class ExtHostSourceControl implements vscode.SourceControl {
return this._groups.get(handle);
}
setSelectionState(selected: boolean): void {
this._selected = selected;
this._onDidChangeSelection.fire(selected);
}
dispose(): void {
this._groups.forEach(group => group.dispose());
this._proxy.$unregisterSourceControl(this.handle);
@@ -471,6 +486,8 @@ export class ExtHostSCM implements ExtHostSCMShape {
private _onDidChangeActiveProvider = new Emitter<vscode.SourceControl>();
get onDidChangeActiveProvider(): Event<vscode.SourceControl> { return this._onDidChangeActiveProvider.event; }
private _selectedSourceControlHandles = new Set<number>();
constructor(
mainContext: IMainContext,
private _commands: ExtHostCommands,
@@ -542,7 +559,7 @@ export class ExtHostSCM implements ExtHostSCMShape {
return inputBox;
}
$provideOriginalResource(sourceControlHandle: number, uriComponents: UriComponents): TPromise<UriComponents> {
$provideOriginalResource(sourceControlHandle: number, uriComponents: UriComponents, token: CancellationToken): Thenable<UriComponents> {
const uri = URI.revive(uriComponents);
this.logService.trace('ExtHostSCM#$provideOriginalResource', sourceControlHandle, uri.toString());
@@ -552,7 +569,7 @@ export class ExtHostSCM implements ExtHostSCMShape {
return TPromise.as(null);
}
return asWinJsPromise(token => sourceControl.quickDiffProvider.provideOriginalResource(uri, token));
return asThenable(() => sourceControl.quickDiffProvider.provideOriginalResource(uri, token));
}
$onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise<void> {
@@ -568,25 +585,25 @@ export class ExtHostSCM implements ExtHostSCMShape {
return TPromise.as(null);
}
async $executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): TPromise<void> {
$executeResourceCommand(sourceControlHandle: number, groupHandle: number, handle: number): Thenable<void> {
this.logService.trace('ExtHostSCM#$executeResourceCommand', sourceControlHandle, groupHandle, handle);
const sourceControl = this._sourceControls.get(sourceControlHandle);
if (!sourceControl) {
return;
return TPromise.as(null);
}
const group = sourceControl.getResourceGroup(groupHandle);
if (!group) {
return;
return TPromise.as(null);
}
await group.$executeResourceCommand(handle);
return group.$executeResourceCommand(handle);
}
async $validateInput(sourceControlHandle: number, value: string, cursorPosition: number): TPromise<[string, number] | undefined> {
$validateInput(sourceControlHandle: number, value: string, cursorPosition: number): Thenable<[string, number] | undefined> {
this.logService.trace('ExtHostSCM#$validateInput', sourceControlHandle);
const sourceControl = this._sourceControls.get(sourceControlHandle);
@@ -599,12 +616,49 @@ export class ExtHostSCM implements ExtHostSCMShape {
return TPromise.as(undefined);
}
const result = await sourceControl.inputBox.validateInput(value, cursorPosition);
return asThenable(() => sourceControl.inputBox.validateInput(value, cursorPosition)).then(result => {
if (!result) {
return TPromise.as(undefined);
}
if (!result) {
return TPromise.as(undefined);
return TPromise.as<[string, number]>([result.message, result.type]);
});
}
$setSelectedSourceControls(selectedSourceControlHandles: number[]): Thenable<void> {
this.logService.trace('ExtHostSCM#$setSelectedSourceControls', selectedSourceControlHandles);
const set = new Set<number>();
for (const handle of selectedSourceControlHandles) {
set.add(handle);
}
return [result.message, result.type];
set.forEach(handle => {
if (!this._selectedSourceControlHandles.has(handle)) {
const sourceControl = this._sourceControls.get(handle);
if (!sourceControl) {
return;
}
sourceControl.setSelectionState(true);
}
});
this._selectedSourceControlHandles.forEach(handle => {
if (!set.has(handle)) {
const sourceControl = this._sourceControls.get(handle);
if (!sourceControl) {
return;
}
sourceControl.setSelectionState(false);
}
});
this._selectedSourceControlHandles = set;
return TPromise.as(null);
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { MainContext, MainThreadStorageShape, IMainContext } from './extHost.protocol';
export class ExtHostStorage {
@@ -15,11 +14,11 @@ export class ExtHostStorage {
this._proxy = mainContext.getProxy(MainContext.MainThreadStorage);
}
getValue<T>(shared: boolean, key: string, defaultValue?: T): TPromise<T> {
getValue<T>(shared: boolean, key: string, defaultValue?: T): Thenable<T> {
return this._proxy.$getValue<T>(shared, key).then(value => value || defaultValue);
}
setValue(shared: boolean, key: string, value: any): TPromise<void> {
setValue(shared: boolean, key: string, value: any): Thenable<void> {
return this._proxy.$setValue(shared, key, value);
}
}

View File

@@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI, { UriComponents } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import * as Objects from 'vs/base/common/objects';
import { asWinJsPromise } from 'vs/base/common/async';
import { asThenable } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
@@ -27,6 +27,7 @@ import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDeb
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
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 {
@@ -803,7 +804,7 @@ export class ExtHostTask implements ExtHostTaskShape {
return result;
}
public terminateTask(execution: vscode.TaskExecution): TPromise<void> {
public terminateTask(execution: vscode.TaskExecution): Thenable<void> {
if (!(execution instanceof TaskExecutionImpl)) {
throw new Error('No valid task execution provided');
}
@@ -860,22 +861,32 @@ export class ExtHostTask implements ExtHostTaskShape {
}
}
public $provideTasks(handle: number): TPromise<tasks.TaskSet> {
public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable<tasks.TaskSet> {
let handler = this._handlers.get(handle);
if (!handler) {
return TPromise.wrapError<tasks.TaskSet>(new Error('no handler found'));
}
return asWinJsPromise(token => handler.provider.provideTasks(token)).then(value => {
return asThenable(() => handler.provider.provideTasks(CancellationToken.None)).then(value => {
let sanitized: vscode.Task[] = [];
for (let task of value) {
if (task.definition && validTypes[task.definition.type] === true) {
sanitized.push(task);
} else {
sanitized.push(task);
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
}
}
let workspaceFolders = this._workspaceService.getWorkspaceFolders();
return {
tasks: Tasks.from(value, workspaceFolders && workspaceFolders.length > 0 ? workspaceFolders[0] : undefined, handler.extension),
tasks: Tasks.from(sanitized, workspaceFolders && workspaceFolders.length > 0 ? workspaceFolders[0] : undefined, handler.extension),
extension: handler.extension
};
});
}
public $resolveVariables(uri: URI, variables: string[]): any {
let result = Object.create(null);
public $resolveVariables(uriComponents: UriComponents, variables: string[]): any {
let uri: URI = URI.revive(uriComponents);
let result: { [key: string]: string; } = Object.create(null);
let workspaceFolder = this._workspaceService.resolveWorkspaceFolder(uri);
let resolver = new ExtHostVariableResolverService(this._workspaceService, this._editorService, this._configurationService);
let ws: IWorkspaceFolder = {
@@ -887,7 +898,7 @@ export class ExtHostTask implements ExtHostTaskShape {
}
};
for (let variable of variables) {
result.push(variable, resolver.resolve(ws, variable));
result[variable] = resolver.resolve(ws, variable);
}
return result;
}

View File

@@ -5,28 +5,77 @@
'use strict';
import * as vscode from 'vscode';
import * as cp from 'child_process';
import * as os from 'os';
import * as platform from 'vs/base/common/platform';
import * as terminalEnvironment from 'vs/workbench/parts/terminal/node/terminalEnvironment';
import Uri from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto } from 'vs/workbench/api/node/extHost.protocol';
import { IMessageFromTerminalProcess } from 'vs/workbench/parts/terminal/node/terminal';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ILogService } from 'vs/platform/log/common/log';
import { EXT_HOST_CREATION_DELAY } from 'vs/workbench/parts/terminal/common/terminal';
import { TerminalProcess } from 'vs/workbench/parts/terminal/node/terminalProcess';
export class ExtHostTerminal implements vscode.Terminal {
private _name: string;
private _id: number;
private _proxy: MainThreadTerminalServiceShape;
private _disposed: boolean;
private _queuedRequests: ApiRequest[];
const RENDERER_NO_PROCESS_ID = -1;
export class BaseExtHostTerminal {
public _id: number;
protected _idPromise: Promise<number>;
private _idPromiseComplete: (value: number) => any;
private _disposed: boolean = false;
private _queuedRequests: ApiRequest[] = [];
constructor(
protected _proxy: MainThreadTerminalServiceShape,
id?: number
) {
this._idPromise = new Promise<number>(c => {
if (id !== undefined) {
this._id = id;
c(id);
} else {
this._idPromiseComplete = c;
}
});
}
public dispose(): void {
if (!this._disposed) {
this._disposed = true;
this._queueApiRequest(this._proxy.$dispose, []);
}
}
protected _checkDisposed() {
if (this._disposed) {
throw new Error('Terminal has already been disposed');
}
}
protected _queueApiRequest(callback: (...args: any[]) => void, args: any[]): void {
const request: ApiRequest = new ApiRequest(callback, args);
if (!this._id) {
this._queuedRequests.push(request);
return;
}
request.run(this._proxy, this._id);
}
public _runQueuedRequests(id: number): void {
this._id = id;
this._idPromiseComplete(id);
this._queuedRequests.forEach((r) => {
r.run(this._proxy, this._id);
});
this._queuedRequests.length = 0;
}
}
export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Terminal {
private _pidPromise: Promise<number>;
private _pidPromiseComplete: (value: number) => any;
private readonly _onData: Emitter<string> = new Emitter<string>();
public get onData(): Event<string> {
public get onDidWriteData(): Event<string> {
// Tell the main side to start sending data if it's not already
this._proxy.$registerOnDataListener(this._id);
return this._onData && this._onData.event;
@@ -34,17 +83,17 @@ export class ExtHostTerminal implements vscode.Terminal {
constructor(
proxy: MainThreadTerminalServiceShape,
name: string = '',
id?: number
private _name: string,
id?: number,
pid?: number
) {
this._proxy = proxy;
this._name = name;
if (id) {
this._id = id;
}
this._queuedRequests = [];
super(proxy, id);
this._pidPromise = new Promise<number>(c => {
this._pidPromiseComplete = c;
if (pid === RENDERER_NO_PROCESS_ID) {
c(undefined);
} else {
this._pidPromiseComplete = c;
}
});
}
@@ -56,11 +105,7 @@ export class ExtHostTerminal implements vscode.Terminal {
waitOnExit?: boolean
): void {
this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit).then((id) => {
this._id = id;
this._queuedRequests.forEach((r) => {
r.run(this._proxy, this._id);
});
this._queuedRequests = [];
this._runQueuedRequests(id);
});
}
@@ -87,13 +132,6 @@ export class ExtHostTerminal implements vscode.Terminal {
this._queueApiRequest(this._proxy.$hide, []);
}
public dispose(): void {
if (!this._disposed) {
this._disposed = true;
this._queueApiRequest(this._proxy.$dispose, []);
}
}
public _setProcessId(processId: number): void {
// The event may fire 2 times when the panel is restored
if (this._pidPromiseComplete) {
@@ -105,34 +143,99 @@ export class ExtHostTerminal implements vscode.Terminal {
public _fireOnData(data: string): void {
this._onData.fire(data);
}
}
private _queueApiRequest(callback: (...args: any[]) => void, args: any[]) {
let request: ApiRequest = new ApiRequest(callback, args);
if (!this._id) {
this._queuedRequests.push(request);
return;
}
request.run(this._proxy, this._id);
export class ExtHostTerminalRenderer extends BaseExtHostTerminal implements vscode.TerminalRenderer {
public get name(): string { return this._name; }
public set name(newName: string) {
this._name = newName;
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererSetName, [this._name]);
}
private _checkDisposed() {
if (this._disposed) {
throw new Error('Terminal has already been disposed');
private readonly _onInput: Emitter<string> = new Emitter<string>();
public get onDidAcceptInput(): Event<string> {
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererRegisterOnInputListener, [this._id]);
// Tell the main side to start sending data if it's not already
// this._proxy.$terminalRendererRegisterOnDataListener(this._id);
return this._onInput && this._onInput.event;
}
private _dimensions: vscode.TerminalDimensions | undefined;
public get dimensions(): vscode.TerminalDimensions { return this._dimensions; }
public set dimensions(dimensions: vscode.TerminalDimensions) {
this._checkDisposed();
this._dimensions = dimensions;
this._queueApiRequest(this._proxy.$terminalRendererSetDimensions, [dimensions]);
}
private _maximumDimensions: vscode.TerminalDimensions;
public get maximumDimensions(): vscode.TerminalDimensions {
if (!this._maximumDimensions) {
return undefined;
}
return {
rows: this._maximumDimensions.rows,
columns: this._maximumDimensions.columns
};
}
private readonly _onDidChangeMaximumDimensions: Emitter<vscode.TerminalDimensions> = new Emitter<vscode.TerminalDimensions>();
public get onDidChangeMaximumDimensions(): Event<vscode.TerminalDimensions> {
return this._onDidChangeMaximumDimensions && this._onDidChangeMaximumDimensions.event;
}
public get terminal(): ExtHostTerminal {
return this._terminal;
}
constructor(
proxy: MainThreadTerminalServiceShape,
private _name: string,
private _terminal: ExtHostTerminal
) {
super(proxy);
this._proxy.$createTerminalRenderer(this._name).then(id => {
this._runQueuedRequests(id);
(<any>this._terminal)._runQueuedRequests(id);
});
}
public write(data: string): void {
this._checkDisposed();
this._queueApiRequest(this._proxy.$terminalRendererWrite, [data]);
}
public _fireOnInput(data: string): void {
this._onInput.fire(data);
}
public _setMaximumDimensions(columns: number, rows: number): void {
if (this._maximumDimensions && this._maximumDimensions.columns === columns && this._maximumDimensions.rows === rows) {
return;
}
this._maximumDimensions = { columns, rows };
this._onDidChangeMaximumDimensions.fire(this.maximumDimensions);
}
}
export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
private _proxy: MainThreadTerminalServiceShape;
private _activeTerminal: ExtHostTerminal;
private _terminals: ExtHostTerminal[] = [];
private _terminalProcesses: { [id: number]: cp.ChildProcess } = {};
private _terminalProcesses: { [id: number]: TerminalProcess } = {};
private _terminalRenderers: ExtHostTerminalRenderer[] = [];
public get activeTerminal(): ExtHostTerminal { return this._activeTerminal; }
public get terminals(): ExtHostTerminal[] { return this._terminals; }
private readonly _onDidCloseTerminal: Emitter<vscode.Terminal> = new Emitter<vscode.Terminal>();
public get onDidCloseTerminal(): Event<vscode.Terminal> { return this._onDidCloseTerminal && this._onDidCloseTerminal.event; }
private readonly _onDidOpenTerminal: Emitter<vscode.Terminal> = new Emitter<vscode.Terminal>();
public get onDidOpenTerminal(): Event<vscode.Terminal> { return this._onDidOpenTerminal && this._onDidOpenTerminal.event; }
private readonly _onDidChangeActiveTerminal: Emitter<vscode.Terminal | undefined> = new Emitter<vscode.Terminal | undefined>();
public get onDidChangeActiveTerminal(): Event<vscode.Terminal | undefined> { return this._onDidChangeActiveTerminal && this._onDidChangeActiveTerminal.event; }
constructor(
mainContext: IMainContext,
@@ -143,53 +246,108 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
let terminal = new ExtHostTerminal(this._proxy, name);
const terminal = new ExtHostTerminal(this._proxy, name);
terminal.create(shellPath, shellArgs);
this._terminals.push(terminal);
return terminal;
}
public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal {
let terminal = new ExtHostTerminal(this._proxy, options.name);
const terminal = new ExtHostTerminal(this._proxy, options.name);
terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env /*, options.waitOnExit*/);
this._terminals.push(terminal);
return terminal;
}
public $acceptTerminalProcessData(id: number, data: string): void {
let index = this._getTerminalIndexById(id);
if (index === null) {
return;
public createTerminalRenderer(name: string): vscode.TerminalRenderer {
const terminal = new ExtHostTerminal(this._proxy, name);
terminal._setProcessId(undefined);
this._terminals.push(terminal);
const renderer = new ExtHostTerminalRenderer(this._proxy, name, terminal);
this._terminalRenderers.push(renderer);
return renderer;
}
public $acceptActiveTerminalChanged(id: number | null): void {
const original = this._activeTerminal;
if (id === null) {
this._activeTerminal = undefined;
if (original !== this._activeTerminal) {
this._onDidChangeActiveTerminal.fire(this._activeTerminal);
}
}
this._performTerminalIdAction(id, terminal => {
if (terminal) {
this._activeTerminal = terminal;
if (original !== this._activeTerminal) {
this._onDidChangeActiveTerminal.fire(this._activeTerminal);
}
}
});
}
public $acceptTerminalProcessData(id: number, data: string): void {
// TODO: Queue requests, currently the first 100ms of data may get missed
const terminal = this._getTerminalById(id);
if (terminal) {
terminal._fireOnData(data);
}
}
public $acceptTerminalRendererDimensions(id: number, cols: number, rows: number): void {
const renderer = this._getTerminalRendererById(id);
if (renderer) {
renderer._setMaximumDimensions(cols, rows);
}
}
public $acceptTerminalRendererInput(id: number, data: string): void {
const renderer = this._getTerminalRendererById(id);
if (renderer) {
renderer._fireOnInput(data);
}
const terminal = this._terminals[index];
terminal._fireOnData(data);
}
public $acceptTerminalClosed(id: number): void {
let index = this._getTerminalIndexById(id);
const index = this._getTerminalObjectIndexById(this.terminals, id);
if (index === null) {
return;
}
let terminal = this._terminals.splice(index, 1)[0];
const terminal = this._terminals.splice(index, 1)[0];
this._onDidCloseTerminal.fire(terminal);
}
public $acceptTerminalOpened(id: number, name: string): void {
let index = this._getTerminalIndexById(id);
const index = this._getTerminalObjectIndexById(this._terminals, id);
if (index !== null) {
// The terminal has already been created (via createTerminal*), only fire the event
this._onDidOpenTerminal.fire(this.terminals[index]);
return;
}
let terminal = new ExtHostTerminal(this._proxy, name, id);
const renderer = this._getTerminalRendererById(id);
const terminal = new ExtHostTerminal(this._proxy, name, id, renderer ? RENDERER_NO_PROCESS_ID : undefined);
this._terminals.push(terminal);
this._onDidOpenTerminal.fire(terminal);
}
public $acceptTerminalProcessId(id: number, processId: number): void {
this._performTerminalIdAction(id, terminal => terminal._setProcessId(processId));
}
private _performTerminalIdAction(id: number, callback: (terminal: ExtHostTerminal) => void): void {
let terminal = this._getTerminalById(id);
if (terminal) {
terminal._setProcessId(processId);
callback(terminal);
} else {
// Retry one more time in case the terminal has not yet been initialized.
setTimeout(() => {
terminal = this._getTerminalById(id);
if (terminal) {
callback(terminal);
}
}, EXT_HOST_CREATION_DELAY);
}
}
@@ -199,7 +357,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
const terminalConfig = this._extHostConfiguration.getConfiguration('terminal.integrated');
const locale = terminalConfig.get('setLocaleVariables') ? platform.locale : undefined;
if (!shellLaunchConfig.executable) {
// TODO: This duplicates some of TerminalConfigHelper.mergeDefaultShellPathAndArgs and should be merged
// this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
@@ -213,7 +370,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
}
// TODO: Base the cwd on the last active workspace root
// const lastActiveWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot('file');
// const lastActiveWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
// this.initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, lastActiveWorkspaceRootUri, this._configHelper);
const initialCwd = os.homedir();
@@ -223,61 +380,48 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
// const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
// const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot);
// const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
// shellLaunchConfig.env = envFromShell;
// Merge process env with the env from config
const parentEnv = { ...process.env };
// terminalEnvironment.mergeEnvironments(parentEnv, envFromConfig);
const env = { ...process.env };
// terminalEnvironment.mergeEnvironments(env, envFromConfig);
terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env);
// Continue env initialization, merging in the env from the launch
// config and adding keys that are needed to create the process
const env = terminalEnvironment.createTerminalEnv(parentEnv, shellLaunchConfig, initialCwd, locale, cols, rows);
let cwd = Uri.parse(require.toUrl('../../parts/terminal/node')).fsPath;
const options = { env, cwd, execArgv: [] };
const locale = terminalConfig.get('setLocaleVariables') ? platform.locale : undefined;
terminalEnvironment.addTerminalEnvironmentKeys(env, locale);
// Fork the process and listen for messages
this._logService.debug(`Terminal process launching on ext host`, options);
this._terminalProcesses[id] = cp.fork(Uri.parse(require.toUrl('bootstrap')).fsPath, ['--type=terminal'], options);
this._terminalProcesses[id].on('message', (message: IMessageFromTerminalProcess) => {
switch (message.type) {
case 'pid': this._proxy.$sendProcessPid(id, <number>message.content); break;
case 'title': this._proxy.$sendProcessTitle(id, <string>message.content); break;
case 'data': this._proxy.$sendProcessData(id, <string>message.content); break;
}
});
this._terminalProcesses[id].on('exit', (exitCode) => this._onProcessExit(id, exitCode));
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env);
this._terminalProcesses[id] = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env);
this._terminalProcesses[id].onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid));
this._terminalProcesses[id].onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title));
this._terminalProcesses[id].onProcessData(data => this._proxy.$sendProcessData(id, data));
this._terminalProcesses[id].onProcessExit((exitCode) => this._onProcessExit(id, exitCode));
}
public $acceptProcessInput(id: number, data: string): void {
if (this._terminalProcesses[id].connected) {
this._terminalProcesses[id].send({ event: 'input', data });
}
this._terminalProcesses[id].input(data);
}
public $acceptProcessResize(id: number, cols: number, rows: number): void {
if (this._terminalProcesses[id].connected) {
try {
this._terminalProcesses[id].send({ event: 'resize', cols, rows });
} catch (error) {
// We tried to write to a closed pipe / channel.
if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') {
throw (error);
}
try {
this._terminalProcesses[id].resize(cols, rows);
} catch (error) {
// We tried to write to a closed pipe / channel.
if (error.code !== 'EPIPE' && error.code !== 'ERR_IPC_CHANNEL_CLOSED') {
throw (error);
}
}
}
public $acceptProcessShutdown(id: number): void {
if (this._terminalProcesses[id].connected) {
this._terminalProcesses[id].send({ event: 'shutdown' });
}
public $acceptProcessShutdown(id: number, immediate: boolean): void {
this._terminalProcesses[id].shutdown(immediate);
}
private _onProcessExit(id: number, exitCode: number): void {
// Remove listeners
const process = this._terminalProcesses[id];
process.removeAllListeners('message');
process.removeAllListeners('exit');
this._terminalProcesses[id].dispose();
// Remove process reference
delete this._terminalProcesses[id];
@@ -286,16 +430,43 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
this._proxy.$sendProcessExit(id, exitCode);
}
private _getTerminalById(id: number): ExtHostTerminal {
let index = this._getTerminalIndexById(id);
return index !== null ? this._terminals[index] : null;
private _getTerminalByIdEventually(id: number, retries: number = 5): Promise<ExtHostTerminal> {
return new Promise(c => {
if (retries === 0) {
c(undefined);
return;
}
const terminal = this._getTerminalById(id);
if (terminal) {
c(terminal);
} else {
// This should only be needed immediately after createTerminalRenderer is called as
// the ExtHostTerminal has not yet been iniitalized
setTimeout(() => {
c(this._getTerminalByIdEventually(id, retries - 1));
}, 200);
}
});
}
private _getTerminalIndexById(id: number): number {
private _getTerminalById(id: number): ExtHostTerminal {
return this._getTerminalObjectById(this._terminals, id);
}
private _getTerminalRendererById(id: number): ExtHostTerminalRenderer {
return this._getTerminalObjectById(this._terminalRenderers, id);
}
private _getTerminalObjectById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): T {
const index = this._getTerminalObjectIndexById(array, id);
return index !== null ? array[index] : null;
}
private _getTerminalObjectIndexById<T extends ExtHostTerminal | ExtHostTerminalRenderer>(array: T[], id: number): number {
let index: number = null;
this._terminals.some((terminal, i) => {
// TODO: This shouldn't be cas
let thisId = (<any>terminal)._id;
array.some((item, i) => {
const thisId = item._id;
if (thisId === id) {
index = i;
return true;

View File

@@ -322,6 +322,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
private _visibleRanges: Range[];
private _viewColumn: vscode.ViewColumn;
private _disposed: boolean = false;
private _hasDecorationsForKey: { [key: string]: boolean; };
get id(): string { return this._id; }
@@ -337,6 +338,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
this._options = new ExtHostTextEditorOptions(this._proxy, this._id, options);
this._visibleRanges = visibleRanges;
this._viewColumn = viewColumn;
this._hasDecorationsForKey = Object.create(null);
}
dispose() {
@@ -436,6 +438,16 @@ export class ExtHostTextEditor implements vscode.TextEditor {
}
setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void {
const willBeEmpty = (ranges.length === 0);
if (willBeEmpty && !this._hasDecorationsForKey[decorationType.key]) {
// avoid no-op call to the renderer
return;
}
if (willBeEmpty) {
delete this._hasDecorationsForKey[decorationType.key];
} else {
this._hasDecorationsForKey[decorationType.key] = true;
}
this._runOnProxy(
() => {
if (TypeConverters.isDecorationOptionsArr(ranges)) {
@@ -473,7 +485,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
);
}
private _trySetSelection(): TPromise<vscode.TextEditor> {
private _trySetSelection(): Thenable<vscode.TextEditor> {
let selection = this._selections.map(TypeConverters.Selection.from);
return this._runOnProxy(() => this._proxy.$trySetSelections(this._id, selection));
}
@@ -494,9 +506,14 @@ export class ExtHostTextEditor implements vscode.TextEditor {
return this._applyEdit(edit);
}
private _applyEdit(editBuilder: TextEditorEdit): TPromise<boolean> {
private _applyEdit(editBuilder: TextEditorEdit): Thenable<boolean> {
let editData = editBuilder.finalize();
// return when there is nothing to do
if (editData.edits.length === 0 && !editData.setEndOfLine) {
return TPromise.wrap(true);
}
// check that the edits are not overlapping (i.e. illegal)
let editRanges = editData.edits.map(edit => edit.range);
@@ -575,7 +592,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
// ---- util
private _runOnProxy(callback: () => TPromise<any>): TPromise<ExtHostTextEditor> {
private _runOnProxy(callback: () => Thenable<any>): Thenable<ExtHostTextEditor> {
if (this._disposed) {
console.warn('TextEditor is closed/disposed');
return TPromise.as(undefined);
@@ -589,7 +606,7 @@ export class ExtHostTextEditor implements vscode.TextEditor {
}
}
function warnOnError(promise: TPromise<any>): void {
function warnOnError(promise: Thenable<any>): void {
promise.then(null, (err) => {
console.warn(err);
});

View File

@@ -6,12 +6,11 @@
import { Event, Emitter } from 'vs/base/common/event';
import { toThenable } from 'vs/base/common/async';
import { TPromise } from 'vs/base/common/winjs.base';
import { TextEditorSelectionChangeKind } from './extHostTypes';
import * as TypeConverters from './extHostTypeConverters';
import { TextEditorDecorationType, ExtHostTextEditor } from './extHostTextEditor';
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
import { MainContext, MainThreadTextEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IMainContext, WorkspaceEditDto, IEditorPropertiesChangeData } from './extHost.protocol';
import { MainContext, MainThreadTextEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IMainContext, IEditorPropertiesChangeData } from './extHost.protocol';
import * as vscode from 'vscode';
export class ExtHostEditors implements ExtHostEditorsShape {
@@ -53,10 +52,10 @@ export class ExtHostEditors implements ExtHostEditorsShape {
return this._extHostDocumentsAndEditors.allEditors();
}
showTextDocument(document: vscode.TextDocument, column: vscode.ViewColumn, preserveFocus: boolean): TPromise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, options: { column: vscode.ViewColumn, preserveFocus: boolean, pinned: boolean }): TPromise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): TPromise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): TPromise<vscode.TextEditor> {
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> {
let options: ITextDocumentShowOptions;
if (typeof columnOrOptions === 'number') {
options = {
@@ -90,24 +89,8 @@ export class ExtHostEditors implements ExtHostEditorsShape {
return new TextEditorDecorationType(this._proxy, options);
}
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
const dto: WorkspaceEditDto = { edits: [] };
for (let entry of edit.entries()) {
let [uri, uriOrEdits] = entry;
if (Array.isArray(uriOrEdits)) {
let doc = this._extHostDocumentsAndEditors.getDocument(uri.toString());
dto.edits.push({
resource: uri,
modelVersionId: doc && doc.version,
edits: uriOrEdits.map(TypeConverters.TextEdit.from)
});
// } else {
// dto.edits.push({ oldUri: uri, newUri: uriOrEdits });
}
}
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): Thenable<boolean> {
const dto = TypeConverters.WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
return this._proxy.$tryApplyWorkspaceEdit(dto);
}

View File

@@ -7,16 +7,18 @@
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 { URI } from 'vs/base/common/uri';
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol';
import { ITreeItem, TreeViewItemHandleArg } from 'vs/workbench/common/views';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
import { asWinJsPromise } from 'vs/base/common/async';
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 { ILogService } from 'vs/platform/log/common/log';
type TreeItemHandle = string;
@@ -26,7 +28,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
constructor(
private _proxy: MainThreadTreeViewsShape,
private commands: ExtHostCommands
private commands: ExtHostCommands,
private logService: ILogService
) {
commands.registerArgumentProcessor({
processArgument: arg => {
@@ -52,7 +55,10 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
get onDidCollapseElement() { return treeView.onDidCollapseElement; },
get onDidExpandElement() { return treeView.onDidExpandElement; },
get selection() { return treeView.selectedElements; },
reveal: (element: T, options?: { select?: boolean }): Thenable<void> => {
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> => {
return treeView.reveal(element, options);
},
dispose: () => {
@@ -62,7 +68,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
};
}
$getChildren(treeViewId: string, treeItemHandle?: string): TPromise<ITreeItem[]> {
$getChildren(treeViewId: string, treeItemHandle?: string): Thenable<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)));
@@ -86,8 +92,16 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
treeView.setSelection(treeItemHandles);
}
$setVisible(treeViewId: string, isVisible: boolean): void {
const treeView = this.treeViews.get(treeViewId);
if (!treeView) {
throw new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId));
}
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);
const treeView = new ExtHostTreeView<T>(id, dataProvider, this._proxy, this.commands.converter, this.logService);
this.treeViews.set(id, treeView);
return treeView;
}
@@ -113,8 +127,11 @@ class ExtHostTreeView<T> extends Disposable {
private elements: Map<TreeItemHandle, T> = new Map<TreeItemHandle, T>();
private nodes: Map<T, TreeNode> = new Map<T, TreeNode>();
private _selectedElements: T[] = [];
get selectedElements(): T[] { return this._selectedElements; }
private _visible: boolean = false;
get visible(): boolean { return this._visible; }
private _selectedHandles: TreeItemHandle[] = [];
get selectedElements(): T[] { return this._selectedHandles.map(handle => this.getExtensionElement(handle)).filter(element => !isUndefinedOrNull(element)); }
private _onDidExpandElement: Emitter<vscode.TreeViewExpansionEvent<T>> = this._register(new Emitter<vscode.TreeViewExpansionEvent<T>>());
readonly onDidExpandElement: Event<vscode.TreeViewExpansionEvent<T>> = this._onDidExpandElement.event;
@@ -122,9 +139,15 @@ class ExtHostTreeView<T> extends Disposable {
private _onDidCollapseElement: Emitter<vscode.TreeViewExpansionEvent<T>> = this._register(new Emitter<vscode.TreeViewExpansionEvent<T>>());
readonly onDidCollapseElement: Event<vscode.TreeViewExpansionEvent<T>> = this._onDidCollapseElement.event;
private _onDidChangeSelection: Emitter<vscode.TreeViewSelectionChangeEvent<T>> = this._register(new Emitter<vscode.TreeViewSelectionChangeEvent<T>>());
readonly onDidChangeSelection: Event<vscode.TreeViewSelectionChangeEvent<T>> = this._onDidChangeSelection.event;
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);
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter) {
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter, private logService: ILogService) {
super();
this.proxy.$registerTreeViewDataProvider(viewId);
if (this.dataProvider.onDidChangeTreeData) {
@@ -144,7 +167,7 @@ class ExtHostTreeView<T> extends Disposable {
}
}
getChildren(parentHandle?: TreeItemHandle): TPromise<ITreeItem[]> {
getChildren(parentHandle?: TreeItemHandle): Thenable<ITreeItem[]> {
const parentElement = parentHandle ? this.getExtensionElement(parentHandle) : void 0;
if (parentHandle && !parentElement) {
console.error(`No tree item with id \'${parentHandle}\' found.`);
@@ -160,32 +183,46 @@ class ExtHostTreeView<T> extends Disposable {
return this.elements.get(treeItemHandle);
}
reveal(element: T, options?: { select?: boolean }): TPromise<void> {
reveal(element: T, options?: { select?: boolean, focus?: boolean }): TPromise<void> {
options = options ? options : { select: true, focus: false };
const select = isUndefinedOrNull(options.select) ? true : options.select;
const focus = isUndefinedOrNull(options.focus) ? false : options.focus;
if (typeof this.dataProvider.getParent !== 'function') {
return TPromise.wrapError(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), options)));
.then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), { select, focus })), error => this.logService.error(error));
}
setExpanded(treeItemHandle: TreeItemHandle, expanded: boolean): void {
const element = this.getExtensionElement(treeItemHandle);
if (element) {
if (expanded) {
this._onDidExpandElement.fire({ element });
this._onDidExpandElement.fire(Object.freeze({ element }));
} else {
this._onDidCollapseElement.fire({ element });
this._onDidCollapseElement.fire(Object.freeze({ element }));
}
}
}
setSelection(treeItemHandles: TreeItemHandle[]): void {
this._selectedElements = treeItemHandles.map(handle => this.getExtensionElement(handle)).filter(element => !isUndefinedOrNull(element));
if (!equals(this._selectedHandles, treeItemHandles)) {
this._selectedHandles = treeItemHandles;
this._onDidChangeSelection.fire(Object.freeze({ selection: this.selectedElements }));
}
}
private resolveUnknownParentChain(element: T): TPromise<TreeNode[]> {
setVisible(visible: boolean): void {
if (visible !== this._visible) {
this._visible = visible;
this._onDidChangeVisibility.fire(Object.freeze({ visible: this._visible }));
}
}
private resolveUnknownParentChain(element: T): Thenable<TreeNode[]> {
return this.resolveParent(element)
.then((parent) => {
if (!parent) {
@@ -200,16 +237,16 @@ class ExtHostTreeView<T> extends Disposable {
});
}
private resolveParent(element: T): TPromise<T> {
private resolveParent(element: T): Thenable<T> {
const node = this.nodes.get(element);
if (node) {
return TPromise.as(node.parent ? this.elements.get(node.parent.item.handle) : null);
}
return asWinJsPromise(() => this.dataProvider.getParent(element));
return asThenable(() => this.dataProvider.getParent(element));
}
private resolveTreeNode(element: T, parent?: TreeNode): TPromise<TreeNode> {
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
private resolveTreeNode(element: T, parent?: TreeNode): Thenable<TreeNode> {
return asThenable(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => this.createHandle(element, extTreeItem, parent, true))
.then(handle => this.getChildren(parent ? parent.item.handle : null)
.then(() => {
@@ -238,21 +275,21 @@ class ExtHostTreeView<T> extends Disposable {
return this.roots;
}
private fetchChildrenNodes(parentElement?: T): TPromise<TreeNode[]> {
private fetchChildrenNodes(parentElement?: T): Thenable<TreeNode[]> {
// clear children cache
this.clearChildren(parentElement);
const parentNode = parentElement ? this.nodes.get(parentElement) : void 0;
return asWinJsPromise(() => this.dataProvider.getChildren(parentElement))
return asThenable(() => this.dataProvider.getChildren(parentElement))
.then(elements => TPromise.join(
(elements || [])
.filter(element => !!element)
.map(element => asWinJsPromise(() => this.dataProvider.getTreeItem(element))
.map(element => asThenable(() => this.dataProvider.getTreeItem(element))
.then(extTreeItem => extTreeItem ? this.createAndRegisterTreeNode(element, extTreeItem, parentNode) : null))))
.then(nodes => nodes.filter(n => !!n));
}
private refresh(elements: T[]): TPromise<void> {
private refresh(elements: T[]): Thenable<void> {
const hasRoot = elements.some(element => !element);
if (hasRoot) {
this.clearAll(); // clear cache
@@ -308,11 +345,11 @@ class ExtHostTreeView<T> extends Disposable {
.then(() => Object.keys(itemsToRefresh).length ? this.proxy.$refresh(this.viewId, itemsToRefresh) : null);
}
private refreshNode(treeItemHandle: TreeItemHandle): TPromise<TreeNode> {
private refreshNode(treeItemHandle: TreeItemHandle): Thenable<TreeNode> {
const extElement = this.getExtensionElement(treeItemHandle);
const existing = this.nodes.get(extElement);
this.clearChildren(extElement); // clear children cache
return asWinJsPromise(() => this.dataProvider.getTreeItem(extElement))
return asThenable(() => this.dataProvider.getTreeItem(extElement))
.then(extTreeItem => {
if (extTreeItem) {
const newNode = this.createTreeNode(extElement, extTreeItem, existing.parent);
@@ -389,7 +426,7 @@ class ExtHostTreeView<T> extends Disposable {
return handle;
}
private getLightIconPath(extensionTreeItem: vscode.TreeItem): string {
private getLightIconPath(extensionTreeItem: vscode.TreeItem): URI {
if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon)) {
if (typeof extensionTreeItem.iconPath === 'string'
|| extensionTreeItem.iconPath instanceof URI) {
@@ -400,18 +437,18 @@ class ExtHostTreeView<T> extends Disposable {
return void 0;
}
private getDarkIconPath(extensionTreeItem: vscode.TreeItem): string {
private getDarkIconPath(extensionTreeItem: vscode.TreeItem): URI {
if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon) && extensionTreeItem.iconPath['dark']) {
return this.getIconPath(extensionTreeItem.iconPath['dark']);
}
return void 0;
}
private getIconPath(iconPath: string | URI): string {
private getIconPath(iconPath: string | URI): URI {
if (iconPath instanceof URI) {
return iconPath.toString();
return iconPath;
}
return URI.file(iconPath).toString();
return URI.file(iconPath);
}
private addNodeToCache(element: T, node: TreeNode): void {
@@ -492,4 +529,4 @@ class ExtHostTreeView<T> extends Disposable {
dispose() {
this.clearAll();
}
}
}

View File

@@ -6,13 +6,14 @@
import * as modes from 'vs/editor/common/modes';
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 * as vscode from 'vscode';
import URI from 'vs/base/common/uri';
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
import { URI } from 'vs/base/common/uri';
import { ProgressLocation as MainProgressLocation } from 'vs/workbench/services/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';
@@ -20,8 +21,10 @@ import { ISelection } from 'vs/editor/common/core/selection';
import * as htmlContent from 'vs/base/common/htmlContent';
import { IRelativePattern } from 'vs/base/common/glob';
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
import { WorkspaceEditDto, ResourceTextEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { WorkspaceEditDto, ResourceTextEditDto, ResourceFileEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { MarkerSeverity, IRelatedInformation, IMarkerData, MarkerTag } from 'vs/platform/markers/common/markers';
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
export interface PositionLike {
line: number;
@@ -108,7 +111,7 @@ export namespace Diagnostic {
code: String(value.code),
severity: DiagnosticSeverity.from(value.severity),
relatedInformation: value.relatedInformation && value.relatedInformation.map(DiagnosticRelatedInformation.from),
customTags: Array.isArray(value.customTags) ? value.customTags.map(DiagnosticTag.from) : undefined,
tags: Array.isArray(value.tags) ? value.tags.map(DiagnosticTag.from) : undefined,
};
}
}
@@ -158,29 +161,22 @@ export namespace DiagnosticSeverity {
export namespace ViewColumn {
export function from(column?: vscode.ViewColumn): EditorViewColumn {
let editorColumn: EditorViewColumn;
if (column === <number>types.ViewColumn.One) {
editorColumn = 0;
} else if (column === <number>types.ViewColumn.Two) {
editorColumn = 1;
} else if (column === <number>types.ViewColumn.Three) {
editorColumn = 2;
} else {
// in any other case (no column or ViewColumn.Active), leave the
// editorColumn as undefined which signals to use the active column
editorColumn = undefined;
if (typeof column === 'number' && column >= types.ViewColumn.One) {
return column - 1; // adjust zero index (ViewColumn.ONE => 0)
}
return editorColumn;
if (column === types.ViewColumn.Beside) {
return SIDE_GROUP;
}
return ACTIVE_GROUP; // default is always the active group
}
export function to(position?: EditorViewColumn): vscode.ViewColumn {
if (position === 0) {
return <number>types.ViewColumn.One;
} else if (position === 1) {
return <number>types.ViewColumn.Two;
} else if (position === 2) {
return <number>types.ViewColumn.Three;
if (typeof position === 'number' && position >= 0) {
return position + 1; // adjust to index (ViewColumn.ONE => 1)
}
return undefined;
}
}
@@ -274,18 +270,19 @@ export const TextEdit = {
};
export namespace WorkspaceEdit {
export function from(value: vscode.WorkspaceEdit): modes.WorkspaceEdit {
const result: modes.WorkspaceEdit = {
export function from(value: vscode.WorkspaceEdit, documents?: ExtHostDocumentsAndEditors): WorkspaceEditDto {
const result: WorkspaceEditDto = {
edits: []
};
for (const entry of value.entries()) {
for (const entry of (value as types.WorkspaceEdit)._allEntries()) {
const [uri, uriOrEdits] = entry;
if (Array.isArray(uriOrEdits)) {
// text edits
result.edits.push({ resource: uri, edits: uriOrEdits.map(TextEdit.from) });
let 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
result.edits.push({ oldUri: uri, newUri: uriOrEdits });
result.edits.push(<ResourceFileEditDto>{ oldUri: uri, newUri: uriOrEdits, options: entry[2] });
}
}
return result;
@@ -299,11 +296,12 @@ export namespace WorkspaceEdit {
URI.revive((<ResourceTextEditDto>edit).resource),
<types.TextEdit[]>(<ResourceTextEditDto>edit).edits.map(TextEdit.to)
);
// } else {
// result.renameResource(
// URI.revive((<ResourceFileEditDto>edit).oldUri),
// URI.revive((<ResourceFileEditDto>edit).newUri)
// );
} else {
result.renameFile(
URI.revive((<ResourceFileEditDto>edit).oldUri),
URI.revive((<ResourceFileEditDto>edit).newUri),
(<ResourceFileEditDto>edit).options
);
}
}
return result;
@@ -355,16 +353,16 @@ export namespace SymbolKind {
}
}
export namespace SymbolInformation {
export function from(info: vscode.SymbolInformation): modes.SymbolInformation {
return <modes.SymbolInformation>{
export namespace WorkspaceSymbol {
export function from(info: vscode.SymbolInformation): search.IWorkspaceSymbol {
return <search.IWorkspaceSymbol>{
name: info.name,
kind: SymbolKind.from(info.kind),
containerName: info.containerName,
location: location.from(info.location)
};
}
export function to(info: modes.SymbolInformation): types.SymbolInformation {
export function to(info: search.IWorkspaceSymbol): types.SymbolInformation {
return new types.SymbolInformation(
info.name,
SymbolKind.to(info.kind),
@@ -374,27 +372,27 @@ export namespace SymbolInformation {
}
}
export namespace SymbolInformation2 {
export function from(info: vscode.SymbolInformation2): modes.SymbolInformation {
let result: modes.SymbolInformation = {
export namespace DocumentSymbol {
export function from(info: vscode.DocumentSymbol): modes.DocumentSymbol {
let result: modes.DocumentSymbol = {
name: info.name,
detail: undefined,
location: location.from(info.location),
definingRange: Range.from(info.definingRange),
kind: SymbolKind.from(info.kind),
containerName: info.containerName
detail: info.detail,
range: Range.from(info.range),
selectionRange: Range.from(info.selectionRange),
kind: SymbolKind.from(info.kind)
};
if (info.children) {
result.children = info.children.map(from);
}
return result;
}
export function to(info: modes.SymbolInformation): vscode.SymbolInformation2 {
let result = new types.SymbolInformation2(
export function to(info: modes.DocumentSymbol): vscode.DocumentSymbol {
let result = new types.DocumentSymbol(
info.name,
info.detail,
SymbolKind.to(info.kind),
info.containerName,
location.to(info.location),
Range.to(info.range),
Range.to(info.selectionRange),
);
if (info.children) {
result.children = info.children.map(to) as any;
@@ -415,6 +413,23 @@ export const location = {
}
};
export namespace DefinitionLink {
export function from(value: vscode.Location | vscode.DefinitionLink): modes.DefinitionLink {
const definitionLink = <vscode.DefinitionLink>value;
const location = <vscode.Location>value;
return {
origin: definitionLink.originSelectionRange
? Range.from(definitionLink.originSelectionRange)
: undefined,
uri: definitionLink.targetUri ? definitionLink.targetUri : location.uri,
range: Range.from(definitionLink.targetRange ? definitionLink.targetRange : location.range),
selectionRange: definitionLink.targetSelectionRange
? Range.from(definitionLink.targetSelectionRange)
: undefined,
};
}
}
export namespace Hover {
export function from(hover: vscode.Hover): modes.Hover {
return <modes.Hover>{
@@ -514,6 +529,8 @@ export namespace Suggest {
result.documentation = htmlContent.isMarkdownString(suggestion.documentation) ? MarkdownString.to(suggestion.documentation) : suggestion.documentation;
result.sortText = suggestion.sortText;
result.filterText = suggestion.filterText;
result.preselect = suggestion.preselect;
result.commitCharacters = suggestion.commitCharacters;
// 'overwrite[Before|After]'-logic
let overwriteBefore = (typeof suggestion.overwriteBefore === 'number') ? suggestion.overwriteBefore : 0;

View File

@@ -6,13 +6,15 @@
import * as crypto from 'crypto';
import URI from 'vs/base/common/uri';
import { URI } from 'vs/base/common/uri';
import { illegalArgument } from 'vs/base/common/errors';
import * as vscode from 'vscode';
import { isMarkdownString } from 'vs/base/common/htmlContent';
import { IRelativePattern } from 'vs/base/common/glob';
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';
export class Disposable {
@@ -492,38 +494,45 @@ export class TextEdit {
}
}
export interface IFileOperationOptions {
overwrite?: boolean;
ignoreIfExists?: boolean;
ignoreIfNotExists?: boolean;
recursive?: boolean;
}
export interface IFileOperation {
_type: 1;
from: URI;
to: URI;
options?: IFileOperationOptions;
}
export interface IFileTextEdit {
_type: 2;
uri: URI;
edit: TextEdit;
}
export class WorkspaceEdit implements vscode.WorkspaceEdit {
private _seqPool: number = 0;
private _edits = new Array<IFileOperation | IFileTextEdit>();
private _resourceEdits: { seq: number, from: URI, to: URI }[] = [];
private _textEdits = new Map<string, { seq: number, uri: URI, edits: TextEdit[] }>();
renameFile(from: vscode.Uri, to: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void {
this._edits.push({ _type: 1, from, to, options });
}
// createResource(uri: vscode.Uri): void {
// this.renameResource(undefined, uri);
// }
createFile(uri: vscode.Uri, options?: { overwrite?: boolean, ignoreIfExists?: boolean }): void {
this._edits.push({ _type: 1, from: undefined, to: uri, options });
}
// deleteResource(uri: vscode.Uri): void {
// this.renameResource(uri, undefined);
// }
// renameResource(from: vscode.Uri, to: vscode.Uri): void {
// this._resourceEdits.push({ seq: this._seqPool++, from, to });
// }
// resourceEdits(): [vscode.Uri, vscode.Uri][] {
// return this._resourceEdits.map(({ from, to }) => (<[vscode.Uri, vscode.Uri]>[from, to]));
// }
deleteFile(uri: vscode.Uri, options?: { recursive?: boolean, ignoreIfNotExists?: boolean }): void {
this._edits.push({ _type: 1, from: uri, to: undefined, options });
}
replace(uri: URI, range: Range, newText: string): void {
let edit = new TextEdit(range, newText);
let array = this.get(uri);
if (array) {
array.push(edit);
} else {
array = [edit];
}
this.set(uri, array);
this._edits.push({ _type: 2, uri, edit: new TextEdit(range, newText) });
}
insert(resource: URI, position: Position, newText: string): void {
@@ -535,55 +544,76 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
}
has(uri: URI): boolean {
return this._textEdits.has(uri.toString());
for (const edit of this._edits) {
if (edit._type === 2 && edit.uri.toString() === uri.toString()) {
return true;
}
}
return false;
}
set(uri: URI, edits: TextEdit[]): void {
let data = this._textEdits.get(uri.toString());
if (!data) {
data = { seq: this._seqPool++, uri, edits: [] };
this._textEdits.set(uri.toString(), data);
}
if (!edits) {
data.edits = undefined;
// remove all text edits for `uri`
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 = coalesce(this._edits);
} else {
data.edits = edits.slice(0);
// append edit to the end
for (const edit of edits) {
if (edit) {
this._edits.push({ _type: 2, uri, edit });
}
}
}
}
get(uri: URI): TextEdit[] {
if (!this._textEdits.has(uri.toString())) {
let res: TextEdit[] = [];
for (let candidate of this._edits) {
if (candidate._type === 2 && candidate.uri.toString() === uri.toString()) {
res.push(candidate.edit);
}
}
if (res.length === 0) {
return undefined;
}
const { edits } = this._textEdits.get(uri.toString());
return edits ? edits.slice() : undefined;
return res;
}
entries(): [URI, TextEdit[]][] {
const res: [URI, TextEdit[]][] = [];
this._textEdits.forEach(value => res.push([value.uri, value.edits]));
return res.slice();
let textEdits = new Map<string, [URI, TextEdit[]]>();
for (let candidate of this._edits) {
if (candidate._type === 2) {
let textEdit = textEdits.get(candidate.uri.toString());
if (!textEdit) {
textEdit = [candidate.uri, []];
textEdits.set(candidate.uri.toString(), textEdit);
}
textEdit[1].push(candidate.edit);
}
}
return values(textEdits);
}
allEntries(): ([URI, TextEdit[]] | [URI, URI])[] {
return this.entries();
// // use the 'seq' the we have assigned when inserting
// // the operation and use that order in the resulting
// // array
// const res: ([URI, TextEdit[]] | [URI, URI])[] = [];
// this._textEdits.forEach(value => {
// const { seq, uri, edits } = value;
// res[seq] = [uri, edits];
// });
// this._resourceEdits.forEach(value => {
// const { seq, from, to } = value;
// res[seq] = [from, to];
// });
// return res;
_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]);
} else {
res.push([edit.uri, [edit.edit]]);
}
}
return res;
}
get size(): number {
return this._textEdits.size + this._resourceEdits.length;
return this.entries().length;
}
toJSON(): any {
@@ -741,6 +771,18 @@ export class DiagnosticRelatedInformation {
this.location = location;
this.message = message;
}
static isEqual(a: DiagnosticRelatedInformation, b: DiagnosticRelatedInformation): boolean {
if (a === b) {
return true;
}
if (!a || !b) {
return false;
}
return a.message === b.message
&& a.location.range.isEqual(b.location.range)
&& a.location.uri.toString() === b.location.uri.toString();
}
}
export class Diagnostic {
@@ -751,7 +793,7 @@ export class Diagnostic {
code: string | number;
severity: DiagnosticSeverity;
relatedInformation: DiagnosticRelatedInformation[];
customTags?: DiagnosticTag[];
tags?: DiagnosticTag[];
constructor(range: Range, message: string, severity: DiagnosticSeverity = DiagnosticSeverity.Error) {
this.range = range;
@@ -768,6 +810,23 @@ export class Diagnostic {
code: this.code,
};
}
static isEqual(a: Diagnostic, b: Diagnostic): boolean {
if (a === b) {
return true;
}
if (!a || !b) {
return false;
}
return a.message === b.message
&& a.severity === b.severity
&& a.code === b.code
&& a.severity === b.severity
&& a.source === b.source
&& a.range.isEqual(b.range)
&& equals(a.tags, b.tags)
&& equals(a.relatedInformation, b.relatedInformation, DiagnosticRelatedInformation.isEqual);
}
}
export class Hover {
@@ -881,18 +940,29 @@ export class SymbolInformation {
}
}
export class SymbolInformation2 extends SymbolInformation {
definingRange: Range;
children: SymbolInformation2[];
constructor(name: string, kind: SymbolKind, containerName: string, location: Location) {
super(name, kind, containerName, location);
export class DocumentSymbol {
name: string;
detail: string;
kind: SymbolKind;
range: Range;
selectionRange: Range;
children: DocumentSymbol[];
constructor(name: string, detail: string, kind: SymbolKind, range: Range, selectionRange: Range) {
this.name = name;
this.detail = detail;
this.kind = kind;
this.range = range;
this.selectionRange = selectionRange;
this.children = [];
this.definingRange = location.range;
}
if (!this.range.contains(this.selectionRange)) {
throw new Error('selectionRange must be contained in fullRange');
}
}
}
export enum CodeActionTrigger {
Automatic = 1,
Manual = 2,
@@ -1023,6 +1093,12 @@ export class SignatureHelp {
}
}
export enum SignatureHelpTriggerReason {
Invoke = 1,
TriggerCharacter = 2,
Retrigger = 3,
}
export enum CompletionTriggerKind {
Invoke = 0,
TriggerCharacter = 1,
@@ -1062,7 +1138,7 @@ export enum CompletionItemKind {
TypeParameter = 24
}
export class CompletionItem {
export class CompletionItem implements vscode.CompletionItem {
label: string;
kind: CompletionItemKind;
@@ -1070,8 +1146,10 @@ export class CompletionItem {
documentation: string | MarkdownString;
sortText: string;
filterText: string;
preselect: boolean;
insertText: string | SnippetString;
range: Range;
commitCharacters: string[];
textEdit: TextEdit;
additionalTextEdits: TextEdit[];
command: vscode.Command;
@@ -1089,6 +1167,7 @@ export class CompletionItem {
documentation: this.documentation,
sortText: this.sortText,
filterText: this.filterText,
preselect: this.preselect,
insertText: this.insertText,
textEdit: this.textEdit
};
@@ -1109,9 +1188,16 @@ export class CompletionList {
export enum ViewColumn {
Active = -1,
Beside = -2,
One = 1,
Two = 2,
Three = 3
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9
}
export enum StatusBarAlignment {
@@ -1906,3 +1992,10 @@ export enum CommentThreadCollapsibleState {
*/
Expanded = 1
}
export class QuickInputButtons {
static readonly Back: vscode.QuickInputButton = { iconPath: 'back.svg' };
private constructor() { }
}

View File

@@ -5,9 +5,10 @@
import * as vscode from 'vscode';
import { MainContext, IMainContext, ExtHostUrlsShape, MainThreadUrlsShape } from './extHost.protocol';
import URI, { UriComponents } from 'vs/base/common/uri';
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';
export class ExtHostUrls implements ExtHostUrlsShape {
@@ -15,7 +16,7 @@ export class ExtHostUrls implements ExtHostUrlsShape {
private readonly _proxy: MainThreadUrlsShape;
private handles = new Set<string>();
private handlers = new Map<number, vscode.ProtocolHandler>();
private handlers = new Map<number, vscode.UriHandler>();
constructor(
mainContext: IMainContext
@@ -23,7 +24,7 @@ export class ExtHostUrls implements ExtHostUrlsShape {
this._proxy = mainContext.getProxy(MainContext.MainThreadUrls);
}
registerProtocolHandler(extensionId: string, handler: vscode.ProtocolHandler): vscode.Disposable {
registerUriHandler(extensionId: string, handler: vscode.UriHandler): vscode.Disposable {
if (this.handles.has(extensionId)) {
throw new Error(`Protocol handler already registered for extension ${extensionId}`);
}
@@ -31,23 +32,27 @@ export class ExtHostUrls implements ExtHostUrlsShape {
const handle = ExtHostUrls.HandlePool++;
this.handles.add(extensionId);
this.handlers.set(handle, handler);
this._proxy.$registerProtocolHandler(handle, extensionId);
this._proxy.$registerUriHandler(handle, extensionId);
return toDisposable(() => {
this.handles.delete(extensionId);
this.handlers.delete(handle);
this._proxy.$unregisterProtocolHandler(handle);
this._proxy.$unregisterUriHandler(handle);
});
}
$handleExternalUri(handle: number, uri: UriComponents): TPromise<void> {
$handleExternalUri(handle: number, uri: UriComponents): Thenable<void> {
const handler = this.handlers.get(handle);
if (!handler) {
return TPromise.as(null);
}
try {
handler.handleUri(URI.revive(uri));
} catch (err) {
onUnexpectedError(err);
}
handler.handleUri(URI.revive(uri));
return TPromise.as(null);
}
}
}

View File

@@ -3,14 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainContext, MainThreadWebviewsShape, IMainContext, ExtHostWebviewsShape, WebviewPanelHandle } from './extHost.protocol';
import * as vscode from 'vscode';
import { Event, Emitter } from 'vs/base/common/event';
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 { TPromise } from 'vs/base/common/winjs.base';
import * as vscode from 'vscode';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol';
import { Disposable } from './extHostTypes';
import URI from 'vs/base/common/uri';
type IconPath = URI | { light: URI, dark: URI };
export class ExtHostWebview implements vscode.Webview {
private readonly _handle: WebviewPanelHandle;
@@ -19,7 +21,7 @@ export class ExtHostWebview implements vscode.Webview {
private _options: vscode.WebviewOptions;
private _isDisposed: boolean = false;
readonly _onMessageEmitter = new Emitter<any>();
public readonly _onMessageEmitter = new Emitter<any>();
public readonly onDidReceiveMessage: Event<any> = this._onMessageEmitter.event;
constructor(
@@ -32,16 +34,16 @@ export class ExtHostWebview implements vscode.Webview {
this._options = options;
}
dispose() {
public dispose() {
this._onMessageEmitter.dispose();
}
get html(): string {
public get html(): string {
this.assertNotDisposed();
return this._html;
}
set html(value: string) {
public set html(value: string) {
this.assertNotDisposed();
if (this._html !== value) {
this._html = value;
@@ -49,11 +51,17 @@ export class ExtHostWebview implements vscode.Webview {
}
}
get options(): vscode.WebviewOptions {
public get options(): vscode.WebviewOptions {
this.assertNotDisposed();
return this._options;
}
public set options(newOptions: vscode.WebviewOptions) {
this.assertNotDisposed();
this._proxy.$setOptions(this._handle, newOptions);
this._options = newOptions;
}
public postMessage(message: any): Thenable<boolean> {
this.assertNotDisposed();
return this._proxy.$postMessage(this._handle, message);
@@ -72,12 +80,14 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
private readonly _proxy: MainThreadWebviewsShape;
private readonly _viewType: string;
private _title: string;
private _iconPath: IconPath;
private readonly _options: vscode.WebviewPanelOptions;
private readonly _webview: ExtHostWebview;
private _isDisposed: boolean = false;
private _viewColumn: vscode.ViewColumn;
private _visible: boolean = true;
private _active: boolean = true;
readonly _onDisposeEmitter = new Emitter<void>();
public readonly onDidDispose: Event<void> = this._onDisposeEmitter.event;
@@ -143,6 +153,20 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
}
}
get iconPath(): IconPath | undefined {
this.assertNotDisposed();
return this._iconPath;
}
set iconPath(value: IconPath | undefined) {
this.assertNotDisposed();
if (this._iconPath !== value) {
this._iconPath = value;
this._proxy.$setIconPath(this._handle, URI.isUri(value) ? { light: value, dark: value } : value);
}
}
get options() {
return this._options;
}
@@ -157,7 +181,17 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
this._viewColumn = value;
}
get visible(): boolean {
public get active(): boolean {
this.assertNotDisposed();
return this._active;
}
_setActive(value: boolean) {
this.assertNotDisposed();
this._active = value;
}
public get visible(): boolean {
this.assertNotDisposed();
return this._visible;
}
@@ -174,9 +208,10 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
public reveal(viewColumn?: vscode.ViewColumn, preserveFocus?: boolean): void {
this.assertNotDisposed();
this._proxy.$reveal(this._handle,
viewColumn ? typeConverters.ViewColumn.from(viewColumn) : undefined,
!!preserveFocus);
this._proxy.$reveal(this._handle, {
viewColumn: viewColumn ? typeConverters.ViewColumn.from(viewColumn) : undefined,
preserveFocus: !!preserveFocus
});
}
private assertNotDisposed() {
@@ -189,8 +224,11 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
export class ExtHostWebviews implements ExtHostWebviewsShape {
private static webviewHandlePool = 1;
private readonly _proxy: MainThreadWebviewsShape;
private static newHandle(): WebviewPanelHandle {
return ExtHostWebviews.webviewHandlePool++ + '';
}
private readonly _proxy: MainThreadWebviewsShape;
private readonly _webviewPanels = new Map<WebviewPanelHandle, ExtHostWebviewPanel>();
private readonly _serializers = new Map<string, vscode.WebviewPanelSerializer>();
@@ -200,22 +238,20 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
this._proxy = mainContext.getProxy(MainContext.MainThreadWebviews);
}
createWebview(
public createWebview(
extensionLocation: URI,
viewType: string,
title: string,
showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean },
options: (vscode.WebviewPanelOptions & vscode.WebviewOptions) | undefined,
extensionLocation: URI
options: (vscode.WebviewPanelOptions & vscode.WebviewOptions) = {},
): vscode.WebviewPanel {
options = options || {};
const viewColumn = typeof showOptions === 'object' ? showOptions.viewColumn : showOptions;
const webviewShowOptions = {
viewColumn: typeConverters.ViewColumn.from(viewColumn),
preserveFocus: typeof showOptions === 'object' && !!showOptions.preserveFocus
};
const handle = ExtHostWebviews.webviewHandlePool++ + '';
const handle = ExtHostWebviews.newHandle();
this._proxy.$createWebviewPanel(handle, viewType, title, webviewShowOptions, options, extensionLocation);
const webview = new ExtHostWebview(handle, this._proxy, options);
@@ -224,7 +260,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
return panel;
}
registerWebviewPanelSerializer(
public registerWebviewPanelSerializer(
viewType: string,
serializer: vscode.WebviewPanelSerializer
): vscode.Disposable {
@@ -241,19 +277,26 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
});
}
$onMessage(handle: WebviewPanelHandle, message: any): void {
public $onMessage(
handle: WebviewPanelHandle,
message: any
): void {
const panel = this.getWebviewPanel(handle);
if (panel) {
panel.webview._onMessageEmitter.fire(message);
}
}
$onDidChangeWebviewPanelViewState(handle: WebviewPanelHandle, visible: boolean, position: EditorViewColumn): void {
public $onDidChangeWebviewPanelViewState(
handle: WebviewPanelHandle,
newState: WebviewPanelViewState
): void {
const panel = this.getWebviewPanel(handle);
if (panel) {
const viewColumn = typeConverters.ViewColumn.to(position);
if (panel.visible !== visible || panel.viewColumn !== viewColumn) {
panel._setVisible(visible);
const viewColumn = typeConverters.ViewColumn.to(newState.position);
if (panel.active !== newState.active || panel.visible !== newState.visible || panel.viewColumn !== viewColumn) {
panel._setActive(newState.active);
panel._setVisible(newState.visible);
panel._setViewColumn(viewColumn);
panel._onDidChangeViewStateEmitter.fire({ webviewPanel: panel });
}

View File

@@ -4,22 +4,27 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { normalize } from 'vs/base/common/paths';
import { join, relative } from 'path';
import { delta as arrayDelta } from 'vs/base/common/arrays';
import { relative, posix } from 'path';
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext, MainThreadMessageServiceShape } from './extHost.protocol';
import * as vscode from 'vscode';
import { compare } from 'vs/base/common/strings';
import { Emitter, Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { basenameOrAuthority, isEqual } from 'vs/base/common/resources';
import { Counter } from 'vs/base/common/numbers';
import { normalize } from 'vs/base/common/paths';
import { isLinux } from 'vs/base/common/platform';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
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 { Severity } from 'vs/platform/notification/common/notification';
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 { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Range } from 'vs/workbench/api/node/extHostTypes';
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);
@@ -104,8 +109,8 @@ class ExtHostWorkspaceImpl extends Workspace {
private readonly _workspaceFolders: vscode.WorkspaceFolder[] = [];
private readonly _structure = TernarySearchTree.forPaths<vscode.WorkspaceFolder>();
private constructor(id: string, name: string, folders: vscode.WorkspaceFolder[]) {
super(id, name, folders.map(f => new WorkspaceFolder(f)));
private constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[]) {
super(id, folders.map(f => new WorkspaceFolder(f)));
// setup the workspace folder data structure
folders.forEach(folder => {
@@ -114,6 +119,10 @@ class ExtHostWorkspaceImpl extends Workspace {
});
}
get name(): string {
return this._name;
}
get workspaceFolders(): vscode.WorkspaceFolder[] {
return this._workspaceFolders.slice(0);
}
@@ -121,7 +130,7 @@ class ExtHostWorkspaceImpl extends Workspace {
getWorkspaceFolder(uri: URI, resolveParent?: boolean): vscode.WorkspaceFolder {
if (resolveParent && this._structure.get(uri.toString())) {
// `uri` is a workspace folder so we check for its parent
uri = uri.with({ path: posix.dirname(uri.path) });
uri = dirname(uri);
}
return this._structure.findSubstr(uri.toString());
}
@@ -133,8 +142,6 @@ class ExtHostWorkspaceImpl extends Workspace {
export class ExtHostWorkspace implements ExtHostWorkspaceShape {
private static _requestIdPool = 0;
private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
private readonly _proxy: MainThreadWorkspaceShape;
@@ -145,10 +152,13 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
private readonly _activeSearchCallbacks: ((match: IRawFileMatch2) => any)[] = [];
constructor(
mainContext: IMainContext,
data: IWorkspaceData,
private _logService: ILogService
private _logService: ILogService,
private _requestIdProvider: Counter
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadWorkspace);
this._messageService = mainContext.getProxy(MainContext.MainThreadMessageService);
@@ -161,6 +171,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
return this._actualWorkspace;
}
get name(): string {
return this._actualWorkspace ? this._actualWorkspace.name : undefined;
}
private get _actualWorkspace(): ExtHostWorkspaceImpl {
return this._unconfirmedWorkspace || this._confirmedWorkspace;
}
@@ -263,6 +277,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
if (folders.length === 0) {
return undefined;
}
// #54483 @Joh Why are we still using fsPath?
return folders[0].uri.fsPath;
}
@@ -324,18 +339,16 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
// Events
this._onDidChangeWorkspace.fire(Object.freeze({
added: Object.freeze<vscode.WorkspaceFolder[]>(added),
removed: Object.freeze<vscode.WorkspaceFolder[]>(removed)
added,
removed,
}));
}
// --- search ---
findFiles(include: vscode.GlobPattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: string, token?: vscode.CancellationToken): Thenable<vscode.Uri[]> {
findFiles(include: vscode.GlobPattern, exclude: vscode.GlobPattern, maxResults: number, extensionId: string, token: vscode.CancellationToken = CancellationToken.None): Thenable<vscode.Uri[]> {
this._logService.trace(`extHostWorkspace#findFiles: fileSearch, extension: ${extensionId}, entryPoint: findFiles`);
const requestId = ExtHostWorkspace._requestIdPool++;
let includePattern: string;
let includeFolder: string;
if (include) {
@@ -358,11 +371,80 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
}
}
const result = this._proxy.$startSearch(includePattern, includeFolder, excludePatternOrDisregardExcludes, maxResults, requestId);
if (token) {
token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId));
if (token && token.isCancellationRequested) {
return TPromise.wrap([]);
}
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) {
this._logService.trace(`extHostWorkspace#findTextInFiles: textSearch, extension: ${extensionId}, entryPoint: findTextInFiles`);
if (options.previewOptions && options.previewOptions.totalChars <= options.previewOptions.leadingChars) {
throw new Error('findTextInFiles: previewOptions.totalChars must be > previewOptions.leadingChars');
}
const requestId = this._requestIdProvider.getNext();
const globPatternToString = (pattern: vscode.GlobPattern | string) => {
if (typeof pattern === 'string') {
return pattern;
}
return join(pattern.base, pattern.pattern);
};
const queryOptions: IQueryOptions = {
ignoreSymlinks: typeof options.followSymlinks === 'boolean' ? !options.followSymlinks : undefined,
disregardIgnoreFiles: typeof options.useIgnoreFiles === 'boolean' ? !options.useIgnoreFiles : undefined,
disregardExcludeSettings: options.exclude === null,
fileEncoding: options.encoding,
maxResults: options.maxResults,
previewOptions: options.previewOptions,
includePattern: options.include && globPatternToString(options.include),
excludePattern: options.exclude && globPatternToString(options.exclude)
};
let isCanceled = false;
this._activeSearchCallbacks[requestId] = p => {
if (isCanceled) {
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)
});
});
};
if (token.isCancellationRequested) {
return TPromise.wrap(undefined);
}
return this._proxy.$startTextSearch(query, queryOptions, requestId, token).then(
() => {
delete this._activeSearchCallbacks[requestId];
},
err => {
delete this._activeSearchCallbacks[requestId];
return TPromise.wrapError(err);
});
}
$handleTextSearchResult(result: IRawFileMatch2, requestId: number): void {
if (this._activeSearchCallbacks[requestId]) {
this._activeSearchCallbacks[requestId](result);
}
return result.then(data => Array.isArray(data) ? data.map(URI.revive) : []);
}
saveAll(includeUntitled?: boolean): Thenable<boolean> {