mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 01:25:37 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
667
src/vs/workbench/api/node/extHost.api.impl.ts
Normal file
667
src/vs/workbench/api/node/extHost.api.impl.ts
Normal file
@@ -0,0 +1,667 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { TrieMap } from 'vs/base/common/map';
|
||||
import { score } from 'vs/editor/common/modes/languageSelector';
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import product from 'vs/platform/node/product';
|
||||
import pkg from 'vs/platform/node/package';
|
||||
import { ExtHostFileSystemEventService } from 'vs/workbench/api/node/extHostFileSystemEventService';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
|
||||
import { ExtHostDocumentContentProvider } from 'vs/workbench/api/node/extHostDocumentContentProviders';
|
||||
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/node/extHostDocumentSaveParticipant';
|
||||
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
|
||||
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
|
||||
import { ExtHostTreeViews } from 'vs/workbench/api/node/extHostTreeViews';
|
||||
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
|
||||
import { ExtHostQuickOpen } from 'vs/workbench/api/node/extHostQuickOpen';
|
||||
import { ExtHostProgress } from 'vs/workbench/api/node/extHostProgress';
|
||||
import { ExtHostSCM } from 'vs/workbench/api/node/extHostSCM';
|
||||
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
|
||||
import { ExtHostStatusBar } from 'vs/workbench/api/node/extHostStatusBar';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
|
||||
import { ExtHostOutputService } from 'vs/workbench/api/node/extHostOutputService';
|
||||
import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService';
|
||||
import { ExtHostMessageService } from 'vs/workbench/api/node/extHostMessageService';
|
||||
import { ExtHostEditors } from 'vs/workbench/api/node/extHostTextEditors';
|
||||
import { ExtHostLanguages } from 'vs/workbench/api/node/extHostLanguages';
|
||||
import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures';
|
||||
import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands';
|
||||
import { ExtHostTask } from 'vs/workbench/api/node/extHostTask';
|
||||
// {{SQL CARBON EDIT}}
|
||||
//import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService';
|
||||
import { ExtHostCredentials } from 'vs/workbench/api/node/extHostCredentials';
|
||||
import { ExtHostWindow } from 'vs/workbench/api/node/extHostWindow';
|
||||
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import EditorCommon = require('vs/editor/common/editorCommon');
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import * as vscode from 'vscode';
|
||||
import * as paths from 'vs/base/common/paths';
|
||||
import { MainContext, ExtHostContext, IInitData } from './extHost.protocol';
|
||||
import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration';
|
||||
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
|
||||
import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService';
|
||||
import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs';
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
(extension: IExtensionDescription): typeof vscode;
|
||||
}
|
||||
|
||||
function proposedApiFunction<T>(extension: IExtensionDescription, fn: T): T {
|
||||
if (extension.enableProposedApi) {
|
||||
return fn;
|
||||
} else {
|
||||
return <any>(() => {
|
||||
throw new Error(`[${extension.id}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.id}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method instantiates and returns the extension API surface
|
||||
*/
|
||||
export function createApiFactory(
|
||||
initData: IInitData,
|
||||
threadService: ExtHostThreadService,
|
||||
extensionService: ExtHostExtensionService
|
||||
): IExtensionApiFactory {
|
||||
|
||||
const mainThreadTelemetry = threadService.get(MainContext.MainThreadTelemetry);
|
||||
|
||||
// Addressable instances
|
||||
const extHostHeapService = threadService.set(ExtHostContext.ExtHostHeapService, new ExtHostHeapService());
|
||||
const extHostDocumentsAndEditors = threadService.set(ExtHostContext.ExtHostDocumentsAndEditors, new ExtHostDocumentsAndEditors(threadService, extensionService));
|
||||
const extHostDocuments = threadService.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(threadService, extHostDocumentsAndEditors));
|
||||
const extHostDocumentContentProviders = threadService.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(threadService, extHostDocumentsAndEditors));
|
||||
const extHostDocumentSaveParticipant = threadService.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostDocuments, threadService.get(MainContext.MainThreadWorkspace)));
|
||||
const extHostEditors = threadService.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(threadService, extHostDocumentsAndEditors));
|
||||
const extHostCommands = threadService.set(ExtHostContext.ExtHostCommands, new ExtHostCommands(threadService, extHostHeapService));
|
||||
const extHostTreeViews = threadService.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(threadService.get(MainContext.MainThreadTreeViews), extHostCommands));
|
||||
const extHostWorkspace = threadService.set(ExtHostContext.ExtHostWorkspace, new ExtHostWorkspace(threadService, initData.workspace));
|
||||
// {{SQL CARBON EDIT}}
|
||||
// const extHostDebugService = threadService.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(threadService, extHostWorkspace));
|
||||
const extHostConfiguration = threadService.set(ExtHostContext.ExtHostConfiguration, new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration));
|
||||
const extHostDiagnostics = threadService.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(threadService));
|
||||
const languageFeatures = threadService.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(threadService, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics));
|
||||
const extHostFileSystemEvent = threadService.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService());
|
||||
const extHostQuickOpen = threadService.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(threadService));
|
||||
const extHostTerminalService = threadService.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(threadService));
|
||||
const extHostSCM = threadService.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(threadService, extHostCommands));
|
||||
const extHostTask = threadService.set(ExtHostContext.ExtHostTask, new ExtHostTask(threadService));
|
||||
const extHostCredentials = threadService.set(ExtHostContext.ExtHostCredentials, new ExtHostCredentials(threadService));
|
||||
const extHostWindow = threadService.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(threadService));
|
||||
threadService.set(ExtHostContext.ExtHostExtensionService, extensionService);
|
||||
|
||||
// Check that no named customers are missing
|
||||
const expected: ProxyIdentifier<any>[] = Object.keys(ExtHostContext).map((key) => ExtHostContext[key]);
|
||||
threadService.assertRegistered(expected);
|
||||
|
||||
// Other instances
|
||||
const extHostMessageService = new ExtHostMessageService(threadService);
|
||||
const extHostDialogs = new ExtHostDialogs(threadService);
|
||||
const extHostStatusBar = new ExtHostStatusBar(threadService);
|
||||
const extHostProgress = new ExtHostProgress(threadService.get(MainContext.MainThreadProgress));
|
||||
const extHostOutputService = new ExtHostOutputService(threadService);
|
||||
const extHostLanguages = new ExtHostLanguages(threadService);
|
||||
|
||||
// Register API-ish commands
|
||||
ExtHostApiCommands.register(extHostCommands);
|
||||
|
||||
return function (extension: IExtensionDescription): typeof vscode {
|
||||
|
||||
if (extension.enableProposedApi && !extension.isBuiltin) {
|
||||
|
||||
if (
|
||||
!initData.environment.enableProposedApiForAll &&
|
||||
initData.environment.enableProposedApiFor.indexOf(extension.id) < 0
|
||||
) {
|
||||
extension.enableProposedApi = false;
|
||||
console.error(`Extension '${extension.id} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`);
|
||||
|
||||
} else {
|
||||
// proposed api is available when developing or when an extension was explicitly
|
||||
// spelled out via a command line argument
|
||||
console.warn(`Extension '${extension.id}' uses PROPOSED API which is subject to change and removal without notice.`);
|
||||
}
|
||||
}
|
||||
|
||||
const apiUsage = new class {
|
||||
private _seen = new Set<string>();
|
||||
publicLog(apiName: string) {
|
||||
if (this._seen.has(apiName)) {
|
||||
return undefined;
|
||||
}
|
||||
this._seen.add(apiName);
|
||||
return mainThreadTelemetry.$publicLog('apiUsage', {
|
||||
name: apiName,
|
||||
extension: extension.id
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// namespace: commands
|
||||
const commands: typeof vscode.commands = {
|
||||
registerCommand<T>(id: string, command: <T>(...args: any[]) => T | Thenable<T>, thisArgs?: any): vscode.Disposable {
|
||||
return extHostCommands.registerCommand(id, command, thisArgs);
|
||||
},
|
||||
registerTextEditorCommand(id: string, callback: (textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit, ...args: any[]) => void, thisArg?: any): vscode.Disposable {
|
||||
return extHostCommands.registerCommand(id, (...args: any[]): any => {
|
||||
let activeTextEditor = extHostEditors.getActiveTextEditor();
|
||||
if (!activeTextEditor) {
|
||||
console.warn('Cannot execute ' + id + ' because there is no active text editor.');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return activeTextEditor.edit((edit: vscode.TextEditorEdit) => {
|
||||
args.unshift(activeTextEditor, edit);
|
||||
callback.apply(thisArg, args);
|
||||
|
||||
}).then((result) => {
|
||||
if (!result) {
|
||||
console.warn('Edits from command ' + id + ' were not applied.');
|
||||
}
|
||||
}, (err) => {
|
||||
console.warn('An error occurred while running command ' + id, err);
|
||||
});
|
||||
});
|
||||
},
|
||||
registerDiffInformationCommand: proposedApiFunction(extension, (id: string, callback: (diff: vscode.LineChange[], ...args: any[]) => any, thisArg?: any): vscode.Disposable => {
|
||||
return extHostCommands.registerCommand(id, async (...args: any[]) => {
|
||||
let activeTextEditor = extHostEditors.getActiveTextEditor();
|
||||
if (!activeTextEditor) {
|
||||
console.warn('Cannot execute ' + id + ' because there is no active text editor.');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const diff = await extHostEditors.getDiffInformation(activeTextEditor.id);
|
||||
callback.apply(thisArg, [diff, ...args]);
|
||||
});
|
||||
}),
|
||||
executeCommand<T>(id: string, ...args: any[]): Thenable<T> {
|
||||
return extHostCommands.executeCommand<T>(id, ...args);
|
||||
},
|
||||
getCommands(filterInternal: boolean = false): Thenable<string[]> {
|
||||
return extHostCommands.getCommands(filterInternal);
|
||||
}
|
||||
};
|
||||
|
||||
// namespace: env
|
||||
const env: typeof vscode.env = Object.freeze({
|
||||
get machineId() { return initData.telemetryInfo.machineId; },
|
||||
get sessionId() { return initData.telemetryInfo.sessionId; },
|
||||
get language() { return Platform.language; },
|
||||
get appName() { return product.nameLong; },
|
||||
get appRoot() { return initData.environment.appRoot; },
|
||||
});
|
||||
|
||||
// namespace: extensions
|
||||
const extensions: typeof vscode.extensions = {
|
||||
getExtension(extensionId: string): Extension<any> {
|
||||
let desc = extensionService.getExtensionDescription(extensionId);
|
||||
if (desc) {
|
||||
return new Extension(extensionService, desc);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
get all(): Extension<any>[] {
|
||||
return extensionService.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc));
|
||||
}
|
||||
};
|
||||
|
||||
// namespace: languages
|
||||
const languages: typeof vscode.languages = {
|
||||
createDiagnosticCollection(name?: string): vscode.DiagnosticCollection {
|
||||
return extHostDiagnostics.createDiagnosticCollection(name);
|
||||
},
|
||||
getLanguages(): TPromise<string[]> {
|
||||
return extHostLanguages.getLanguages();
|
||||
},
|
||||
match(selector: vscode.DocumentSelector, document: vscode.TextDocument): number {
|
||||
return score(selector, <any>document.uri, document.languageId);
|
||||
},
|
||||
registerCodeActionsProvider(selector: vscode.DocumentSelector, provider: vscode.CodeActionProvider): vscode.Disposable {
|
||||
return languageFeatures.registerCodeActionProvider(selector, provider);
|
||||
},
|
||||
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
|
||||
return languageFeatures.registerCodeLensProvider(selector, provider);
|
||||
},
|
||||
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
|
||||
return languageFeatures.registerDefinitionProvider(selector, provider);
|
||||
},
|
||||
registerImplementationProvider(selector: vscode.DocumentSelector, provider: vscode.ImplementationProvider): vscode.Disposable {
|
||||
return languageFeatures.registerImplementationProvider(selector, provider);
|
||||
},
|
||||
registerTypeDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.TypeDefinitionProvider): vscode.Disposable {
|
||||
return languageFeatures.registerTypeDefinitionProvider(selector, provider);
|
||||
},
|
||||
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable {
|
||||
return languageFeatures.registerHoverProvider(selector, provider, extension.id);
|
||||
},
|
||||
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
|
||||
return languageFeatures.registerDocumentHighlightProvider(selector, provider);
|
||||
},
|
||||
registerReferenceProvider(selector: vscode.DocumentSelector, provider: vscode.ReferenceProvider): vscode.Disposable {
|
||||
return languageFeatures.registerReferenceProvider(selector, provider);
|
||||
},
|
||||
registerRenameProvider(selector: vscode.DocumentSelector, provider: vscode.RenameProvider): vscode.Disposable {
|
||||
return languageFeatures.registerRenameProvider(selector, provider);
|
||||
},
|
||||
registerDocumentSymbolProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider): vscode.Disposable {
|
||||
return languageFeatures.registerDocumentSymbolProvider(selector, provider);
|
||||
},
|
||||
registerWorkspaceSymbolProvider(provider: vscode.WorkspaceSymbolProvider): vscode.Disposable {
|
||||
return languageFeatures.registerWorkspaceSymbolProvider(provider);
|
||||
},
|
||||
registerDocumentFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable {
|
||||
return languageFeatures.registerDocumentFormattingEditProvider(selector, provider);
|
||||
},
|
||||
registerDocumentRangeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable {
|
||||
return languageFeatures.registerDocumentRangeFormattingEditProvider(selector, provider);
|
||||
},
|
||||
registerOnTypeFormattingEditProvider(selector: vscode.DocumentSelector, provider: vscode.OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacters: string[]): vscode.Disposable {
|
||||
return languageFeatures.registerOnTypeFormattingEditProvider(selector, provider, [firstTriggerCharacter].concat(moreTriggerCharacters));
|
||||
},
|
||||
registerSignatureHelpProvider(selector: vscode.DocumentSelector, provider: vscode.SignatureHelpProvider, ...triggerCharacters: string[]): vscode.Disposable {
|
||||
return languageFeatures.registerSignatureHelpProvider(selector, provider, triggerCharacters);
|
||||
},
|
||||
registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable {
|
||||
return languageFeatures.registerCompletionItemProvider(selector, provider, triggerCharacters);
|
||||
},
|
||||
registerDocumentLinkProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentLinkProvider): vscode.Disposable {
|
||||
return languageFeatures.registerDocumentLinkProvider(selector, provider);
|
||||
},
|
||||
setLanguageConfiguration: (language: string, configuration: vscode.LanguageConfiguration): vscode.Disposable => {
|
||||
return languageFeatures.setLanguageConfiguration(language, configuration);
|
||||
},
|
||||
// proposed API
|
||||
registerColorProvider: proposedApiFunction(extension, (selector: vscode.DocumentSelector, provider: vscode.DocumentColorProvider) => {
|
||||
return languageFeatures.registerColorProvider(selector, provider);
|
||||
})
|
||||
};
|
||||
|
||||
// namespace: window
|
||||
const window: typeof vscode.window = {
|
||||
get activeTextEditor() {
|
||||
return extHostEditors.getActiveTextEditor();
|
||||
},
|
||||
get visibleTextEditors() {
|
||||
return extHostEditors.getVisibleTextEditors();
|
||||
},
|
||||
showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): TPromise<vscode.TextEditor> {
|
||||
let documentPromise: TPromise<vscode.TextDocument>;
|
||||
if (URI.isUri(documentOrUri)) {
|
||||
documentPromise = TPromise.wrap(workspace.openTextDocument(documentOrUri));
|
||||
} else {
|
||||
documentPromise = TPromise.wrap(<vscode.TextDocument>documentOrUri);
|
||||
}
|
||||
return documentPromise.then(document => {
|
||||
return extHostEditors.showTextDocument(document, columnOrOptions, preserveFocus);
|
||||
});
|
||||
},
|
||||
createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType {
|
||||
return extHostEditors.createTextEditorDecorationType(options);
|
||||
},
|
||||
onDidChangeActiveTextEditor(listener, thisArg?, disposables?) {
|
||||
return extHostEditors.onDidChangeActiveTextEditor(listener, thisArg, disposables);
|
||||
},
|
||||
onDidChangeVisibleTextEditors(listener, thisArg, disposables) {
|
||||
return extHostEditors.onDidChangeVisibleTextEditors(listener, thisArg, disposables);
|
||||
},
|
||||
onDidChangeTextEditorSelection(listener: (e: vscode.TextEditorSelectionChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) {
|
||||
return extHostEditors.onDidChangeTextEditorSelection(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeTextEditorOptions(listener: (e: vscode.TextEditorOptionsChangeEvent) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) {
|
||||
return extHostEditors.onDidChangeTextEditorOptions(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeTextEditorViewColumn(listener, thisArg?, disposables?) {
|
||||
return extHostEditors.onDidChangeTextEditorViewColumn(listener, thisArg, disposables);
|
||||
},
|
||||
onDidCloseTerminal(listener, thisArg?, disposables?) {
|
||||
return extHostTerminalService.onDidCloseTerminal(listener, thisArg, disposables);
|
||||
},
|
||||
get state() {
|
||||
return extHostWindow.state;
|
||||
},
|
||||
onDidChangeWindowState: proposedApiFunction(extension, (listener, thisArg?, disposables?) => {
|
||||
return extHostWindow.onDidChangeWindowState(listener, thisArg, disposables);
|
||||
}),
|
||||
showInformationMessage(message, first, ...rest) {
|
||||
return extHostMessageService.showMessage(extension, Severity.Info, message, first, rest);
|
||||
},
|
||||
showWarningMessage(message, first, ...rest) {
|
||||
return extHostMessageService.showMessage(extension, Severity.Warning, message, first, rest);
|
||||
},
|
||||
showErrorMessage(message, first, ...rest) {
|
||||
return extHostMessageService.showMessage(extension, Severity.Error, message, first, rest);
|
||||
},
|
||||
showQuickPick(items: any, options: vscode.QuickPickOptions, token?: vscode.CancellationToken) {
|
||||
return extHostQuickOpen.showQuickPick(items, options, token);
|
||||
},
|
||||
showInputBox(options?: vscode.InputBoxOptions, token?: vscode.CancellationToken) {
|
||||
return extHostQuickOpen.showInput(options, token);
|
||||
},
|
||||
createStatusBarItem(position?: vscode.StatusBarAlignment, priority?: number): vscode.StatusBarItem {
|
||||
return extHostStatusBar.createStatusBarEntry(extension.id, <number>position, priority);
|
||||
},
|
||||
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): vscode.Disposable {
|
||||
return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable);
|
||||
},
|
||||
withScmProgress<R>(task: (progress: vscode.Progress<number>) => Thenable<R>) {
|
||||
console.warn(`[Deprecation Warning] function 'withScmProgress' is deprecated and should no longer be used. Use 'withProgress' instead.`);
|
||||
return extHostProgress.withProgress(extension, { location: extHostTypes.ProgressLocation.SourceControl }, (progress, token) => task({ report(n: number) { /*noop*/ } }));
|
||||
},
|
||||
withProgress<R>(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; percentage?: number }>) => Thenable<R>) {
|
||||
return extHostProgress.withProgress(extension, options, task);
|
||||
},
|
||||
createOutputChannel(name: string): vscode.OutputChannel {
|
||||
return extHostOutputService.createOutputChannel(name);
|
||||
},
|
||||
createTerminal(nameOrOptions: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
|
||||
if (typeof nameOrOptions === 'object') {
|
||||
return extHostTerminalService.createTerminalFromOptions(<vscode.TerminalOptions>nameOrOptions);
|
||||
}
|
||||
return extHostTerminalService.createTerminal(<string>nameOrOptions, shellPath, shellArgs);
|
||||
},
|
||||
registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider<any>): vscode.Disposable {
|
||||
return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider);
|
||||
},
|
||||
// proposed API
|
||||
sampleFunction: proposedApiFunction(extension, () => {
|
||||
return extHostMessageService.showMessage(extension, Severity.Info, 'Hello Proposed Api!', {}, []);
|
||||
}),
|
||||
showOpenDialog: proposedApiFunction(extension, options => {
|
||||
return extHostDialogs.showOpenDialog(options);
|
||||
})
|
||||
};
|
||||
|
||||
// namespace: workspace
|
||||
const workspace: typeof vscode.workspace = {
|
||||
get rootPath() {
|
||||
apiUsage.publicLog('workspace#rootPath');
|
||||
return extHostWorkspace.getPath();
|
||||
},
|
||||
set rootPath(value) {
|
||||
throw errors.readonly();
|
||||
},
|
||||
getWorkspaceFolder(resource) {
|
||||
return extHostWorkspace.getWorkspaceFolder(resource);
|
||||
},
|
||||
get workspaceFolders() {
|
||||
apiUsage.publicLog('workspace#workspaceFolders');
|
||||
return extHostWorkspace.getWorkspaceFolders();
|
||||
},
|
||||
onDidChangeWorkspaceFolders: function (listener, thisArgs?, disposables?) {
|
||||
apiUsage.publicLog('workspace#onDidChangeWorkspaceFolders');
|
||||
return extHostWorkspace.onDidChangeWorkspace(listener, thisArgs, disposables);
|
||||
},
|
||||
asRelativePath: (pathOrUri, includeWorkspace) => {
|
||||
return extHostWorkspace.getRelativePath(pathOrUri, includeWorkspace);
|
||||
},
|
||||
findFiles: (include, exclude, maxResults?, token?) => {
|
||||
return extHostWorkspace.findFiles(include, exclude, maxResults, token);
|
||||
},
|
||||
saveAll: (includeUntitled?) => {
|
||||
return extHostWorkspace.saveAll(includeUntitled);
|
||||
},
|
||||
applyEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
|
||||
return extHostWorkspace.appyEdit(edit);
|
||||
},
|
||||
createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): vscode.FileSystemWatcher => {
|
||||
return extHostFileSystemEvent.createFileSystemWatcher(pattern, ignoreCreate, ignoreChange, ignoreDelete);
|
||||
},
|
||||
get textDocuments() {
|
||||
return extHostDocuments.getAllDocumentData().map(data => data.document);
|
||||
},
|
||||
set textDocuments(value) {
|
||||
throw errors.readonly();
|
||||
},
|
||||
openTextDocument(uriOrFileNameOrOptions?: vscode.Uri | string | { language?: string; content?: string; }) {
|
||||
let uriPromise: TPromise<URI>;
|
||||
|
||||
let options = uriOrFileNameOrOptions as { language?: string; content?: string; };
|
||||
if (!options || typeof options.language === 'string') {
|
||||
uriPromise = extHostDocuments.createDocumentData(options);
|
||||
} else if (typeof uriOrFileNameOrOptions === 'string') {
|
||||
uriPromise = TPromise.as(URI.file(uriOrFileNameOrOptions));
|
||||
} else if (uriOrFileNameOrOptions instanceof URI) {
|
||||
uriPromise = TPromise.as(<URI>uriOrFileNameOrOptions);
|
||||
} else {
|
||||
throw new Error('illegal argument - uriOrFileNameOrOptions');
|
||||
}
|
||||
|
||||
return uriPromise.then(uri => {
|
||||
return extHostDocuments.ensureDocumentData(uri).then(() => {
|
||||
const data = extHostDocuments.getDocumentData(uri);
|
||||
return data && data.document;
|
||||
});
|
||||
});
|
||||
},
|
||||
onDidOpenTextDocument: (listener, thisArgs?, disposables?) => {
|
||||
return extHostDocuments.onDidAddDocument(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidCloseTextDocument: (listener, thisArgs?, disposables?) => {
|
||||
return extHostDocuments.onDidRemoveDocument(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeTextDocument: (listener, thisArgs?, disposables?) => {
|
||||
return extHostDocuments.onDidChangeDocument(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidSaveTextDocument: (listener, thisArgs?, disposables?) => {
|
||||
return extHostDocuments.onDidSaveDocument(listener, thisArgs, disposables);
|
||||
},
|
||||
onWillSaveTextDocument: (listener, thisArgs?, disposables?) => {
|
||||
return extHostDocumentSaveParticipant.onWillSaveTextDocumentEvent(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeConfiguration: (listener: (_: any) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => {
|
||||
return extHostConfiguration.onDidChangeConfiguration(listener, thisArgs, disposables);
|
||||
},
|
||||
getConfiguration: (section?: string, resource?: vscode.Uri): vscode.WorkspaceConfiguration => {
|
||||
return extHostConfiguration.getConfiguration(section, <URI>resource);
|
||||
},
|
||||
registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider) {
|
||||
return extHostDocumentContentProviders.registerTextDocumentContentProvider(scheme, provider);
|
||||
},
|
||||
registerTaskProvider: (type: string, provider: vscode.TaskProvider) => {
|
||||
return extHostTask.registerTaskProvider(extension, provider);
|
||||
},
|
||||
registerFileSystemProvider: proposedApiFunction(extension, (authority, provider) => {
|
||||
return extHostWorkspace.registerFileSystemProvider(authority, provider);
|
||||
})
|
||||
};
|
||||
|
||||
// namespace: scm
|
||||
const scm: typeof vscode.scm = {
|
||||
get inputBox() {
|
||||
return extHostSCM.getLastInputBox(extension);
|
||||
},
|
||||
createSourceControl(id: string, label: string) {
|
||||
mainThreadTelemetry.$publicLog('registerSCMProvider', {
|
||||
extensionId: extension.id,
|
||||
providerId: id,
|
||||
providerLabel: label
|
||||
});
|
||||
|
||||
return extHostSCM.createSourceControl(extension, id, label);
|
||||
}
|
||||
};
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// delete namespace: debug
|
||||
|
||||
// namespace: credentials
|
||||
const credentials = {
|
||||
readSecret(service: string, account: string): Thenable<string | undefined> {
|
||||
return extHostCredentials.readSecret(service, account);
|
||||
},
|
||||
writeSecret(service: string, account: string, secret: string): Thenable<void> {
|
||||
return extHostCredentials.writeSecret(service, account, secret);
|
||||
},
|
||||
deleteSecret(service: string, account: string): Thenable<boolean> {
|
||||
return extHostCredentials.deleteSecret(service, account);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const api: typeof vscode = {
|
||||
version: pkg.version,
|
||||
// namespaces
|
||||
commands,
|
||||
env,
|
||||
extensions,
|
||||
languages,
|
||||
window,
|
||||
workspace,
|
||||
scm,
|
||||
// {{SQL CARBON EDIT}}
|
||||
// debug,
|
||||
// types
|
||||
CancellationTokenSource: CancellationTokenSource,
|
||||
CodeLens: extHostTypes.CodeLens,
|
||||
Color: extHostTypes.Color,
|
||||
ColorRange: extHostTypes.ColorRange,
|
||||
EndOfLine: extHostTypes.EndOfLine,
|
||||
CompletionItem: extHostTypes.CompletionItem,
|
||||
CompletionItemKind: extHostTypes.CompletionItemKind,
|
||||
CompletionList: extHostTypes.CompletionList,
|
||||
Diagnostic: extHostTypes.Diagnostic,
|
||||
DiagnosticSeverity: extHostTypes.DiagnosticSeverity,
|
||||
Disposable: extHostTypes.Disposable,
|
||||
DocumentHighlight: extHostTypes.DocumentHighlight,
|
||||
DocumentHighlightKind: extHostTypes.DocumentHighlightKind,
|
||||
DocumentLink: extHostTypes.DocumentLink,
|
||||
EventEmitter: Emitter,
|
||||
Hover: extHostTypes.Hover,
|
||||
IndentAction: languageConfiguration.IndentAction,
|
||||
Location: extHostTypes.Location,
|
||||
MarkdownString: MarkdownString,
|
||||
OverviewRulerLane: EditorCommon.OverviewRulerLane,
|
||||
ParameterInformation: extHostTypes.ParameterInformation,
|
||||
Position: extHostTypes.Position,
|
||||
Range: extHostTypes.Range,
|
||||
Selection: extHostTypes.Selection,
|
||||
SignatureHelp: extHostTypes.SignatureHelp,
|
||||
SignatureInformation: extHostTypes.SignatureInformation,
|
||||
SnippetString: extHostTypes.SnippetString,
|
||||
StatusBarAlignment: extHostTypes.StatusBarAlignment,
|
||||
SymbolInformation: extHostTypes.SymbolInformation,
|
||||
SymbolKind: extHostTypes.SymbolKind,
|
||||
TextDocumentSaveReason: extHostTypes.TextDocumentSaveReason,
|
||||
TextEdit: extHostTypes.TextEdit,
|
||||
TextEditorCursorStyle: TextEditorCursorStyle,
|
||||
TextEditorLineNumbersStyle: extHostTypes.TextEditorLineNumbersStyle,
|
||||
TextEditorRevealType: extHostTypes.TextEditorRevealType,
|
||||
TextEditorSelectionChangeKind: extHostTypes.TextEditorSelectionChangeKind,
|
||||
DecorationRangeBehavior: extHostTypes.DecorationRangeBehavior,
|
||||
Uri: <any>URI,
|
||||
ViewColumn: extHostTypes.ViewColumn,
|
||||
WorkspaceEdit: extHostTypes.WorkspaceEdit,
|
||||
ProgressLocation: extHostTypes.ProgressLocation,
|
||||
TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState,
|
||||
TreeItem: extHostTypes.TreeItem,
|
||||
ThemeColor: extHostTypes.ThemeColor,
|
||||
// functions
|
||||
TaskRevealKind: extHostTypes.TaskRevealKind,
|
||||
TaskPanelKind: extHostTypes.TaskPanelKind,
|
||||
TaskGroup: extHostTypes.TaskGroup,
|
||||
ProcessExecution: extHostTypes.ProcessExecution,
|
||||
ShellExecution: extHostTypes.ShellExecution,
|
||||
Task: extHostTypes.Task,
|
||||
ConfigurationTarget: extHostTypes.ConfigurationTarget
|
||||
};
|
||||
if (extension.enableProposedApi && extension.isBuiltin) {
|
||||
api['credentials'] = credentials;
|
||||
}
|
||||
return api;
|
||||
};
|
||||
}
|
||||
|
||||
class Extension<T> implements vscode.Extension<T> {
|
||||
|
||||
private _extensionService: ExtHostExtensionService;
|
||||
|
||||
public id: string;
|
||||
public extensionPath: string;
|
||||
public packageJSON: any;
|
||||
|
||||
constructor(extensionService: ExtHostExtensionService, description: IExtensionDescription) {
|
||||
this._extensionService = extensionService;
|
||||
this.id = description.id;
|
||||
this.extensionPath = paths.normalize(description.extensionFolderPath, true);
|
||||
this.packageJSON = description;
|
||||
}
|
||||
|
||||
get isActive(): boolean {
|
||||
return this._extensionService.isActivated(this.id);
|
||||
}
|
||||
|
||||
get exports(): T {
|
||||
return <T>this._extensionService.getExtensionExports(this.id);
|
||||
}
|
||||
|
||||
activate(): Thenable<T> {
|
||||
return this._extensionService.activateById(this.id, false).then(() => this.exports);
|
||||
}
|
||||
}
|
||||
|
||||
export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): TPromise<void> {
|
||||
return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie));
|
||||
}
|
||||
|
||||
function defineAPI(factory: IExtensionApiFactory, extensionPaths: TrieMap<IExtensionDescription>): void {
|
||||
|
||||
// each extension is meant to get its own api implementation
|
||||
const extApiImpl = new Map<string, typeof vscode>();
|
||||
let defaultApiImpl: typeof vscode;
|
||||
|
||||
const node_module = <any>require.__$__nodeRequire('module');
|
||||
const original = node_module._load;
|
||||
node_module._load = function load(request, parent, isMain) {
|
||||
if (request !== 'vscode') {
|
||||
return original.apply(this, arguments);
|
||||
}
|
||||
|
||||
// get extension id from filename and api for extension
|
||||
const ext = extensionPaths.findSubstr(parent.filename);
|
||||
if (ext) {
|
||||
let apiImpl = extApiImpl.get(ext.id);
|
||||
if (!apiImpl) {
|
||||
apiImpl = factory(ext);
|
||||
extApiImpl.set(ext.id, apiImpl);
|
||||
}
|
||||
return apiImpl;
|
||||
}
|
||||
|
||||
// fall back to a default implementation
|
||||
if (!defaultApiImpl) {
|
||||
defaultApiImpl = factory(nullExtensionDescription);
|
||||
}
|
||||
return defaultApiImpl;
|
||||
};
|
||||
}
|
||||
|
||||
const nullExtensionDescription: IExtensionDescription = {
|
||||
id: 'nullExtensionDescription',
|
||||
name: 'Null Extension Description',
|
||||
publisher: 'vscode',
|
||||
activationEvents: undefined,
|
||||
contributes: undefined,
|
||||
enableProposedApi: false,
|
||||
engines: undefined,
|
||||
extensionDependencies: undefined,
|
||||
extensionFolderPath: undefined,
|
||||
isBuiltin: false,
|
||||
main: undefined,
|
||||
version: undefined
|
||||
};
|
||||
614
src/vs/workbench/api/node/extHost.protocol.ts
Normal file
614
src/vs/workbench/api/node/extHost.protocol.ts
Normal file
@@ -0,0 +1,614 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {
|
||||
createMainContextProxyIdentifier as createMainId,
|
||||
createExtHostContextProxyIdentifier as createExtId,
|
||||
ProxyIdentifier
|
||||
} from 'vs/workbench/services/thread/common/threadService';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
import { IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
|
||||
import { IExtensionDescription } from 'vs/platform/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 { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
|
||||
import { ITextSource } from 'vs/editor/common/model/textSource';
|
||||
|
||||
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
|
||||
import { IConfigurationData } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
import { IPickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
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/mirrorModel';
|
||||
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/parts/views/common/views';
|
||||
import { ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { SerializedError } from 'vs/base/common/errors';
|
||||
|
||||
export interface IEnvironment {
|
||||
isExtensionDevelopmentDebug: boolean;
|
||||
enableProposedApiForAll: boolean;
|
||||
enableProposedApiFor: string | string[];
|
||||
appRoot: string;
|
||||
appSettingsHome: string;
|
||||
disableExtensions: boolean;
|
||||
userExtensionsHome: string;
|
||||
extensionDevelopmentPath: string;
|
||||
extensionTestsPath: string;
|
||||
}
|
||||
|
||||
export interface IWorkspaceData {
|
||||
id: string;
|
||||
name: string;
|
||||
roots: URI[];
|
||||
}
|
||||
|
||||
export interface IInitData {
|
||||
parentPid: number;
|
||||
environment: IEnvironment;
|
||||
workspace: IWorkspaceData;
|
||||
extensions: IExtensionDescription[];
|
||||
configuration: IConfigurationData<any>;
|
||||
telemetryInfo: ITelemetryInfo;
|
||||
}
|
||||
|
||||
export interface IExtHostContext {
|
||||
/**
|
||||
* Returns a proxy to an object addressable/named in the extension host process.
|
||||
*/
|
||||
get<T>(identifier: ProxyIdentifier<T>): T;
|
||||
|
||||
/**
|
||||
* Register manually created instance.
|
||||
*/
|
||||
set<T, R extends T>(identifier: ProxyIdentifier<T>, instance: R): R;
|
||||
}
|
||||
|
||||
export interface IMainContext {
|
||||
/**
|
||||
* Returns a proxy to an object addressable/named in the main/renderer process.
|
||||
*/
|
||||
get<T>(identifier: ProxyIdentifier<T>): T;
|
||||
}
|
||||
|
||||
// --- main thread
|
||||
|
||||
export interface MainThreadCommandsShape extends IDisposable {
|
||||
$registerCommand(id: string): TPromise<any>;
|
||||
$unregisterCommand(id: string): TPromise<any>;
|
||||
$executeCommand<T>(id: string, args: any[]): Thenable<T>;
|
||||
$getCommands(): Thenable<string[]>;
|
||||
}
|
||||
|
||||
export interface MainThreadConfigurationShape extends IDisposable {
|
||||
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: URI): TPromise<void>;
|
||||
$removeConfigurationOption(target: ConfigurationTarget, key: string, resource: URI): TPromise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadDiagnosticsShape extends IDisposable {
|
||||
$changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise<any>;
|
||||
$clear(owner: string): TPromise<any>;
|
||||
}
|
||||
|
||||
export interface MainThreadDialogOptions {
|
||||
uri?: URI;
|
||||
openLabel?: string;
|
||||
openFiles?: boolean;
|
||||
openFolders?: boolean;
|
||||
openMany?: boolean;
|
||||
}
|
||||
|
||||
export interface MainThreadDiaglogsShape extends IDisposable {
|
||||
$showOpenDialog(options: MainThreadDialogOptions): TPromise<string[]>;
|
||||
}
|
||||
|
||||
export interface MainThreadDocumentContentProvidersShape extends IDisposable {
|
||||
$registerTextContentProvider(handle: number, scheme: string): void;
|
||||
$unregisterTextContentProvider(handle: number): void;
|
||||
$onVirtualDocumentChange(uri: URI, value: ITextSource): void;
|
||||
}
|
||||
|
||||
export interface MainThreadDocumentsShape extends IDisposable {
|
||||
$tryCreateDocument(options?: { language?: string; content?: string; }): TPromise<any>;
|
||||
$tryOpenDocument(uri: URI): TPromise<any>;
|
||||
$trySaveDocument(uri: URI): TPromise<boolean>;
|
||||
}
|
||||
|
||||
export interface ISelectionChangeEvent {
|
||||
selections: Selection[];
|
||||
source?: string;
|
||||
}
|
||||
|
||||
export interface ITextEditorConfigurationUpdate {
|
||||
tabSize?: number | 'auto';
|
||||
insertSpaces?: boolean | 'auto';
|
||||
cursorStyle?: TextEditorCursorStyle;
|
||||
lineNumbers?: TextEditorLineNumbersStyle;
|
||||
}
|
||||
|
||||
export interface IResolvedTextEditorConfiguration {
|
||||
tabSize: number;
|
||||
insertSpaces: boolean;
|
||||
cursorStyle: TextEditorCursorStyle;
|
||||
lineNumbers: TextEditorLineNumbersStyle;
|
||||
}
|
||||
|
||||
export enum TextEditorRevealType {
|
||||
Default = 0,
|
||||
InCenter = 1,
|
||||
InCenterIfOutsideViewport = 2,
|
||||
AtTop = 3
|
||||
}
|
||||
|
||||
export interface IUndoStopOptions {
|
||||
undoStopBefore: boolean;
|
||||
undoStopAfter: boolean;
|
||||
}
|
||||
|
||||
export interface IApplyEditsOptions extends IUndoStopOptions {
|
||||
setEndOfLine: EndOfLine;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface ITextDocumentShowOptions {
|
||||
position?: EditorPosition;
|
||||
preserveFocus?: boolean;
|
||||
pinned?: boolean;
|
||||
selection?: IRange;
|
||||
}
|
||||
|
||||
export interface MainThreadEditorsShape extends IDisposable {
|
||||
$tryShowTextDocument(resource: URI, options: ITextDocumentShowOptions): TPromise<string>;
|
||||
$registerTextEditorDecorationType(key: string, options: editorCommon.IDecorationRenderOptions): void;
|
||||
$removeTextEditorDecorationType(key: string): void;
|
||||
$tryShowEditor(id: string, position: EditorPosition): TPromise<void>;
|
||||
$tryHideEditor(id: string): TPromise<void>;
|
||||
$trySetOptions(id: string, options: ITextEditorConfigurationUpdate): TPromise<any>;
|
||||
$trySetDecorations(id: string, key: string, ranges: editorCommon.IDecorationOptions[]): TPromise<any>;
|
||||
$tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): TPromise<any>;
|
||||
$trySetSelections(id: string, selections: ISelection[]): TPromise<any>;
|
||||
$tryApplyEdits(id: string, modelVersionId: number, edits: editorCommon.ISingleEditOperation[], opts: IApplyEditsOptions): TPromise<boolean>;
|
||||
$tryInsertSnippet(id: string, template: string, selections: IRange[], opts: IUndoStopOptions): TPromise<any>;
|
||||
$getDiffInformation(id: string): TPromise<editorCommon.ILineChange[]>;
|
||||
}
|
||||
|
||||
export interface MainThreadTreeViewsShape extends IDisposable {
|
||||
$registerView(treeViewId: string): void;
|
||||
$refresh(treeViewId: string, treeItemHandles: number[]): void;
|
||||
}
|
||||
|
||||
export interface MainThreadErrorsShape extends IDisposable {
|
||||
$onUnexpectedError(err: any | SerializedError, extensionId: string | undefined): void;
|
||||
}
|
||||
|
||||
export interface MainThreadLanguageFeaturesShape extends IDisposable {
|
||||
$unregister(handle: number): TPromise<any>;
|
||||
$registerOutlineSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerCodeLensSupport(handle: number, selector: vscode.DocumentSelector, eventHandle: number): TPromise<any>;
|
||||
$emitCodeLensEvent(eventHandle: number, event?: any): TPromise<any>;
|
||||
$registerDeclaractionSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerImplementationSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerTypeDefinitionSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerHoverProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerDocumentHighlightProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerReferenceSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerQuickFixSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerDocumentFormattingSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerRangeFormattingSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerOnTypeFormattingSupport(handle: number, selector: vscode.DocumentSelector, autoFormatTriggerCharacters: string[]): TPromise<any>;
|
||||
$registerNavigateTypeSupport(handle: number): TPromise<any>;
|
||||
$registerRenameSupport(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerSuggestSupport(handle: number, selector: vscode.DocumentSelector, triggerCharacters: string[]): TPromise<any>;
|
||||
$registerSignatureHelpProvider(handle: number, selector: vscode.DocumentSelector, triggerCharacter: string[]): TPromise<any>;
|
||||
$registerDocumentLinkProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$registerColorFormats(formats: IRawColorFormatMap): TPromise<any>;
|
||||
$registerDocumentColorProvider(handle: number, selector: vscode.DocumentSelector): TPromise<any>;
|
||||
$setLanguageConfiguration(handle: number, languageId: string, configuration: vscode.LanguageConfiguration): TPromise<any>;
|
||||
}
|
||||
|
||||
export interface MainThreadLanguagesShape extends IDisposable {
|
||||
$getLanguages(): TPromise<string[]>;
|
||||
}
|
||||
|
||||
export interface MainThreadMessageOptions {
|
||||
extension?: IExtensionDescription;
|
||||
modal?: boolean;
|
||||
}
|
||||
|
||||
export interface MainThreadMessageServiceShape extends IDisposable {
|
||||
$showMessage(severity: Severity, message: string, options: MainThreadMessageOptions, commands: { title: string; isCloseAffordance: boolean; handle: number; }[]): Thenable<number>;
|
||||
}
|
||||
|
||||
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>;
|
||||
}
|
||||
|
||||
export interface MainThreadProgressShape extends IDisposable {
|
||||
|
||||
$startProgress(handle: number, options: IProgressOptions): void;
|
||||
$progressReport(handle: number, message: IProgressStep): void;
|
||||
$progressEnd(handle: number): void;
|
||||
}
|
||||
|
||||
export interface MainThreadTerminalServiceShape extends IDisposable {
|
||||
$createTerminal(name?: string, shellPath?: string, shellArgs?: string[], waitOnExit?: boolean): TPromise<number>;
|
||||
$dispose(terminalId: number): void;
|
||||
$hide(terminalId: number): void;
|
||||
$sendText(terminalId: number, text: string, addNewLine: boolean): void;
|
||||
$show(terminalId: number, preserveFocus: boolean): void;
|
||||
}
|
||||
|
||||
export interface MyQuickPickItems extends IPickOpenEntry {
|
||||
handle: number;
|
||||
}
|
||||
export interface MainThreadQuickOpenShape extends IDisposable {
|
||||
$show(options: IPickOptions): TPromise<number>;
|
||||
$setItems(items: MyQuickPickItems[]): TPromise<any>;
|
||||
$setError(error: Error): TPromise<any>;
|
||||
$input(options: vscode.InputBoxOptions, validateInput: boolean): TPromise<string>;
|
||||
}
|
||||
|
||||
export interface MainThreadStatusBarShape extends IDisposable {
|
||||
$setEntry(id: number, extensionId: string, text: string, tooltip: string, command: string, color: string | ThemeColor, alignment: MainThreadStatusBarAlignment, priority: number): void;
|
||||
$dispose(id: number);
|
||||
}
|
||||
|
||||
export interface MainThreadStorageShape extends IDisposable {
|
||||
$getValue<T>(shared: boolean, key: string): TPromise<T>;
|
||||
$setValue(shared: boolean, key: string, value: any): TPromise<any>;
|
||||
}
|
||||
|
||||
export interface MainThreadTelemetryShape extends IDisposable {
|
||||
$publicLog(eventName: string, data?: any): void;
|
||||
}
|
||||
|
||||
export interface MainThreadWorkspaceShape extends IDisposable {
|
||||
$startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable<URI[]>;
|
||||
$cancelSearch(requestId: number): Thenable<boolean>;
|
||||
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
|
||||
$applyWorkspaceEdit(edits: IResourceEdit[]): TPromise<boolean>;
|
||||
|
||||
$registerFileSystemProvider(handle: number, authority: string): void;
|
||||
$unregisterFileSystemProvider(handle): void;
|
||||
$onFileSystemChange(handle: number, resource: URI): void;
|
||||
$updateSearchSession(session: number, data): void;
|
||||
$finishSearchSession(session: number, err?: any): void;
|
||||
}
|
||||
|
||||
export interface MainThreadTaskShape extends IDisposable {
|
||||
$registerTaskProvider(handle: number): TPromise<any>;
|
||||
$unregisterTaskProvider(handle: number): TPromise<any>;
|
||||
}
|
||||
|
||||
export interface MainThreadExtensionServiceShape extends IDisposable {
|
||||
$localShowMessage(severity: Severity, msg: string): void;
|
||||
$onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number): void;
|
||||
$onExtensionActivationFailed(extensionId: string): void;
|
||||
}
|
||||
|
||||
export interface SCMProviderFeatures {
|
||||
hasQuickDiffProvider?: boolean;
|
||||
count?: number;
|
||||
commitTemplate?: string;
|
||||
acceptInputCommand?: modes.Command;
|
||||
statusBarCommands?: modes.Command[];
|
||||
}
|
||||
|
||||
export interface SCMGroupFeatures {
|
||||
hideWhenEmpty?: boolean;
|
||||
}
|
||||
|
||||
export type SCMRawResource = [
|
||||
number /*handle*/,
|
||||
string /*resourceUri*/,
|
||||
modes.Command /*command*/,
|
||||
string[] /*icons: light, dark*/,
|
||||
string /*tooltip*/,
|
||||
boolean /*strike through*/,
|
||||
boolean /*faded*/
|
||||
];
|
||||
|
||||
export interface MainThreadSCMShape extends IDisposable {
|
||||
$registerSourceControl(handle: number, id: string, label: string): void;
|
||||
$updateSourceControl(handle: number, features: SCMProviderFeatures): void;
|
||||
$unregisterSourceControl(handle: number): void;
|
||||
|
||||
$registerGroup(sourceControlHandle: number, handle: number, id: string, label: string): void;
|
||||
$updateGroup(sourceControlHandle: number, handle: number, features: SCMGroupFeatures): void;
|
||||
$updateGroupLabel(sourceControlHandle: number, handle: number, label: string): void;
|
||||
$updateGroupResourceStates(sourceControlHandle: number, groupHandle: number, resources: SCMRawResource[]): void;
|
||||
$unregisterGroup(sourceControlHandle: number, handle: number): void;
|
||||
|
||||
$setInputBoxValue(sourceControlHandle: number, value: string): void;
|
||||
}
|
||||
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
/*
|
||||
export type DebugSessionUUID = string;
|
||||
|
||||
export interface MainThreadDebugServiceShape extends IDisposable {
|
||||
$registerDebugConfigurationProvider(type: string, hasProvideMethod: boolean, hasResolveMethod: boolean, handle: number): TPromise<any>;
|
||||
$unregisterDebugConfigurationProvider(handle: number): TPromise<any>;
|
||||
$startDebugging(folderUri: URI | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise<boolean>;
|
||||
$startDebugSession(folderUri: URI | undefined, config: vscode.DebugConfiguration): TPromise<DebugSessionUUID>;
|
||||
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): TPromise<any>;
|
||||
}
|
||||
*/
|
||||
export interface MainThreadCredentialsShape extends IDisposable {
|
||||
$readSecret(service: string, account: string): Thenable<string | undefined>;
|
||||
$writeSecret(service: string, account: string, secret: string): Thenable<void>;
|
||||
$deleteSecret(service: string, account: string): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface MainThreadWindowShape extends IDisposable {
|
||||
$getWindowVisibility(): TPromise<boolean>;
|
||||
}
|
||||
|
||||
// -- extension host
|
||||
|
||||
export interface ExtHostCommandsShape {
|
||||
$executeContributedCommand<T>(id: string, ...args: any[]): Thenable<T>;
|
||||
$getContributedCommandHandlerDescriptions(): TPromise<{ [id: string]: string | ICommandHandlerDescription }>;
|
||||
}
|
||||
|
||||
export interface ExtHostConfigurationShape {
|
||||
$acceptConfigurationChanged(data: IConfigurationData<any>);
|
||||
}
|
||||
|
||||
export interface ExtHostDiagnosticsShape {
|
||||
|
||||
}
|
||||
|
||||
export interface ExtHostDocumentContentProvidersShape {
|
||||
$provideTextDocumentContent(handle: number, uri: URI): TPromise<string>;
|
||||
}
|
||||
|
||||
export interface IModelAddedData {
|
||||
url: URI;
|
||||
versionId: number;
|
||||
lines: string[];
|
||||
EOL: string;
|
||||
modeId: string;
|
||||
isDirty: boolean;
|
||||
}
|
||||
export interface ExtHostDocumentsShape {
|
||||
$acceptModelModeChanged(strURL: string, oldModeId: string, newModeId: string): void;
|
||||
$acceptModelSaved(strURL: string): void;
|
||||
$acceptDirtyStateChanged(strURL: string, isDirty: boolean): void;
|
||||
$acceptModelChanged(strURL: string, e: IModelChangedEvent, isDirty: boolean): void;
|
||||
}
|
||||
|
||||
export interface ExtHostDocumentSaveParticipantShape {
|
||||
$participateInSave(resource: URI, reason: SaveReason): TPromise<boolean[]>;
|
||||
}
|
||||
|
||||
export interface ITextEditorAddData {
|
||||
id: string;
|
||||
document: URI;
|
||||
options: IResolvedTextEditorConfiguration;
|
||||
selections: ISelection[];
|
||||
editorPosition: EditorPosition;
|
||||
}
|
||||
export interface ITextEditorPositionData {
|
||||
[id: string]: EditorPosition;
|
||||
}
|
||||
export interface ExtHostEditorsShape {
|
||||
$acceptOptionsChanged(id: string, opts: IResolvedTextEditorConfiguration): void;
|
||||
$acceptSelectionsChanged(id: string, event: ISelectionChangeEvent): void;
|
||||
$acceptEditorPositionData(data: ITextEditorPositionData): void;
|
||||
}
|
||||
|
||||
export interface IDocumentsAndEditorsDelta {
|
||||
removedDocuments?: string[];
|
||||
addedDocuments?: IModelAddedData[];
|
||||
removedEditors?: string[];
|
||||
addedEditors?: ITextEditorAddData[];
|
||||
newActiveEditor?: string;
|
||||
}
|
||||
|
||||
export interface ExtHostDocumentsAndEditorsShape {
|
||||
$acceptDocumentsAndEditorsDelta(delta: IDocumentsAndEditorsDelta): void;
|
||||
}
|
||||
|
||||
export interface ExtHostTreeViewsShape {
|
||||
$getElements(treeViewId: string): TPromise<ITreeItem[]>;
|
||||
$getChildren(treeViewId: string, treeItemHandle: number): TPromise<ITreeItem[]>;
|
||||
}
|
||||
|
||||
export interface ExtHostWorkspaceShape {
|
||||
$acceptWorkspaceData(workspace: IWorkspaceData): void;
|
||||
|
||||
$resolveFile(handle: number, resource: URI): TPromise<string>;
|
||||
$storeFile(handle: number, resource: URI, content: string): TPromise<any>;
|
||||
$startSearch(handle: number, session: number, query: string): void;
|
||||
$cancelSearch(handle: number, session: number): void;
|
||||
}
|
||||
|
||||
export interface ExtHostExtensionServiceShape {
|
||||
$activateByEvent(activationEvent: string): TPromise<void>;
|
||||
}
|
||||
|
||||
export interface FileSystemEvents {
|
||||
created: URI[];
|
||||
changed: URI[];
|
||||
deleted: URI[];
|
||||
}
|
||||
export interface ExtHostFileSystemEventServiceShape {
|
||||
$onFileEvent(events: FileSystemEvents);
|
||||
}
|
||||
|
||||
export interface ObjectIdentifier {
|
||||
$ident: number;
|
||||
}
|
||||
|
||||
export namespace ObjectIdentifier {
|
||||
export const name = '$ident';
|
||||
export function mixin<T>(obj: T, id: number): T & ObjectIdentifier {
|
||||
Object.defineProperty(obj, name, { value: id, enumerable: true });
|
||||
return <T & ObjectIdentifier>obj;
|
||||
}
|
||||
export function of(obj: any): number {
|
||||
return obj[name];
|
||||
}
|
||||
}
|
||||
|
||||
export interface ExtHostHeapServiceShape {
|
||||
$onGarbageCollection(ids: number[]): void;
|
||||
}
|
||||
export interface IRawColorInfo {
|
||||
color: [number, number, number, number];
|
||||
availableFormats: (number | [number, number])[];
|
||||
range: IRange;
|
||||
}
|
||||
|
||||
export type IRawColorFormatMap = [number, string][];
|
||||
|
||||
export interface ExtHostLanguageFeaturesShape {
|
||||
$provideDocumentSymbols(handle: number, resource: URI): TPromise<modes.SymbolInformation[]>;
|
||||
$provideCodeLenses(handle: number, resource: URI): TPromise<modes.ICodeLensSymbol[]>;
|
||||
$resolveCodeLens(handle: number, resource: URI, symbol: modes.ICodeLensSymbol): TPromise<modes.ICodeLensSymbol>;
|
||||
$provideDefinition(handle: number, resource: URI, position: IPosition): TPromise<modes.Definition>;
|
||||
$provideImplementation(handle: number, resource: URI, position: IPosition): TPromise<modes.Definition>;
|
||||
$provideTypeDefinition(handle: number, resource: URI, position: IPosition): TPromise<modes.Definition>;
|
||||
$provideHover(handle: number, resource: URI, position: IPosition): TPromise<modes.Hover>;
|
||||
$provideDocumentHighlights(handle: number, resource: URI, position: IPosition): TPromise<modes.DocumentHighlight[]>;
|
||||
$provideReferences(handle: number, resource: URI, position: IPosition, context: modes.ReferenceContext): TPromise<modes.Location[]>;
|
||||
$provideCodeActions(handle: number, resource: URI, range: IRange): TPromise<modes.Command[]>;
|
||||
$provideDocumentFormattingEdits(handle: number, resource: URI, options: modes.FormattingOptions): TPromise<editorCommon.ISingleEditOperation[]>;
|
||||
$provideDocumentRangeFormattingEdits(handle: number, resource: URI, range: IRange, options: modes.FormattingOptions): TPromise<editorCommon.ISingleEditOperation[]>;
|
||||
$provideOnTypeFormattingEdits(handle: number, resource: URI, position: IPosition, ch: string, options: modes.FormattingOptions): TPromise<editorCommon.ISingleEditOperation[]>;
|
||||
$provideWorkspaceSymbols(handle: number, search: string): TPromise<modes.SymbolInformation[]>;
|
||||
$resolveWorkspaceSymbol(handle: number, symbol: modes.SymbolInformation): TPromise<modes.SymbolInformation>;
|
||||
$provideRenameEdits(handle: number, resource: URI, position: IPosition, newName: string): TPromise<modes.WorkspaceEdit>;
|
||||
$provideCompletionItems(handle: number, resource: URI, position: IPosition): TPromise<modes.ISuggestResult>;
|
||||
$resolveCompletionItem(handle: number, resource: URI, position: IPosition, suggestion: modes.ISuggestion): TPromise<modes.ISuggestion>;
|
||||
$provideSignatureHelp(handle: number, resource: URI, position: IPosition): TPromise<modes.SignatureHelp>;
|
||||
$provideDocumentLinks(handle: number, resource: URI): TPromise<modes.ILink[]>;
|
||||
$provideDocumentColors(handle: number, resource: URI): TPromise<IRawColorInfo[]>;
|
||||
$resolveDocumentLink(handle: number, link: modes.ILink): TPromise<modes.ILink>;
|
||||
}
|
||||
|
||||
export interface ExtHostQuickOpenShape {
|
||||
$onItemSelected(handle: number): void;
|
||||
$validateInput(input: string): TPromise<string>;
|
||||
}
|
||||
|
||||
export interface ExtHostTerminalServiceShape {
|
||||
$acceptTerminalClosed(id: number): void;
|
||||
$acceptTerminalProcessId(id: number, processId: number): void;
|
||||
}
|
||||
|
||||
export interface ExtHostSCMShape {
|
||||
$provideOriginalResource(sourceControlHandle: number, uri: URI): TPromise<URI>;
|
||||
$onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise<void>;
|
||||
}
|
||||
|
||||
export interface ExtHostTaskShape {
|
||||
$provideTasks(handle: number): TPromise<TaskSet>;
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
/*
|
||||
export interface ExtHostDebugServiceShape {
|
||||
$resolveDebugConfiguration(handle: number, folder: URI | undefined, debugConfiguration: any): TPromise<any>;
|
||||
$provideDebugConfigurations(handle: number, folder: URI | undefined): TPromise<any[]>;
|
||||
$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;
|
||||
$acceptDebugSessionCustomEvent(id: DebugSessionUUID, type: string, name: string, event: any): void;
|
||||
}
|
||||
*/
|
||||
|
||||
export interface ExtHostCredentialsShape {
|
||||
}
|
||||
|
||||
export interface ExtHostWindowShape {
|
||||
$onDidChangeWindowFocus(value: boolean): void;
|
||||
}
|
||||
|
||||
// --- proxy identifiers
|
||||
|
||||
export const MainContext = {
|
||||
MainThreadCommands: createMainId<MainThreadCommandsShape>('MainThreadCommands'),
|
||||
MainThreadConfiguration: createMainId<MainThreadConfigurationShape>('MainThreadConfiguration'),
|
||||
// {{SQL CARBON EDIT}}
|
||||
// MainThreadDebugService: createMainId<MainThreadDebugServiceShape>('MainThreadDebugService'),
|
||||
MainThreadDiagnostics: createMainId<MainThreadDiagnosticsShape>('MainThreadDiagnostics'),
|
||||
MainThreadDialogs: createMainId<MainThreadDiaglogsShape>('MainThreadDiaglogs'),
|
||||
MainThreadDocuments: createMainId<MainThreadDocumentsShape>('MainThreadDocuments'),
|
||||
MainThreadDocumentContentProviders: createMainId<MainThreadDocumentContentProvidersShape>('MainThreadDocumentContentProviders'),
|
||||
MainThreadEditors: createMainId<MainThreadEditorsShape>('MainThreadEditors'),
|
||||
MainThreadErrors: createMainId<MainThreadErrorsShape>('MainThreadErrors'),
|
||||
MainThreadTreeViews: createMainId<MainThreadTreeViewsShape>('MainThreadTreeViews'),
|
||||
MainThreadLanguageFeatures: createMainId<MainThreadLanguageFeaturesShape>('MainThreadLanguageFeatures'),
|
||||
MainThreadLanguages: createMainId<MainThreadLanguagesShape>('MainThreadLanguages'),
|
||||
MainThreadMessageService: createMainId<MainThreadMessageServiceShape>('MainThreadMessageService'),
|
||||
MainThreadOutputService: createMainId<MainThreadOutputServiceShape>('MainThreadOutputService'),
|
||||
MainThreadProgress: createMainId<MainThreadProgressShape>('MainThreadProgress'),
|
||||
MainThreadQuickOpen: createMainId<MainThreadQuickOpenShape>('MainThreadQuickOpen'),
|
||||
MainThreadStatusBar: createMainId<MainThreadStatusBarShape>('MainThreadStatusBar'),
|
||||
MainThreadStorage: createMainId<MainThreadStorageShape>('MainThreadStorage'),
|
||||
MainThreadTelemetry: createMainId<MainThreadTelemetryShape>('MainThreadTelemetry'),
|
||||
MainThreadTerminalService: createMainId<MainThreadTerminalServiceShape>('MainThreadTerminalService'),
|
||||
MainThreadWorkspace: createMainId<MainThreadWorkspaceShape>('MainThreadWorkspace'),
|
||||
MainThreadExtensionService: createMainId<MainThreadExtensionServiceShape>('MainThreadExtensionService'),
|
||||
MainThreadSCM: createMainId<MainThreadSCMShape>('MainThreadSCM'),
|
||||
MainThreadTask: createMainId<MainThreadTaskShape>('MainThreadTask'),
|
||||
MainThreadCredentials: createMainId<MainThreadCredentialsShape>('MainThreadCredentials'),
|
||||
MainThreadWindow: createMainId<MainThreadWindowShape>('MainThreadWindow'),
|
||||
};
|
||||
|
||||
export const ExtHostContext = {
|
||||
ExtHostCommands: createExtId<ExtHostCommandsShape>('ExtHostCommands'),
|
||||
ExtHostConfiguration: createExtId<ExtHostConfigurationShape>('ExtHostConfiguration'),
|
||||
ExtHostDiagnostics: createExtId<ExtHostDiagnosticsShape>('ExtHostDiagnostics'),
|
||||
// {{SQL CARBON EDIT}}
|
||||
// ExtHostDebugService: createExtId<ExtHostDebugServiceShape>('ExtHostDebugService'),
|
||||
ExtHostDocumentsAndEditors: createExtId<ExtHostDocumentsAndEditorsShape>('ExtHostDocumentsAndEditors'),
|
||||
ExtHostDocuments: createExtId<ExtHostDocumentsShape>('ExtHostDocuments'),
|
||||
ExtHostDocumentContentProviders: createExtId<ExtHostDocumentContentProvidersShape>('ExtHostDocumentContentProviders'),
|
||||
ExtHostDocumentSaveParticipant: createExtId<ExtHostDocumentSaveParticipantShape>('ExtHostDocumentSaveParticipant'),
|
||||
ExtHostEditors: createExtId<ExtHostEditorsShape>('ExtHostEditors'),
|
||||
ExtHostTreeViews: createExtId<ExtHostTreeViewsShape>('ExtHostTreeViews'),
|
||||
ExtHostFileSystemEventService: createExtId<ExtHostFileSystemEventServiceShape>('ExtHostFileSystemEventService'),
|
||||
ExtHostHeapService: createExtId<ExtHostHeapServiceShape>('ExtHostHeapMonitor'),
|
||||
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures'),
|
||||
ExtHostQuickOpen: createExtId<ExtHostQuickOpenShape>('ExtHostQuickOpen'),
|
||||
ExtHostExtensionService: createExtId<ExtHostExtensionServiceShape>('ExtHostExtensionService'),
|
||||
ExtHostTerminalService: createExtId<ExtHostTerminalServiceShape>('ExtHostTerminalService'),
|
||||
ExtHostSCM: createExtId<ExtHostSCMShape>('ExtHostSCM'),
|
||||
ExtHostTask: createExtId<ExtHostTaskShape>('ExtHostTask'),
|
||||
ExtHostWorkspace: createExtId<ExtHostWorkspaceShape>('ExtHostWorkspace'),
|
||||
ExtHostCredentials: createExtId<ExtHostCredentialsShape>('ExtHostCredentials'),
|
||||
ExtHostWindow: createExtId<ExtHostWindowShape>('ExtHostWindow'),
|
||||
};
|
||||
474
src/vs/workbench/api/node/extHostApiCommands.ts
Normal file
474
src/vs/workbench/api/node/extHostApiCommands.ts
Normal file
@@ -0,0 +1,474 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { 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 { ISingleEditOperation } from 'vs/editor/common/editorCommon';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
|
||||
import { IWorkspaceSymbolProvider } from 'vs/workbench/parts/search/common/search';
|
||||
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
|
||||
export class ExtHostApiCommands {
|
||||
|
||||
static register(commands: ExtHostCommands) {
|
||||
return new ExtHostApiCommands(commands).registerCommands();
|
||||
}
|
||||
|
||||
private _commands: ExtHostCommands;
|
||||
private _disposables: IDisposable[] = [];
|
||||
|
||||
private constructor(commands: ExtHostCommands) {
|
||||
this._commands = commands;
|
||||
}
|
||||
|
||||
registerCommands() {
|
||||
this._register('vscode.executeWorkspaceSymbolProvider', this._executeWorkspaceSymbolProvider, {
|
||||
description: 'Execute all workspace symbol provider.',
|
||||
args: [{ name: 'query', description: 'Search string', constraint: String }],
|
||||
returns: 'A promise that resolves to an array of SymbolInformation-instances.'
|
||||
|
||||
});
|
||||
this._register('vscode.executeDefinitionProvider', this._executeDefinitionProvider, {
|
||||
description: 'Execute all definition provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'position', description: 'Position of a symbol', constraint: types.Position }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of Location-instances.'
|
||||
});
|
||||
this._register('vscode.executeImplementationProvider', this._executeImplementationProvider, {
|
||||
description: 'Execute all implementation providers.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'position', description: 'Position of a symbol', constraint: types.Position }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of Location-instance.'
|
||||
});
|
||||
this._register('vscode.executeHoverProvider', this._executeHoverProvider, {
|
||||
description: 'Execute all hover provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'position', description: 'Position of a symbol', constraint: types.Position }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of Hover-instances.'
|
||||
});
|
||||
this._register('vscode.executeDocumentHighlights', this._executeDocumentHighlights, {
|
||||
description: 'Execute document highlight provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'position', description: 'Position in a text document', constraint: types.Position }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of DocumentHighlight-instances.'
|
||||
});
|
||||
this._register('vscode.executeReferenceProvider', this._executeReferenceProvider, {
|
||||
description: 'Execute reference provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'position', description: 'Position in a text document', constraint: types.Position }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of Location-instances.'
|
||||
});
|
||||
this._register('vscode.executeDocumentRenameProvider', this._executeDocumentRenameProvider, {
|
||||
description: 'Execute rename provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
|
||||
{ name: 'newName', description: 'The new symbol name', constraint: String }
|
||||
],
|
||||
returns: 'A promise that resolves to a WorkspaceEdit.'
|
||||
});
|
||||
this._register('vscode.executeSignatureHelpProvider', this._executeSignatureHelpProvider, {
|
||||
description: 'Execute signature help provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
|
||||
{ name: 'triggerCharacter', description: '(optional) Trigger signature help when the user types the character, like `,` or `(`', constraint: value => value === void 0 || typeof value === 'string' }
|
||||
],
|
||||
returns: 'A promise that resolves to SignatureHelp.'
|
||||
});
|
||||
this._register('vscode.executeDocumentSymbolProvider', this._executeDocumentSymbolProvider, {
|
||||
description: 'Execute document symbol provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of SymbolInformation-instances.'
|
||||
});
|
||||
this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider, {
|
||||
description: 'Execute completion item provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
|
||||
{ name: 'triggerCharacter', description: '(optional) Trigger completion when the user types the character, like `,` or `(`', constraint: value => value === void 0 || typeof value === 'string' }
|
||||
],
|
||||
returns: 'A promise that resolves to a CompletionList-instance.'
|
||||
});
|
||||
this._register('vscode.executeCodeActionProvider', this._executeCodeActionProvider, {
|
||||
description: 'Execute code action provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'range', description: 'Range in a text document', constraint: types.Range }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of Command-instances.'
|
||||
});
|
||||
this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider, {
|
||||
description: 'Execute CodeLens provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of CodeLens-instances.'
|
||||
});
|
||||
this._register('vscode.executeFormatDocumentProvider', this._executeFormatDocumentProvider, {
|
||||
description: 'Execute document format provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'options', description: 'Formatting options' }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of TextEdits.'
|
||||
});
|
||||
this._register('vscode.executeFormatRangeProvider', this._executeFormatRangeProvider, {
|
||||
description: 'Execute range format provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'range', description: 'Range in a text document', constraint: types.Range },
|
||||
{ name: 'options', description: 'Formatting options' }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of TextEdits.'
|
||||
});
|
||||
this._register('vscode.executeFormatOnTypeProvider', this._executeFormatOnTypeProvider, {
|
||||
description: 'Execute document format provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI },
|
||||
{ name: 'position', description: 'Position in a text document', constraint: types.Position },
|
||||
{ name: 'ch', description: 'Character that got typed', constraint: String },
|
||||
{ name: 'options', description: 'Formatting options' }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of TextEdits.'
|
||||
});
|
||||
this._register('vscode.executeLinkProvider', this._executeDocumentLinkProvider, {
|
||||
description: 'Execute document link provider.',
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of a text document', constraint: URI }
|
||||
],
|
||||
returns: 'A promise that resolves to an array of DocumentLink-instances.'
|
||||
});
|
||||
|
||||
this._register('vscode.previewHtml', (uri: URI, position?: vscode.ViewColumn, label?: string, options?: any) => {
|
||||
return this._commands.executeCommand('_workbench.previewHtml',
|
||||
uri,
|
||||
typeof position === 'number' && typeConverters.fromViewColumn(position),
|
||||
label,
|
||||
options);
|
||||
}, {
|
||||
description: `
|
||||
Render the html of the resource in an editor view.
|
||||
|
||||
See [working with the html preview](https://code.visualstudio.com/docs/extensionAPI/vscode-api-commands#working-with-the-html-preview) for more information about the html preview's intergration with the editor and for best practices for extension authors.
|
||||
`,
|
||||
args: [
|
||||
{ name: 'uri', description: 'Uri of the resource to preview.', constraint: value => value instanceof URI || typeof value === 'string' },
|
||||
{ name: 'column', description: '(optional) Column in which to preview.', constraint: value => typeof value === 'undefined' || (typeof value === 'number' && typeof types.ViewColumn[value] === 'string') },
|
||||
{ name: 'label', description: '(optional) An human readable string that is used as title for the preview.', constraint: v => typeof v === 'string' || typeof v === 'undefined' },
|
||||
{ name: 'options', description: '(optional) Options for controlling webview environment.', constraint: v => typeof v === 'object' || typeof v === 'undefined' }
|
||||
]
|
||||
});
|
||||
|
||||
this._register('vscode.openFolder', (uri?: URI, forceNewWindow?: boolean) => {
|
||||
if (!uri) {
|
||||
return this._commands.executeCommand('_files.pickFolderAndOpen', forceNewWindow);
|
||||
}
|
||||
|
||||
return this._commands.executeCommand('_files.windowOpen', [uri.fsPath], forceNewWindow);
|
||||
}, {
|
||||
description: 'Open a folder in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder unless the newWindow parameter is set to true.',
|
||||
args: [
|
||||
{ name: 'uri', description: '(optional) Uri of the folder to open. If not provided, a native dialog will ask the user for the folder', constraint: value => value === void 0 || value instanceof URI },
|
||||
{ name: 'newWindow', description: '(optional) Whether to open the folder in a new window or the same. Defaults to opening in the same window.', constraint: value => value === void 0 || typeof value === 'boolean' }
|
||||
]
|
||||
});
|
||||
|
||||
this._register('vscode.startDebug', (configuration?: any, folderUri?: URI) => {
|
||||
return this._commands.executeCommand('_workbench.startDebug', configuration, folderUri);
|
||||
}, {
|
||||
description: 'Start a debugging session.',
|
||||
args: [
|
||||
{ name: 'configuration', description: '(optional) Name of the debug configuration from \'launch.json\' to use. Or a configuration json object to use.' }
|
||||
]
|
||||
});
|
||||
|
||||
this._register('vscode.diff', (left: URI, right: URI, label: string, options?: vscode.TextDocumentShowOptions) => {
|
||||
let editorOptions: ITextEditorOptions;
|
||||
if (options) {
|
||||
editorOptions = {
|
||||
pinned: typeof options.preview === 'boolean' ? !options.preview : undefined,
|
||||
preserveFocus: options.preserveFocus,
|
||||
selection: typeof options.selection === 'object' ? typeConverters.fromRange(options.selection) : undefined
|
||||
};
|
||||
}
|
||||
|
||||
return this._commands.executeCommand('_workbench.diff', [
|
||||
left, right,
|
||||
label,
|
||||
undefined,
|
||||
editorOptions,
|
||||
options ? typeConverters.fromViewColumn(options.viewColumn) : undefined
|
||||
]);
|
||||
}, {
|
||||
description: 'Opens the provided resources in the diff editor to compare their contents.',
|
||||
args: [
|
||||
{ name: 'left', description: 'Left-hand side resource of the diff editor', constraint: URI },
|
||||
{ name: 'right', description: 'Right-hand side resource of the diff editor', constraint: URI },
|
||||
{ name: 'title', description: '(optional) Human readable title for the diff editor', constraint: v => v === void 0 || typeof v === 'string' },
|
||||
{ name: 'options', description: '(optional) Editor options, see vscode.TextDocumentShowOptions' }
|
||||
]
|
||||
});
|
||||
|
||||
this._register('vscode.open', (resource: URI, column: vscode.ViewColumn) => {
|
||||
return this._commands.executeCommand('_workbench.open', [resource, typeConverters.fromViewColumn(column)]);
|
||||
}, {
|
||||
description: 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.',
|
||||
args: [
|
||||
{ name: 'resource', description: 'Resource to open', constraint: URI },
|
||||
{ name: 'column', description: '(optional) Column in which to open', constraint: v => v === void 0 || typeof v === 'number' }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// --- command impl
|
||||
|
||||
private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void {
|
||||
let disposable = this._commands.registerCommand(id, handler, this, description);
|
||||
this._disposables.push(disposable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute workspace symbol provider.
|
||||
*
|
||||
* @param query Search string to match query symbol names
|
||||
* @return A promise that resolves to an array of symbol information.
|
||||
*/
|
||||
private _executeWorkspaceSymbolProvider(query: string): Thenable<types.SymbolInformation[]> {
|
||||
return this._commands.executeCommand<[IWorkspaceSymbolProvider, modes.SymbolInformation[]][]>('_executeWorkspaceSymbolProvider', { query }).then(value => {
|
||||
const result: types.SymbolInformation[] = [];
|
||||
if (Array.isArray(value)) {
|
||||
for (let tuple of value) {
|
||||
result.push(...tuple[1].map(typeConverters.toSymbolInformation));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeDefinitionProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
|
||||
const args = {
|
||||
resource,
|
||||
position: position && typeConverters.fromPosition(position)
|
||||
};
|
||||
return this._commands.executeCommand<modes.Location[]>('_executeDefinitionProvider', args).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(typeConverters.location.to);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeImplementationProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
|
||||
const args = {
|
||||
resource,
|
||||
position: position && typeConverters.fromPosition(position)
|
||||
};
|
||||
return this._commands.executeCommand<modes.Location[]>('_executeImplementationProvider', args).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(typeConverters.location.to);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeHoverProvider(resource: URI, position: types.Position): Thenable<types.Hover[]> {
|
||||
const args = {
|
||||
resource,
|
||||
position: position && typeConverters.fromPosition(position)
|
||||
};
|
||||
return this._commands.executeCommand<modes.Hover[]>('_executeHoverProvider', args).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(typeConverters.toHover);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeDocumentHighlights(resource: URI, position: types.Position): Thenable<types.DocumentHighlight[]> {
|
||||
const args = {
|
||||
resource,
|
||||
position: position && typeConverters.fromPosition(position)
|
||||
};
|
||||
return this._commands.executeCommand<modes.DocumentHighlight[]>('_executeDocumentHighlights', args).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(typeConverters.toDocumentHighlight);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeReferenceProvider(resource: URI, position: types.Position): Thenable<types.Location[]> {
|
||||
const args = {
|
||||
resource,
|
||||
position: position && typeConverters.fromPosition(position)
|
||||
};
|
||||
return this._commands.executeCommand<modes.Location[]>('_executeReferenceProvider', args).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(typeConverters.location.to);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeDocumentRenameProvider(resource: URI, position: types.Position, newName: string): Thenable<types.WorkspaceEdit> {
|
||||
const args = {
|
||||
resource,
|
||||
position: position && typeConverters.fromPosition(position),
|
||||
newName
|
||||
};
|
||||
return this._commands.executeCommand<modes.WorkspaceEdit>('_executeDocumentRenameProvider', args).then(value => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
if (value.rejectReason) {
|
||||
return TPromise.wrapError<types.WorkspaceEdit>(new Error(value.rejectReason));
|
||||
}
|
||||
let workspaceEdit = new types.WorkspaceEdit();
|
||||
for (let edit of value.edits) {
|
||||
workspaceEdit.replace(edit.resource, typeConverters.toRange(edit.range), edit.newText);
|
||||
}
|
||||
return workspaceEdit;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Thenable<types.SignatureHelp> {
|
||||
const args = {
|
||||
resource,
|
||||
position: position && typeConverters.fromPosition(position),
|
||||
triggerCharacter
|
||||
};
|
||||
return this._commands.executeCommand<modes.SignatureHelp>('_executeSignatureHelpProvider', args).then(value => {
|
||||
if (value) {
|
||||
return typeConverters.SignatureHelp.to(value);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string): Thenable<types.CompletionList> {
|
||||
const args = {
|
||||
resource,
|
||||
position: position && typeConverters.fromPosition(position),
|
||||
triggerCharacter
|
||||
};
|
||||
return this._commands.executeCommand<modes.ISuggestResult>('_executeCompletionItemProvider', args).then(result => {
|
||||
if (result) {
|
||||
const items = result.suggestions.map(suggestion => typeConverters.Suggest.to(position, suggestion));
|
||||
return new types.CompletionList(items, result.incomplete);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeDocumentSymbolProvider(resource: URI): Thenable<types.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.toSymbolInformation);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeCodeActionProvider(resource: URI, range: types.Range): Thenable<vscode.Command[]> {
|
||||
const args = {
|
||||
resource,
|
||||
range: typeConverters.fromRange(range)
|
||||
};
|
||||
return this._commands.executeCommand<modes.Command[]>('_executeCodeActionProvider', args).then(value => {
|
||||
if (!Array.isArray(value)) {
|
||||
return undefined;
|
||||
}
|
||||
return value.map(quickFix => this._commands.converter.fromInternal(quickFix));
|
||||
});
|
||||
}
|
||||
|
||||
private _executeCodeLensProvider(resource: URI): Thenable<vscode.CodeLens[]> {
|
||||
const args = { resource };
|
||||
return this._commands.executeCommand<modes.ICodeLensSymbol[]>('_executeCodeLensProvider', args).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(item => {
|
||||
return new types.CodeLens(
|
||||
typeConverters.toRange(item.range),
|
||||
this._commands.converter.fromInternal(item.command));
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
|
||||
const args = {
|
||||
resource,
|
||||
options
|
||||
};
|
||||
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatDocumentProvider', args).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text));
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeFormatRangeProvider(resource: URI, range: types.Range, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
|
||||
const args = {
|
||||
resource,
|
||||
range: typeConverters.fromRange(range),
|
||||
options
|
||||
};
|
||||
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatRangeProvider', args).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text));
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeFormatOnTypeProvider(resource: URI, position: types.Position, ch: string, options: vscode.FormattingOptions): Thenable<vscode.TextEdit[]> {
|
||||
const args = {
|
||||
resource,
|
||||
position: typeConverters.fromPosition(position),
|
||||
ch,
|
||||
options
|
||||
};
|
||||
return this._commands.executeCommand<ISingleEditOperation[]>('_executeFormatOnTypeProvider', args).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(edit => new types.TextEdit(typeConverters.toRange(edit.range), edit.text));
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _executeDocumentLinkProvider(resource: URI): Thenable<vscode.DocumentLink[]> {
|
||||
return this._commands.executeCommand<modes.ILink[]>('_executeLinkProvider', resource).then(value => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(typeConverters.DocumentLink.to);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
224
src/vs/workbench/api/node/extHostCommands.ts
Normal file
224
src/vs/workbench/api/node/extHostCommands.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { validateConstraint } from 'vs/base/common/types';
|
||||
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
|
||||
import * as extHostTypeConverter from 'vs/workbench/api/node/extHostTypeConverters';
|
||||
import { cloneAndChange } from 'vs/base/common/objects';
|
||||
import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ObjectIdentifier, IMainContext } from './extHost.protocol';
|
||||
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
interface CommandHandler {
|
||||
callback: Function;
|
||||
thisArg: any;
|
||||
description: ICommandHandlerDescription;
|
||||
}
|
||||
|
||||
export interface ArgumentProcessor {
|
||||
processArgument(arg: any): any;
|
||||
}
|
||||
|
||||
export class ExtHostCommands implements ExtHostCommandsShape {
|
||||
|
||||
private _commands = new Map<string, CommandHandler>();
|
||||
private _proxy: MainThreadCommandsShape;
|
||||
private _converter: CommandsConverter;
|
||||
private _argumentProcessors: ArgumentProcessor[] = [];
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
heapService: ExtHostHeapService
|
||||
) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadCommands);
|
||||
this._converter = new CommandsConverter(this, heapService);
|
||||
}
|
||||
|
||||
get converter(): CommandsConverter {
|
||||
return this._converter;
|
||||
}
|
||||
|
||||
registerArgumentProcessor(processor: ArgumentProcessor): void {
|
||||
this._argumentProcessors.push(processor);
|
||||
}
|
||||
|
||||
registerCommand(id: string, callback: <T>(...args: any[]) => T | Thenable<T>, thisArg?: any, description?: ICommandHandlerDescription): extHostTypes.Disposable {
|
||||
|
||||
if (!id.trim().length) {
|
||||
throw new Error('invalid id');
|
||||
}
|
||||
|
||||
if (this._commands.has(id)) {
|
||||
throw new Error(`command '${id}' already exists`);
|
||||
}
|
||||
|
||||
this._commands.set(id, { callback, thisArg, description });
|
||||
this._proxy.$registerCommand(id);
|
||||
|
||||
return new extHostTypes.Disposable(() => {
|
||||
if (this._commands.delete(id)) {
|
||||
this._proxy.$unregisterCommand(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executeCommand<T>(id: string, ...args: any[]): Thenable<T> {
|
||||
|
||||
if (this._commands.has(id)) {
|
||||
// we stay inside the extension host and support
|
||||
// to pass any kind of parameters around
|
||||
return this.$executeContributedCommand<T>(id, ...args);
|
||||
|
||||
} else {
|
||||
// automagically convert some argument types
|
||||
|
||||
args = cloneAndChange(args, function (value) {
|
||||
if (value instanceof extHostTypes.Position) {
|
||||
return extHostTypeConverter.fromPosition(value);
|
||||
}
|
||||
if (value instanceof extHostTypes.Range) {
|
||||
return extHostTypeConverter.fromRange(value);
|
||||
}
|
||||
if (value instanceof extHostTypes.Location) {
|
||||
return extHostTypeConverter.location.from(value);
|
||||
}
|
||||
if (!Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
|
||||
return this._proxy.$executeCommand<T>(id, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$executeContributedCommand<T>(id: string, ...args: any[]): Thenable<T> {
|
||||
let command = this._commands.get(id);
|
||||
if (!command) {
|
||||
return TPromise.wrapError<T>(new Error(`Contributed command '${id}' does not exist.`));
|
||||
}
|
||||
|
||||
let { callback, thisArg, description } = command;
|
||||
|
||||
if (description) {
|
||||
for (let i = 0; i < description.args.length; i++) {
|
||||
try {
|
||||
validateConstraint(args[i], description.args[i].constraint);
|
||||
} catch (err) {
|
||||
return TPromise.wrapError<T>(new Error(`Running the contributed command:'${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
args = args.map(arg => this._argumentProcessors.reduce((r, p) => p.processArgument(r), arg));
|
||||
|
||||
try {
|
||||
let result = callback.apply(thisArg, args);
|
||||
return TPromise.as(result);
|
||||
} catch (err) {
|
||||
// console.log(err);
|
||||
// try {
|
||||
// console.log(toErrorMessage(err));
|
||||
// } catch (err) {
|
||||
// //
|
||||
// }
|
||||
return TPromise.wrapError<T>(new Error(`Running the contributed command:'${id}' failed.`));
|
||||
}
|
||||
}
|
||||
|
||||
getCommands(filterUnderscoreCommands: boolean = false): Thenable<string[]> {
|
||||
return this._proxy.$getCommands().then(result => {
|
||||
if (filterUnderscoreCommands) {
|
||||
result = result.filter(command => command[0] !== '_');
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
$getContributedCommandHandlerDescriptions(): TPromise<{ [id: string]: string | ICommandHandlerDescription }> {
|
||||
const result: { [id: string]: string | ICommandHandlerDescription } = Object.create(null);
|
||||
this._commands.forEach((command, id) => {
|
||||
let { description } = command;
|
||||
if (description) {
|
||||
result[id] = description;
|
||||
}
|
||||
});
|
||||
return TPromise.as(result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class CommandsConverter {
|
||||
|
||||
private _commands: ExtHostCommands;
|
||||
private _heap: ExtHostHeapService;
|
||||
|
||||
// --- conversion between internal and api commands
|
||||
constructor(commands: ExtHostCommands, heap: ExtHostHeapService) {
|
||||
|
||||
this._commands = commands;
|
||||
this._heap = heap;
|
||||
this._commands.registerCommand('_internal_command_delegation', this._executeConvertedCommand, this);
|
||||
}
|
||||
|
||||
toInternal(command: vscode.Command): modes.Command {
|
||||
|
||||
if (!command) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result: modes.Command = {
|
||||
id: command.command,
|
||||
title: command.title
|
||||
};
|
||||
|
||||
if (command.command && !isFalsyOrEmpty(command.arguments)) {
|
||||
// we have a contributed command with arguments. that
|
||||
// means we don't want to send the arguments around
|
||||
|
||||
const id = this._heap.keep(command);
|
||||
ObjectIdentifier.mixin(result, id);
|
||||
|
||||
result.id = '_internal_command_delegation';
|
||||
result.arguments = [id];
|
||||
}
|
||||
|
||||
if (command.tooltip) {
|
||||
result.tooltip = command.tooltip;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fromInternal(command: modes.Command): vscode.Command {
|
||||
|
||||
if (!command) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const id = ObjectIdentifier.of(command);
|
||||
if (typeof id === 'number') {
|
||||
return this._heap.get<vscode.Command>(id);
|
||||
|
||||
} else {
|
||||
return {
|
||||
command: command.id,
|
||||
title: command.title,
|
||||
arguments: command.arguments
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private _executeConvertedCommand<R>(...args: any[]): Thenable<R> {
|
||||
const actualCmd = this._heap.get<vscode.Command>(args[0]);
|
||||
return this._commands.executeCommand(actualCmd.command, ...actualCmd.arguments);
|
||||
}
|
||||
|
||||
}
|
||||
120
src/vs/workbench/api/node/extHostConfiguration.ts
Normal file
120
src/vs/workbench/api/node/extHostConfiguration.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { WorkspaceConfiguration } from 'vscode';
|
||||
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
|
||||
import { ExtHostConfigurationShape, MainThreadConfigurationShape } from './extHost.protocol';
|
||||
import { ConfigurationTarget as ExtHostConfigurationTarget } from './extHostTypes';
|
||||
import { IConfigurationData, Configuration } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing';
|
||||
|
||||
function lookUp(tree: any, key: string) {
|
||||
if (key) {
|
||||
const parts = key.split('.');
|
||||
let node = tree;
|
||||
for (let i = 0; node && i < parts.length; i++) {
|
||||
node = node[parts[i]];
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
type ConfigurationInspect<T> = {
|
||||
key: string;
|
||||
defaultValue?: T;
|
||||
globalValue?: T;
|
||||
workspaceValue?: T;
|
||||
workspaceFolderValue?: T;
|
||||
};
|
||||
|
||||
export class ExtHostConfiguration implements ExtHostConfigurationShape {
|
||||
|
||||
private readonly _onDidChangeConfiguration = new Emitter<void>();
|
||||
private readonly _proxy: MainThreadConfigurationShape;
|
||||
private readonly _extHostWorkspace: ExtHostWorkspace;
|
||||
private _configuration: Configuration<any>;
|
||||
|
||||
constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationData<any>) {
|
||||
this._proxy = proxy;
|
||||
this._extHostWorkspace = extHostWorkspace;
|
||||
this._configuration = Configuration.parse(data, extHostWorkspace.workspace);
|
||||
}
|
||||
|
||||
get onDidChangeConfiguration(): Event<void> {
|
||||
return this._onDidChangeConfiguration && this._onDidChangeConfiguration.event;
|
||||
}
|
||||
|
||||
$acceptConfigurationChanged(data: IConfigurationData<any>) {
|
||||
this._configuration = Configuration.parse(data, this._extHostWorkspace.workspace);
|
||||
this._onDidChangeConfiguration.fire(undefined);
|
||||
}
|
||||
|
||||
getConfiguration(section?: string, resource?: URI): WorkspaceConfiguration {
|
||||
const config = section
|
||||
? lookUp(this._configuration.getValue(null, { resource }), section)
|
||||
: this._configuration.getValue(null, { resource });
|
||||
|
||||
function parseConfigurationTarget(arg: boolean | ExtHostConfigurationTarget): ConfigurationTarget {
|
||||
if (arg === void 0 || arg === null) {
|
||||
return null;
|
||||
}
|
||||
if (typeof arg === 'boolean') {
|
||||
return arg ? ConfigurationTarget.USER : ConfigurationTarget.WORKSPACE;
|
||||
}
|
||||
|
||||
switch (arg) {
|
||||
case ExtHostConfigurationTarget.Global: return ConfigurationTarget.USER;
|
||||
case ExtHostConfigurationTarget.Workspace: return ConfigurationTarget.WORKSPACE;
|
||||
case ExtHostConfigurationTarget.WorkspaceFolder: return ConfigurationTarget.FOLDER;
|
||||
}
|
||||
}
|
||||
|
||||
const result: WorkspaceConfiguration = {
|
||||
has(key: string): boolean {
|
||||
return typeof lookUp(config, key) !== 'undefined';
|
||||
},
|
||||
get<T>(key: string, defaultValue?: T): T {
|
||||
let result = lookUp(config, key);
|
||||
if (typeof result === 'undefined') {
|
||||
result = defaultValue;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
update: (key: string, value: any, arg: ExtHostConfigurationTarget | boolean) => {
|
||||
key = section ? `${section}.${key}` : key;
|
||||
const target = parseConfigurationTarget(arg);
|
||||
if (value !== void 0) {
|
||||
return this._proxy.$updateConfigurationOption(target, key, value, resource);
|
||||
} else {
|
||||
return this._proxy.$removeConfigurationOption(target, key, resource);
|
||||
}
|
||||
},
|
||||
inspect: <T>(key: string): ConfigurationInspect<T> => {
|
||||
key = section ? `${section}.${key}` : key;
|
||||
const config = this._configuration.lookup<T>(key, { resource });
|
||||
if (config) {
|
||||
return {
|
||||
key,
|
||||
defaultValue: config.default,
|
||||
globalValue: config.user,
|
||||
workspaceValue: config.workspace,
|
||||
workspaceFolderValue: config.folder
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof config === 'object') {
|
||||
mixin(result, config, false);
|
||||
}
|
||||
|
||||
return <WorkspaceConfiguration>Object.freeze(result);
|
||||
}
|
||||
}
|
||||
29
src/vs/workbench/api/node/extHostCredentials.ts
Normal file
29
src/vs/workbench/api/node/extHostCredentials.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { MainContext, MainThreadCredentialsShape, ExtHostCredentialsShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
|
||||
|
||||
export class ExtHostCredentials implements ExtHostCredentialsShape {
|
||||
|
||||
private _proxy: MainThreadCredentialsShape;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadCredentials);
|
||||
};
|
||||
|
||||
readSecret(service: string, account: string): Thenable<string | undefined> {
|
||||
return this._proxy.$readSecret(service, account);
|
||||
}
|
||||
|
||||
writeSecret(service: string, account: string, secret: string): Thenable<void> {
|
||||
return this._proxy.$writeSecret(service, account, secret);
|
||||
}
|
||||
|
||||
deleteSecret(service: string, account: string): Thenable<boolean> {
|
||||
return this._proxy.$deleteSecret(service, account);
|
||||
}
|
||||
}
|
||||
213
src/vs/workbench/api/node/extHostDebugService.ts
Normal file
213
src/vs/workbench/api/node/extHostDebugService.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
/*
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { asWinJsPromise } from 'vs/base/common/async';
|
||||
import { MainContext, MainThreadDebugServiceShape, ExtHostDebugServiceShape, DebugSessionUUID, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import * as types from 'vs/workbench/api/node/extHostTypes';
|
||||
|
||||
|
||||
export class ExtHostDebugService implements ExtHostDebugServiceShape {
|
||||
|
||||
private _workspace: ExtHostWorkspace;
|
||||
|
||||
private _handleCounter: number;
|
||||
private _handlers: Map<number, vscode.DebugConfigurationProvider>;
|
||||
|
||||
private _debugServiceProxy: MainThreadDebugServiceShape;
|
||||
private _debugSessions: Map<DebugSessionUUID, ExtHostDebugSession> = new Map<DebugSessionUUID, ExtHostDebugSession>();
|
||||
|
||||
private _onDidStartDebugSession: Emitter<vscode.DebugSession>;
|
||||
get onDidStartDebugSession(): Event<vscode.DebugSession> { return this._onDidStartDebugSession.event; }
|
||||
|
||||
private _onDidTerminateDebugSession: Emitter<vscode.DebugSession>;
|
||||
get onDidTerminateDebugSession(): Event<vscode.DebugSession> { return this._onDidTerminateDebugSession.event; }
|
||||
|
||||
private _onDidChangeActiveDebugSession: Emitter<vscode.DebugSession | undefined>;
|
||||
get onDidChangeActiveDebugSession(): Event<vscode.DebugSession | undefined> { return this._onDidChangeActiveDebugSession.event; }
|
||||
|
||||
private _activeDebugSession: ExtHostDebugSession | undefined;
|
||||
get activeDebugSession(): ExtHostDebugSession | undefined { return this._activeDebugSession; }
|
||||
|
||||
private _onDidReceiveDebugSessionCustomEvent: Emitter<vscode.DebugSessionCustomEvent>;
|
||||
get onDidReceiveDebugSessionCustomEvent(): Event<vscode.DebugSessionCustomEvent> { return this._onDidReceiveDebugSessionCustomEvent.event; }
|
||||
|
||||
|
||||
constructor(mainContext: IMainContext, workspace: ExtHostWorkspace) {
|
||||
|
||||
this._workspace = workspace;
|
||||
|
||||
this._handleCounter = 0;
|
||||
this._handlers = new Map<number, vscode.DebugConfigurationProvider>();
|
||||
|
||||
this._onDidStartDebugSession = new Emitter<vscode.DebugSession>();
|
||||
this._onDidTerminateDebugSession = new Emitter<vscode.DebugSession>();
|
||||
this._onDidChangeActiveDebugSession = new Emitter<vscode.DebugSession>();
|
||||
this._onDidReceiveDebugSessionCustomEvent = new Emitter<vscode.DebugSessionCustomEvent>();
|
||||
|
||||
this._debugServiceProxy = mainContext.get(MainContext.MainThreadDebugService);
|
||||
}
|
||||
|
||||
public registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable {
|
||||
if (!provider) {
|
||||
return new types.Disposable(() => { });
|
||||
}
|
||||
|
||||
let handle = this.nextHandle();
|
||||
this._handlers.set(handle, provider);
|
||||
this._debugServiceProxy.$registerDebugConfigurationProvider(type, !!provider.provideDebugConfigurations, !!provider.resolveDebugConfiguration, handle);
|
||||
|
||||
return new types.Disposable(() => {
|
||||
this._handlers.delete(handle);
|
||||
this._debugServiceProxy.$unregisterDebugConfigurationProvider(handle);
|
||||
});
|
||||
}
|
||||
|
||||
public $provideDebugConfigurations(handle: number, folderUri: URI | undefined): TPromise<vscode.DebugConfiguration[]> {
|
||||
let handler = this._handlers.get(handle);
|
||||
if (!handler) {
|
||||
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('no handler found'));
|
||||
}
|
||||
if (!handler.provideDebugConfigurations) {
|
||||
return TPromise.wrapError<vscode.DebugConfiguration[]>(new Error('handler has no method provideDebugConfigurations'));
|
||||
}
|
||||
return asWinJsPromise(token => handler.provideDebugConfigurations(this.getFolder(folderUri), token));
|
||||
}
|
||||
|
||||
|
||||
public $resolveDebugConfiguration(handle: number, folderUri: URI | undefined, debugConfiguration: vscode.DebugConfiguration): TPromise<vscode.DebugConfiguration> {
|
||||
let handler = this._handlers.get(handle);
|
||||
if (!handler) {
|
||||
return TPromise.wrapError<vscode.DebugConfiguration>(new Error('no handler found'));
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration): TPromise<boolean> {
|
||||
|
||||
return this._debugServiceProxy.$startDebugging(folder ? <URI>folder.uri : undefined, nameOrConfig);
|
||||
}
|
||||
|
||||
public startDebugSession(folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration): TPromise<vscode.DebugSession> {
|
||||
|
||||
return this._debugServiceProxy.$startDebugSession(folder ? <URI>folder.uri : undefined, config).then((id: DebugSessionUUID) => {
|
||||
const debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, config.type, config.name);
|
||||
this._debugSessions.set(id, debugSession);
|
||||
return debugSession;
|
||||
});
|
||||
}
|
||||
|
||||
public $acceptDebugSessionStarted(id: DebugSessionUUID, type: string, name: string): void {
|
||||
|
||||
let debugSession = this._debugSessions.get(id);
|
||||
if (!debugSession) {
|
||||
debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
|
||||
this._debugSessions.set(id, debugSession);
|
||||
}
|
||||
this._onDidStartDebugSession.fire(debugSession);
|
||||
}
|
||||
|
||||
public $acceptDebugSessionTerminated(id: DebugSessionUUID, type: string, name: string): void {
|
||||
|
||||
let debugSession = this._debugSessions.get(id);
|
||||
if (!debugSession) {
|
||||
debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
|
||||
this._debugSessions.set(id, debugSession);
|
||||
}
|
||||
this._onDidTerminateDebugSession.fire(debugSession);
|
||||
this._debugSessions.delete(id);
|
||||
}
|
||||
|
||||
public $acceptDebugSessionActiveChanged(id: DebugSessionUUID | undefined, type?: string, name?: string): void {
|
||||
|
||||
if (id) {
|
||||
this._activeDebugSession = this._debugSessions.get(id);
|
||||
if (!this._activeDebugSession) {
|
||||
this._activeDebugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
|
||||
this._debugSessions.set(id, this._activeDebugSession);
|
||||
}
|
||||
} else {
|
||||
this._activeDebugSession = undefined;
|
||||
}
|
||||
this._onDidChangeActiveDebugSession.fire(this._activeDebugSession);
|
||||
}
|
||||
|
||||
public $acceptDebugSessionCustomEvent(id: DebugSessionUUID, type: string, name: string, event: any): void {
|
||||
|
||||
let debugSession = this._debugSessions.get(id);
|
||||
if (!debugSession) {
|
||||
debugSession = new ExtHostDebugSession(this._debugServiceProxy, id, type, name);
|
||||
this._debugSessions.set(id, debugSession);
|
||||
}
|
||||
const ee: vscode.DebugSessionCustomEvent = {
|
||||
session: debugSession,
|
||||
event: event.event,
|
||||
body: event.body
|
||||
};
|
||||
this._onDidReceiveDebugSessionCustomEvent.fire(ee);
|
||||
}
|
||||
|
||||
private getFolder(folderUri: URI | undefined) {
|
||||
if (folderUri) {
|
||||
const folders = this._workspace.getWorkspaceFolders();
|
||||
const found = folders.filter(f => f.uri.toString() === folderUri.toString());
|
||||
if (found && found.length > 0) {
|
||||
return found[0];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private nextHandle(): number {
|
||||
return this._handleCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostDebugSession implements vscode.DebugSession {
|
||||
|
||||
private _debugServiceProxy: MainThreadDebugServiceShape;
|
||||
|
||||
private _id: DebugSessionUUID;
|
||||
|
||||
private _type: string;
|
||||
private _name: string;
|
||||
|
||||
constructor(proxy: MainThreadDebugServiceShape, id: DebugSessionUUID, type: string, name: string) {
|
||||
this._debugServiceProxy = proxy;
|
||||
this._id = id;
|
||||
this._type = type;
|
||||
this._name = name;
|
||||
};
|
||||
|
||||
public get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get type(): string {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public customRequest(command: string, args: any): Thenable<any> {
|
||||
return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args);
|
||||
}
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
*/
|
||||
258
src/vs/workbench/api/node/extHostDiagnostics.ts
Normal file
258
src/vs/workbench/api/node/extHostDiagnostics.ts
Normal file
@@ -0,0 +1,258 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import * as vscode from 'vscode';
|
||||
import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol';
|
||||
import { DiagnosticSeverity } from './extHostTypes';
|
||||
import { mergeSort } from 'vs/base/common/arrays';
|
||||
|
||||
export class DiagnosticCollection implements vscode.DiagnosticCollection {
|
||||
|
||||
private static readonly _maxDiagnosticsPerFile: number = 250;
|
||||
|
||||
private readonly _name: string;
|
||||
|
||||
private _proxy: MainThreadDiagnosticsShape;
|
||||
private _isDisposed = false;
|
||||
private _data = new Map<string, vscode.Diagnostic[]>();
|
||||
|
||||
constructor(name: string, proxy: MainThreadDiagnosticsShape) {
|
||||
this._name = name;
|
||||
this._proxy = proxy;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
if (!this._isDisposed) {
|
||||
this._proxy.$clear(this.name);
|
||||
this._proxy = undefined;
|
||||
this._data = undefined;
|
||||
this._isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
this._checkDisposed();
|
||||
return this._name;
|
||||
}
|
||||
|
||||
set(uri: vscode.Uri, diagnostics: vscode.Diagnostic[]): void;
|
||||
set(entries: [vscode.Uri, vscode.Diagnostic[]][]): void;
|
||||
set(first: vscode.Uri | [vscode.Uri, vscode.Diagnostic[]][], diagnostics?: vscode.Diagnostic[]) {
|
||||
|
||||
if (!first) {
|
||||
// this set-call is a clear-call
|
||||
this.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// the actual implementation for #set
|
||||
|
||||
this._checkDisposed();
|
||||
let toSync: vscode.Uri[];
|
||||
|
||||
if (first instanceof URI) {
|
||||
|
||||
if (!diagnostics) {
|
||||
// remove this entry
|
||||
this.delete(first);
|
||||
return;
|
||||
}
|
||||
|
||||
// update single row
|
||||
this._data.set(first.toString(), diagnostics);
|
||||
toSync = [first];
|
||||
|
||||
} else if (Array.isArray(first)) {
|
||||
// update many rows
|
||||
toSync = [];
|
||||
let lastUri: vscode.Uri;
|
||||
|
||||
// ensure stable-sort
|
||||
mergeSort(first, DiagnosticCollection._compareIndexedTuplesByUri);
|
||||
|
||||
for (const tuple of first) {
|
||||
const [uri, diagnostics] = tuple;
|
||||
if (!lastUri || uri.toString() !== lastUri.toString()) {
|
||||
if (lastUri && this._data.get(lastUri.toString()).length === 0) {
|
||||
this._data.delete(lastUri.toString());
|
||||
}
|
||||
lastUri = uri;
|
||||
toSync.push(uri);
|
||||
this._data.set(uri.toString(), []);
|
||||
}
|
||||
|
||||
if (!diagnostics) {
|
||||
// [Uri, undefined] means clear this
|
||||
this._data.get(uri.toString()).length = 0;
|
||||
} else {
|
||||
this._data.get(uri.toString()).push(...diagnostics);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compute change and send to main side
|
||||
const entries: [URI, IMarkerData[]][] = [];
|
||||
for (let uri of toSync) {
|
||||
let marker: IMarkerData[];
|
||||
let diagnostics = this._data.get(uri.toString());
|
||||
if (diagnostics) {
|
||||
|
||||
// no more than 250 diagnostics per file
|
||||
if (diagnostics.length > DiagnosticCollection._maxDiagnosticsPerFile) {
|
||||
marker = [];
|
||||
const order = [DiagnosticSeverity.Error, DiagnosticSeverity.Warning, DiagnosticSeverity.Information, DiagnosticSeverity.Hint];
|
||||
orderLoop: for (let i = 0; i < 4; i++) {
|
||||
for (let diagnostic of diagnostics) {
|
||||
if (diagnostic.severity === order[i]) {
|
||||
const len = marker.push(DiagnosticCollection._toMarkerData(diagnostic));
|
||||
if (len === DiagnosticCollection._maxDiagnosticsPerFile) {
|
||||
break orderLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add 'signal' marker for showing omitted errors/warnings
|
||||
marker.push({
|
||||
severity: Severity.Error,
|
||||
message: localize({ key: 'limitHit', comment: ['amount of errors/warning skipped due to limits'] }, "Not showing {0} further errors and warnings.", diagnostics.length - DiagnosticCollection._maxDiagnosticsPerFile),
|
||||
startLineNumber: marker[marker.length - 1].startLineNumber,
|
||||
startColumn: marker[marker.length - 1].startColumn,
|
||||
endLineNumber: marker[marker.length - 1].endLineNumber,
|
||||
endColumn: marker[marker.length - 1].endColumn
|
||||
});
|
||||
} else {
|
||||
marker = diagnostics.map(DiagnosticCollection._toMarkerData);
|
||||
}
|
||||
}
|
||||
|
||||
entries.push([<URI>uri, marker]);
|
||||
}
|
||||
|
||||
this._proxy.$changeMany(this.name, entries);
|
||||
}
|
||||
|
||||
delete(uri: vscode.Uri): void {
|
||||
this._checkDisposed();
|
||||
this._data.delete(uri.toString());
|
||||
this._proxy.$changeMany(this.name, [[<URI>uri, undefined]]);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this._checkDisposed();
|
||||
this._data.clear();
|
||||
this._proxy.$clear(this.name);
|
||||
}
|
||||
|
||||
forEach(callback: (uri: URI, diagnostics: vscode.Diagnostic[], collection: DiagnosticCollection) => any, thisArg?: any): void {
|
||||
this._checkDisposed();
|
||||
this._data.forEach((value, key) => {
|
||||
let uri = URI.parse(key);
|
||||
callback.apply(thisArg, [uri, this.get(uri), this]);
|
||||
});
|
||||
}
|
||||
|
||||
get(uri: URI): vscode.Diagnostic[] {
|
||||
this._checkDisposed();
|
||||
let result = this._data.get(uri.toString());
|
||||
if (Array.isArray(result)) {
|
||||
return <vscode.Diagnostic[]>Object.freeze(result.slice(0));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
has(uri: URI): boolean {
|
||||
this._checkDisposed();
|
||||
return Array.isArray(this._data.get(uri.toString()));
|
||||
}
|
||||
|
||||
private _checkDisposed() {
|
||||
if (this._isDisposed) {
|
||||
throw new Error('illegal state - object is disposed');
|
||||
}
|
||||
}
|
||||
|
||||
private static _toMarkerData(diagnostic: vscode.Diagnostic): IMarkerData {
|
||||
|
||||
let range = diagnostic.range;
|
||||
|
||||
return <IMarkerData>{
|
||||
startLineNumber: range.start.line + 1,
|
||||
startColumn: range.start.character + 1,
|
||||
endLineNumber: range.end.line + 1,
|
||||
endColumn: range.end.character + 1,
|
||||
message: diagnostic.message,
|
||||
source: diagnostic.source,
|
||||
severity: DiagnosticCollection._convertDiagnosticsSeverity(diagnostic.severity),
|
||||
code: String(diagnostic.code)
|
||||
};
|
||||
}
|
||||
|
||||
private static _convertDiagnosticsSeverity(severity: number): Severity {
|
||||
switch (severity) {
|
||||
case 0: return Severity.Error;
|
||||
case 1: return Severity.Warning;
|
||||
case 2: return Severity.Info;
|
||||
case 3: return Severity.Ignore;
|
||||
default: return Severity.Error;
|
||||
}
|
||||
}
|
||||
|
||||
private static _compareIndexedTuplesByUri(a: [vscode.Uri, vscode.Diagnostic[]], b: [vscode.Uri, vscode.Diagnostic[]]): number {
|
||||
if (a[0].toString() < b[0].toString()) {
|
||||
return -1;
|
||||
} else if (a[0].toString() > b[0].toString()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
|
||||
|
||||
private static _idPool: number = 0;
|
||||
|
||||
private _proxy: MainThreadDiagnosticsShape;
|
||||
private _collections: DiagnosticCollection[];
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadDiagnostics);
|
||||
this._collections = [];
|
||||
}
|
||||
|
||||
createDiagnosticCollection(name: string): vscode.DiagnosticCollection {
|
||||
if (!name) {
|
||||
name = '_generated_diagnostic_collection_name_#' + ExtHostDiagnostics._idPool++;
|
||||
}
|
||||
|
||||
const { _collections, _proxy } = this;
|
||||
const result = new class extends DiagnosticCollection {
|
||||
constructor() {
|
||||
super(name, _proxy);
|
||||
_collections.push(this);
|
||||
}
|
||||
dispose() {
|
||||
super.dispose();
|
||||
let idx = _collections.indexOf(this);
|
||||
if (idx !== -1) {
|
||||
_collections.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
forEach(callback: (collection: DiagnosticCollection) => any): void {
|
||||
this._collections.forEach(callback);
|
||||
}
|
||||
}
|
||||
|
||||
24
src/vs/workbench/api/node/extHostDialogs.ts
Normal file
24
src/vs/workbench/api/node/extHostDialogs.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { MainContext, MainThreadDiaglogsShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
|
||||
export class ExtHostDialogs {
|
||||
|
||||
private readonly _proxy: MainThreadDiaglogsShape;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadDialogs);
|
||||
}
|
||||
|
||||
showOpenDialog(options: vscode.OpenDialogOptions): Thenable<URI[]> {
|
||||
return this._proxy.$showOpenDialog(<any>options).then(filepaths => {
|
||||
return filepaths && filepaths.map(URI.file);
|
||||
});
|
||||
}
|
||||
}
|
||||
88
src/vs/workbench/api/node/extHostDocumentContentProviders.ts
Normal file
88
src/vs/workbench/api/node/extHostDocumentContentProviders.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import URI 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 { TextSource } from 'vs/editor/common/model/textSource';
|
||||
import { MainContext, ExtHostDocumentContentProvidersShape, MainThreadDocumentContentProvidersShape, IMainContext } from './extHost.protocol';
|
||||
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
|
||||
|
||||
export class ExtHostDocumentContentProvider implements ExtHostDocumentContentProvidersShape {
|
||||
|
||||
private static _handlePool = 0;
|
||||
|
||||
private readonly _documentContentProviders = new Map<number, vscode.TextDocumentContentProvider>();
|
||||
private readonly _proxy: MainThreadDocumentContentProvidersShape;
|
||||
private readonly _documentsAndEditors: ExtHostDocumentsAndEditors;
|
||||
|
||||
constructor(mainContext: IMainContext, documentsAndEditors: ExtHostDocumentsAndEditors) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadDocumentContentProviders);
|
||||
this._documentsAndEditors = documentsAndEditors;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
// todo@joh
|
||||
}
|
||||
|
||||
registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider): vscode.Disposable {
|
||||
if (scheme === 'file' || scheme === 'untitled') {
|
||||
throw new Error(`scheme '${scheme}' already registered`);
|
||||
}
|
||||
|
||||
const handle = ExtHostDocumentContentProvider._handlePool++;
|
||||
|
||||
this._documentContentProviders.set(handle, provider);
|
||||
this._proxy.$registerTextContentProvider(handle, scheme);
|
||||
|
||||
let subscription: IDisposable;
|
||||
if (typeof provider.onDidChange === 'function') {
|
||||
subscription = provider.onDidChange(uri => {
|
||||
if (this._documentsAndEditors.getDocument(uri.toString())) {
|
||||
this.$provideTextDocumentContent(handle, <URI>uri).then(value => {
|
||||
|
||||
const document = this._documentsAndEditors.getDocument(uri.toString());
|
||||
if (!document) {
|
||||
// disposed in the meantime
|
||||
return;
|
||||
}
|
||||
|
||||
// create lines and compare
|
||||
const textSource = TextSource.fromString(value, editorCommon.DefaultEndOfLine.CRLF);
|
||||
|
||||
// broadcast event when content changed
|
||||
if (!document.equalLines(textSource)) {
|
||||
return this._proxy.$onVirtualDocumentChange(<URI>uri, textSource);
|
||||
}
|
||||
|
||||
}, onUnexpectedError);
|
||||
}
|
||||
});
|
||||
}
|
||||
return new Disposable(() => {
|
||||
if (this._documentContentProviders.delete(handle)) {
|
||||
this._proxy.$unregisterTextContentProvider(handle);
|
||||
}
|
||||
if (subscription) {
|
||||
subscription.dispose();
|
||||
subscription = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$provideTextDocumentContent(handle: number, uri: URI): TPromise<string> {
|
||||
const provider = this._documentContentProviders.get(handle);
|
||||
if (!provider) {
|
||||
return TPromise.wrapError<string>(new Error(`unsupported uri-scheme: ${uri.scheme}`));
|
||||
}
|
||||
return asWinJsPromise(token => provider.provideTextDocumentContent(uri, token));
|
||||
}
|
||||
}
|
||||
268
src/vs/workbench/api/node/extHostDocumentData.ts
Normal file
268
src/vs/workbench/api/node/extHostDocumentData.ts
Normal file
@@ -0,0 +1,268 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { ok } from 'vs/base/common/assert';
|
||||
import { regExpLeadsToEndlessLoop } from 'vs/base/common/strings';
|
||||
import { MirrorModel } from 'vs/editor/common/model/mirrorModel';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { Range, Position, EndOfLine } from 'vs/workbench/api/node/extHostTypes';
|
||||
import * as vscode from 'vscode';
|
||||
import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper';
|
||||
import { MainThreadDocumentsShape } from './extHost.protocol';
|
||||
import { ITextSource } from 'vs/editor/common/model/textSource';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
const _modeId2WordDefinition = new Map<string, RegExp>();
|
||||
export function setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void {
|
||||
_modeId2WordDefinition.set(modeId, wordDefinition);
|
||||
}
|
||||
export function getWordDefinitionFor(modeId: string): RegExp {
|
||||
return _modeId2WordDefinition.get(modeId);
|
||||
}
|
||||
|
||||
export class ExtHostDocumentData extends MirrorModel {
|
||||
|
||||
private _proxy: MainThreadDocumentsShape;
|
||||
private _languageId: string;
|
||||
private _isDirty: boolean;
|
||||
private _document: vscode.TextDocument;
|
||||
private _textLines: vscode.TextLine[] = [];
|
||||
private _isDisposed: boolean = false;
|
||||
|
||||
constructor(proxy: MainThreadDocumentsShape, uri: URI, lines: string[], eol: string,
|
||||
languageId: string, versionId: number, isDirty: boolean
|
||||
) {
|
||||
super(uri, lines, eol, versionId);
|
||||
this._proxy = proxy;
|
||||
this._languageId = languageId;
|
||||
this._isDirty = isDirty;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
// we don't really dispose documents but let
|
||||
// extensions still read from them. some
|
||||
// operations, live saving, will now error tho
|
||||
ok(!this._isDisposed);
|
||||
this._isDisposed = true;
|
||||
this._isDirty = false;
|
||||
}
|
||||
|
||||
equalLines({ lines }: ITextSource): boolean {
|
||||
const len = lines.length;
|
||||
if (len !== this._lines.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (lines[i] !== this._lines[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
get document(): vscode.TextDocument {
|
||||
if (!this._document) {
|
||||
const data = this;
|
||||
this._document = {
|
||||
get uri() { return data._uri; },
|
||||
get fileName() { return data._uri.fsPath; },
|
||||
get isUntitled() { return data._uri.scheme !== 'file'; },
|
||||
get languageId() { return data._languageId; },
|
||||
get version() { return data._versionId; },
|
||||
get isClosed() { return data._isDisposed; },
|
||||
get isDirty() { return data._isDirty; },
|
||||
save() { return data._save(); },
|
||||
getText(range?) { return range ? data._getTextInRange(range) : data.getText(); },
|
||||
get eol() { return data._eol === '\n' ? EndOfLine.LF : EndOfLine.CRLF; },
|
||||
get lineCount() { return data._lines.length; },
|
||||
lineAt(lineOrPos) { return data._lineAt(lineOrPos); },
|
||||
offsetAt(pos) { return data._offsetAt(pos); },
|
||||
positionAt(offset) { return data._positionAt(offset); },
|
||||
validateRange(ran) { return data._validateRange(ran); },
|
||||
validatePosition(pos) { return data._validatePosition(pos); },
|
||||
getWordRangeAtPosition(pos, regexp?) { return data._getWordRangeAtPosition(pos, regexp); }
|
||||
};
|
||||
}
|
||||
return Object.freeze(this._document);
|
||||
}
|
||||
|
||||
_acceptLanguageId(newLanguageId: string): void {
|
||||
ok(!this._isDisposed);
|
||||
this._languageId = newLanguageId;
|
||||
}
|
||||
|
||||
_acceptIsDirty(isDirty: boolean): void {
|
||||
ok(!this._isDisposed);
|
||||
this._isDirty = isDirty;
|
||||
}
|
||||
|
||||
private _save(): TPromise<boolean> {
|
||||
if (this._isDisposed) {
|
||||
return TPromise.wrapError<boolean>(new Error('Document has been closed'));
|
||||
}
|
||||
return this._proxy.$trySaveDocument(this._uri);
|
||||
}
|
||||
|
||||
private _getTextInRange(_range: vscode.Range): string {
|
||||
let range = this._validateRange(_range);
|
||||
|
||||
if (range.isEmpty) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (range.isSingleLine) {
|
||||
return this._lines[range.start.line].substring(range.start.character, range.end.character);
|
||||
}
|
||||
|
||||
let lineEnding = this._eol,
|
||||
startLineIndex = range.start.line,
|
||||
endLineIndex = range.end.line,
|
||||
resultLines: string[] = [];
|
||||
|
||||
resultLines.push(this._lines[startLineIndex].substring(range.start.character));
|
||||
for (let i = startLineIndex + 1; i < endLineIndex; i++) {
|
||||
resultLines.push(this._lines[i]);
|
||||
}
|
||||
resultLines.push(this._lines[endLineIndex].substring(0, range.end.character));
|
||||
|
||||
return resultLines.join(lineEnding);
|
||||
}
|
||||
|
||||
private _lineAt(lineOrPosition: number | vscode.Position): vscode.TextLine {
|
||||
|
||||
let line: number;
|
||||
if (lineOrPosition instanceof Position) {
|
||||
line = lineOrPosition.line;
|
||||
} else if (typeof lineOrPosition === 'number') {
|
||||
line = lineOrPosition;
|
||||
}
|
||||
|
||||
if (line < 0 || line >= this._lines.length) {
|
||||
throw new Error('Illegal value for `line`');
|
||||
}
|
||||
|
||||
let result = this._textLines[line];
|
||||
if (!result || result.lineNumber !== line || result.text !== this._lines[line]) {
|
||||
|
||||
const text = this._lines[line];
|
||||
const firstNonWhitespaceCharacterIndex = /^(\s*)/.exec(text)[1].length;
|
||||
const range = new Range(line, 0, line, text.length);
|
||||
const rangeIncludingLineBreak = line < this._lines.length - 1
|
||||
? new Range(line, 0, line + 1, 0)
|
||||
: range;
|
||||
|
||||
result = Object.freeze({
|
||||
lineNumber: line,
|
||||
range,
|
||||
rangeIncludingLineBreak,
|
||||
text,
|
||||
firstNonWhitespaceCharacterIndex, //TODO@api, rename to 'leadingWhitespaceLength'
|
||||
isEmptyOrWhitespace: firstNonWhitespaceCharacterIndex === text.length
|
||||
});
|
||||
|
||||
this._textLines[line] = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _offsetAt(position: vscode.Position): number {
|
||||
position = this._validatePosition(position);
|
||||
this._ensureLineStarts();
|
||||
return this._lineStarts.getAccumulatedValue(position.line - 1) + position.character;
|
||||
}
|
||||
|
||||
private _positionAt(offset: number): vscode.Position {
|
||||
offset = Math.floor(offset);
|
||||
offset = Math.max(0, offset);
|
||||
|
||||
this._ensureLineStarts();
|
||||
let out = this._lineStarts.getIndexOf(offset);
|
||||
|
||||
let lineLength = this._lines[out.index].length;
|
||||
|
||||
// Ensure we return a valid position
|
||||
return new Position(out.index, Math.min(out.remainder, lineLength));
|
||||
}
|
||||
|
||||
// ---- range math
|
||||
|
||||
private _validateRange(range: vscode.Range): vscode.Range {
|
||||
if (!(range instanceof Range)) {
|
||||
throw new Error('Invalid argument');
|
||||
}
|
||||
|
||||
let start = this._validatePosition(range.start);
|
||||
let end = this._validatePosition(range.end);
|
||||
|
||||
if (start === range.start && end === range.end) {
|
||||
return range;
|
||||
}
|
||||
return new Range(start.line, start.character, end.line, end.character);
|
||||
}
|
||||
|
||||
private _validatePosition(position: vscode.Position): vscode.Position {
|
||||
if (!(position instanceof Position)) {
|
||||
throw new Error('Invalid argument');
|
||||
}
|
||||
|
||||
let { line, character } = position;
|
||||
let hasChanged = false;
|
||||
|
||||
if (line < 0) {
|
||||
line = 0;
|
||||
character = 0;
|
||||
hasChanged = true;
|
||||
}
|
||||
else if (line >= this._lines.length) {
|
||||
line = this._lines.length - 1;
|
||||
character = this._lines[line].length;
|
||||
hasChanged = true;
|
||||
}
|
||||
else {
|
||||
let maxCharacter = this._lines[line].length;
|
||||
if (character < 0) {
|
||||
character = 0;
|
||||
hasChanged = true;
|
||||
}
|
||||
else if (character > maxCharacter) {
|
||||
character = maxCharacter;
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasChanged) {
|
||||
return position;
|
||||
}
|
||||
return new Position(line, character);
|
||||
}
|
||||
|
||||
private _getWordRangeAtPosition(_position: vscode.Position, regexp?: RegExp): vscode.Range {
|
||||
let position = this._validatePosition(_position);
|
||||
|
||||
if (!regexp) {
|
||||
// use default when custom-regexp isn't provided
|
||||
regexp = getWordDefinitionFor(this._languageId);
|
||||
|
||||
} else if (regExpLeadsToEndlessLoop(regexp)) {
|
||||
// use default when custom-regexp is bad
|
||||
console.warn(`[getWordRangeAtPosition]: ignoring custom regexp '${regexp.source}' because it matches the empty string.`);
|
||||
regexp = getWordDefinitionFor(this._languageId);
|
||||
}
|
||||
|
||||
let wordAtText = getWordAtText(
|
||||
position.character + 1,
|
||||
ensureValidWordDefinition(regexp),
|
||||
this._lines[position.line],
|
||||
0
|
||||
);
|
||||
|
||||
if (wordAtText) {
|
||||
return new Range(position.line, wordAtText.startColumn - 1, position.line, wordAtText.endColumn - 1);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
165
src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts
Normal file
165
src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Event from 'vs/base/common/event';
|
||||
import CallbackList from 'vs/base/common/callbackList';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { sequence, always } from 'vs/base/common/async';
|
||||
import { illegalState } from 'vs/base/common/errors';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { MainThreadWorkspaceShape, ExtHostDocumentSaveParticipantShape } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { TextEdit } from 'vs/workbench/api/node/extHostTypes';
|
||||
import { fromRange, TextDocumentSaveReason, EndOfLine } from 'vs/workbench/api/node/extHostTypeConverters';
|
||||
import { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
|
||||
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSaveParticipantShape {
|
||||
|
||||
private _documents: ExtHostDocuments;
|
||||
private _workspace: MainThreadWorkspaceShape;
|
||||
private _callbacks = new CallbackList();
|
||||
private _badListeners = new WeakMap<Function, number>();
|
||||
private _thresholds: { timeout: number; errors: number; };
|
||||
|
||||
constructor(documents: ExtHostDocuments, workspace: MainThreadWorkspaceShape, thresholds: { timeout: number; errors: number; } = { timeout: 1500, errors: 3 }) {
|
||||
this._documents = documents;
|
||||
this._workspace = workspace;
|
||||
this._thresholds = thresholds;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._callbacks.dispose();
|
||||
}
|
||||
|
||||
get onWillSaveTextDocumentEvent(): Event<vscode.TextDocumentWillSaveEvent> {
|
||||
return (listener, thisArg, disposables) => {
|
||||
this._callbacks.add(listener, thisArg);
|
||||
const result = {
|
||||
dispose: () => {
|
||||
this._callbacks.remove(listener, thisArg);
|
||||
}
|
||||
};
|
||||
if (Array.isArray(disposables)) {
|
||||
disposables.push(result);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
$participateInSave(resource: URI, reason: SaveReason): TPromise<boolean[]> {
|
||||
const entries = this._callbacks.entries();
|
||||
|
||||
let didTimeout = false;
|
||||
let didTimeoutHandle = setTimeout(() => didTimeout = true, this._thresholds.timeout);
|
||||
|
||||
const promise = sequence(entries.map(([fn, thisArg]) => {
|
||||
return () => {
|
||||
|
||||
if (didTimeout) {
|
||||
// timeout - no more listeners
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const document = this._documents.getDocumentData(resource).document;
|
||||
return this._deliverEventAsyncAndBlameBadListeners(fn, thisArg, <any>{ document, reason: TextDocumentSaveReason.to(reason) });
|
||||
};
|
||||
}));
|
||||
|
||||
return always(promise, () => clearTimeout(didTimeoutHandle));
|
||||
}
|
||||
|
||||
private _deliverEventAsyncAndBlameBadListeners(listener: Function, thisArg: any, stubEvent: vscode.TextDocumentWillSaveEvent): TPromise<any> {
|
||||
const errors = this._badListeners.get(listener);
|
||||
if (errors > this._thresholds.errors) {
|
||||
// bad listener - ignore
|
||||
return TPromise.wrap(false);
|
||||
}
|
||||
|
||||
return this._deliverEventAsync(listener, thisArg, stubEvent).then(() => {
|
||||
// don't send result across the wire
|
||||
return true;
|
||||
|
||||
}, err => {
|
||||
if (!(err instanceof Error) || (<Error>err).message !== 'concurrent_edits') {
|
||||
const errors = this._badListeners.get(listener);
|
||||
this._badListeners.set(listener, !errors ? 1 : errors + 1);
|
||||
|
||||
// todo@joh signal to the listener?
|
||||
// if (errors === this._thresholds.errors) {
|
||||
// console.warn('BAD onWillSaveTextDocumentEvent-listener is from now on being ignored');
|
||||
// }
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private _deliverEventAsync(listener: Function, thisArg: any, stubEvent: vscode.TextDocumentWillSaveEvent): TPromise<any> {
|
||||
|
||||
const promises: TPromise<vscode.TextEdit[]>[] = [];
|
||||
|
||||
const { document, reason } = stubEvent;
|
||||
const { version } = document;
|
||||
|
||||
const event = Object.freeze(<vscode.TextDocumentWillSaveEvent>{
|
||||
document,
|
||||
reason,
|
||||
waitUntil(p: Thenable<any | vscode.TextEdit[]>) {
|
||||
if (Object.isFrozen(promises)) {
|
||||
throw illegalState('waitUntil can not be called async');
|
||||
}
|
||||
promises.push(TPromise.wrap(p));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// fire event
|
||||
listener.apply(thisArg, [event]);
|
||||
} catch (err) {
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
|
||||
// freeze promises after event call
|
||||
Object.freeze(promises);
|
||||
|
||||
return new TPromise<vscode.TextEdit[][]>((resolve, reject) => {
|
||||
// join on all listener promises, reject after timeout
|
||||
const handle = setTimeout(() => reject(new Error('timeout')), this._thresholds.timeout);
|
||||
return always(TPromise.join(promises), () => clearTimeout(handle)).then(resolve, reject);
|
||||
|
||||
}).then(values => {
|
||||
|
||||
let edits: IResourceEdit[] = [];
|
||||
|
||||
for (const value of values) {
|
||||
if (Array.isArray(value) && (<vscode.TextEdit[]>value).every(e => e instanceof TextEdit)) {
|
||||
for (const { newText, newEol, range } of value) {
|
||||
edits.push({
|
||||
resource: <URI>document.uri,
|
||||
range: range && fromRange(range),
|
||||
newText,
|
||||
newEol: EndOfLine.from(newEol)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply edits if any and if document
|
||||
// didn't change somehow in the meantime
|
||||
if (edits.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (version === document.version) {
|
||||
return this._workspace.$applyWorkspaceEdit(edits);
|
||||
}
|
||||
|
||||
// TODO@joh bubble this to listener?
|
||||
return TPromise.wrapError(new Error('concurrent_edits'));
|
||||
});
|
||||
}
|
||||
}
|
||||
142
src/vs/workbench/api/node/extHostDocuments.ts
Normal file
142
src/vs/workbench/api/node/extHostDocuments.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import URI 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';
|
||||
import * as vscode from 'vscode';
|
||||
import { MainContext, MainThreadDocumentsShape, ExtHostDocumentsShape, IMainContext } from './extHost.protocol';
|
||||
import { ExtHostDocumentData, setWordDefinitionFor } from './extHostDocumentData';
|
||||
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors';
|
||||
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorModel';
|
||||
|
||||
export class ExtHostDocuments implements ExtHostDocumentsShape {
|
||||
|
||||
private _onDidAddDocument = new Emitter<vscode.TextDocument>();
|
||||
private _onDidRemoveDocument = new Emitter<vscode.TextDocument>();
|
||||
private _onDidChangeDocument = new Emitter<vscode.TextDocumentChangeEvent>();
|
||||
private _onDidSaveDocument = new Emitter<vscode.TextDocument>();
|
||||
|
||||
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;
|
||||
|
||||
private _toDispose: IDisposable[];
|
||||
private _proxy: MainThreadDocumentsShape;
|
||||
private _documentsAndEditors: ExtHostDocumentsAndEditors;
|
||||
private _documentLoader = new Map<string, TPromise<ExtHostDocumentData>>();
|
||||
|
||||
constructor(mainContext: IMainContext, documentsAndEditors: ExtHostDocumentsAndEditors) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadDocuments);
|
||||
this._documentsAndEditors = documentsAndEditors;
|
||||
|
||||
this._toDispose = [
|
||||
this._documentsAndEditors.onDidRemoveDocuments(documents => {
|
||||
for (const data of documents) {
|
||||
this._onDidRemoveDocument.fire(data.document);
|
||||
}
|
||||
}),
|
||||
this._documentsAndEditors.onDidAddDocuments(documents => {
|
||||
for (const data of documents) {
|
||||
this._onDidAddDocument.fire(data.document);
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
dispose(this._toDispose);
|
||||
}
|
||||
|
||||
public getAllDocumentData(): ExtHostDocumentData[] {
|
||||
return this._documentsAndEditors.allDocuments();
|
||||
}
|
||||
|
||||
public getDocumentData(resource: vscode.Uri): ExtHostDocumentData {
|
||||
if (!resource) {
|
||||
return undefined;
|
||||
}
|
||||
const data = this._documentsAndEditors.getDocument(resource.toString());
|
||||
if (data) {
|
||||
return data;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public ensureDocumentData(uri: URI): TPromise<ExtHostDocumentData> {
|
||||
|
||||
let cached = this._documentsAndEditors.getDocument(uri.toString());
|
||||
if (cached) {
|
||||
return TPromise.as(cached);
|
||||
}
|
||||
|
||||
let promise = this._documentLoader.get(uri.toString());
|
||||
if (!promise) {
|
||||
promise = this._proxy.$tryOpenDocument(uri).then(() => {
|
||||
this._documentLoader.delete(uri.toString());
|
||||
return this._documentsAndEditors.getDocument(uri.toString());
|
||||
}, err => {
|
||||
this._documentLoader.delete(uri.toString());
|
||||
return TPromise.wrapError<ExtHostDocumentData>(err);
|
||||
});
|
||||
this._documentLoader.set(uri.toString(), promise);
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
public createDocumentData(options?: { language?: string; content?: string }): TPromise<URI> {
|
||||
return this._proxy.$tryCreateDocument(options);
|
||||
}
|
||||
|
||||
public $acceptModelModeChanged(strURL: string, oldModeId: string, newModeId: string): void {
|
||||
let data = this._documentsAndEditors.getDocument(strURL);
|
||||
|
||||
// Treat a mode change as a remove + add
|
||||
|
||||
this._onDidRemoveDocument.fire(data.document);
|
||||
data._acceptLanguageId(newModeId);
|
||||
this._onDidAddDocument.fire(data.document);
|
||||
}
|
||||
|
||||
public $acceptModelSaved(strURL: string): void {
|
||||
let data = this._documentsAndEditors.getDocument(strURL);
|
||||
this.$acceptDirtyStateChanged(strURL, false);
|
||||
this._onDidSaveDocument.fire(data.document);
|
||||
}
|
||||
|
||||
public $acceptDirtyStateChanged(strURL: string, isDirty: boolean): void {
|
||||
let data = this._documentsAndEditors.getDocument(strURL);
|
||||
data._acceptIsDirty(isDirty);
|
||||
this._onDidChangeDocument.fire({
|
||||
document: data.document,
|
||||
contentChanges: []
|
||||
});
|
||||
}
|
||||
|
||||
public $acceptModelChanged(strURL: string, events: IModelChangedEvent, isDirty: boolean): void {
|
||||
let data = this._documentsAndEditors.getDocument(strURL);
|
||||
data._acceptIsDirty(isDirty);
|
||||
data.onEvents(events);
|
||||
this._onDidChangeDocument.fire({
|
||||
document: data.document,
|
||||
contentChanges: events.changes.map((change) => {
|
||||
return {
|
||||
range: TypeConverters.toRange(change.range),
|
||||
rangeLength: change.rangeLength,
|
||||
text: change.text
|
||||
};
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
public setWordDefinitionFor(modeId: string, wordDefinition: RegExp): void {
|
||||
setWordDefinitionFor(modeId, wordDefinition);
|
||||
}
|
||||
}
|
||||
149
src/vs/workbench/api/node/extHostDocumentsAndEditors.ts
Normal file
149
src/vs/workbench/api/node/extHostDocumentsAndEditors.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { MainContext, ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta, IMainContext } from './extHost.protocol';
|
||||
import { ExtHostDocumentData } from './extHostDocumentData';
|
||||
import { ExtHostTextEditor, ExtHostTextEditor2 } from './extHostTextEditor';
|
||||
import * as assert from 'assert';
|
||||
import * as typeConverters from './extHostTypeConverters';
|
||||
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
|
||||
|
||||
export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape {
|
||||
|
||||
private _activeEditorId: string;
|
||||
private readonly _editors = new Map<string, ExtHostTextEditor>();
|
||||
private readonly _documents = new Map<string, ExtHostDocumentData>();
|
||||
|
||||
private readonly _onDidAddDocuments = new Emitter<ExtHostDocumentData[]>();
|
||||
private readonly _onDidRemoveDocuments = new Emitter<ExtHostDocumentData[]>();
|
||||
private readonly _onDidChangeVisibleTextEditors = new Emitter<ExtHostTextEditor[]>();
|
||||
private readonly _onDidChangeActiveTextEditor = new Emitter<ExtHostTextEditor>();
|
||||
|
||||
readonly onDidAddDocuments: Event<ExtHostDocumentData[]> = this._onDidAddDocuments.event;
|
||||
readonly onDidRemoveDocuments: Event<ExtHostDocumentData[]> = this._onDidRemoveDocuments.event;
|
||||
readonly onDidChangeVisibleTextEditors: Event<ExtHostTextEditor[]> = this._onDidChangeVisibleTextEditors.event;
|
||||
readonly onDidChangeActiveTextEditor: Event<ExtHostTextEditor> = this._onDidChangeActiveTextEditor.event;
|
||||
|
||||
constructor(
|
||||
private readonly _mainContext: IMainContext,
|
||||
private readonly _extHostExtensions?: ExtHostExtensionService
|
||||
) {
|
||||
}
|
||||
|
||||
$acceptDocumentsAndEditorsDelta(delta: IDocumentsAndEditorsDelta): void {
|
||||
|
||||
const removedDocuments: ExtHostDocumentData[] = [];
|
||||
const addedDocuments: ExtHostDocumentData[] = [];
|
||||
const removedEditors: ExtHostTextEditor[] = [];
|
||||
|
||||
if (delta.removedDocuments) {
|
||||
for (const id of delta.removedDocuments) {
|
||||
const data = this._documents.get(id);
|
||||
this._documents.delete(id);
|
||||
removedDocuments.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
if (delta.addedDocuments) {
|
||||
for (const data of delta.addedDocuments) {
|
||||
assert.ok(!this._documents.has(data.url.toString()), `document '${data.url} already exists!'`);
|
||||
|
||||
const documentData = new ExtHostDocumentData(
|
||||
this._mainContext.get(MainContext.MainThreadDocuments),
|
||||
data.url,
|
||||
data.lines,
|
||||
data.EOL,
|
||||
data.modeId,
|
||||
data.versionId,
|
||||
data.isDirty
|
||||
);
|
||||
this._documents.set(data.url.toString(), documentData);
|
||||
addedDocuments.push(documentData);
|
||||
}
|
||||
}
|
||||
|
||||
if (delta.removedEditors) {
|
||||
for (const id of delta.removedEditors) {
|
||||
const editor = this._editors.get(id);
|
||||
this._editors.delete(id);
|
||||
removedEditors.push(editor);
|
||||
}
|
||||
}
|
||||
|
||||
if (delta.addedEditors) {
|
||||
for (const data of delta.addedEditors) {
|
||||
assert.ok(this._documents.has(data.document.toString()), `document '${data.document}' does not exist`);
|
||||
assert.ok(!this._editors.has(data.id), `editor '${data.id}' already exists!`);
|
||||
|
||||
const documentData = this._documents.get(data.document.toString());
|
||||
const editor = new ExtHostTextEditor2(
|
||||
this._extHostExtensions,
|
||||
this._mainContext.get(MainContext.MainThreadTelemetry),
|
||||
this._mainContext.get(MainContext.MainThreadEditors),
|
||||
data.id,
|
||||
documentData,
|
||||
data.selections.map(typeConverters.toSelection),
|
||||
data.options,
|
||||
typeConverters.toViewColumn(data.editorPosition)
|
||||
);
|
||||
this._editors.set(data.id, editor);
|
||||
}
|
||||
}
|
||||
|
||||
if (delta.newActiveEditor !== undefined) {
|
||||
assert.ok(delta.newActiveEditor === null || this._editors.has(delta.newActiveEditor), `active editor '${delta.newActiveEditor}' does not exist`);
|
||||
this._activeEditorId = delta.newActiveEditor;
|
||||
}
|
||||
|
||||
dispose(removedDocuments);
|
||||
dispose(removedEditors);
|
||||
|
||||
// now that the internal state is complete, fire events
|
||||
if (delta.removedDocuments) {
|
||||
this._onDidRemoveDocuments.fire(removedDocuments);
|
||||
}
|
||||
if (delta.addedDocuments) {
|
||||
this._onDidAddDocuments.fire(addedDocuments);
|
||||
}
|
||||
|
||||
if (delta.removedEditors || delta.addedEditors) {
|
||||
this._onDidChangeVisibleTextEditors.fire(this.allEditors());
|
||||
}
|
||||
if (delta.newActiveEditor !== undefined) {
|
||||
this._onDidChangeActiveTextEditor.fire(this.activeEditor());
|
||||
}
|
||||
}
|
||||
|
||||
getDocument(strUrl: string): ExtHostDocumentData {
|
||||
return this._documents.get(strUrl);
|
||||
}
|
||||
|
||||
allDocuments(): ExtHostDocumentData[] {
|
||||
const result: ExtHostDocumentData[] = [];
|
||||
this._documents.forEach(data => result.push(data));
|
||||
return result;
|
||||
}
|
||||
|
||||
getEditor(id: string): ExtHostTextEditor {
|
||||
return this._editors.get(id);
|
||||
}
|
||||
|
||||
activeEditor(): ExtHostTextEditor | undefined {
|
||||
if (!this._activeEditorId) {
|
||||
return undefined;
|
||||
} else {
|
||||
return this._editors.get(this._activeEditorId);
|
||||
}
|
||||
}
|
||||
|
||||
allEditors(): ExtHostTextEditor[] {
|
||||
const result: ExtHostTextEditor[] = [];
|
||||
this._editors.forEach(data => result.push(data));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
327
src/vs/workbench/api/node/extHostExtensionActivator.ts
Normal file
327
src/vs/workbench/api/node/extHostExtensionActivator.ts
Normal file
@@ -0,0 +1,327 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
const NO_OP_VOID_PROMISE = TPromise.as<void>(void 0);
|
||||
|
||||
export interface IExtensionMemento {
|
||||
get<T>(key: string, defaultValue: T): T;
|
||||
update(key: string, value: any): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface IExtensionContext {
|
||||
subscriptions: IDisposable[];
|
||||
workspaceState: IExtensionMemento;
|
||||
globalState: IExtensionMemento;
|
||||
extensionPath: string;
|
||||
storagePath: string;
|
||||
asAbsolutePath(relativePath: string): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the source code (module) of an extension.
|
||||
*/
|
||||
export interface IExtensionModule {
|
||||
activate(ctx: IExtensionContext): TPromise<IExtensionAPI>;
|
||||
deactivate(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the API of an extension (return value of `activate`).
|
||||
*/
|
||||
export interface IExtensionAPI {
|
||||
// _extensionAPIBrand: any;
|
||||
}
|
||||
|
||||
export class ExtensionActivationTimes {
|
||||
|
||||
public static NONE = new ExtensionActivationTimes(false, -1, -1, -1);
|
||||
|
||||
public readonly startup: boolean;
|
||||
public readonly codeLoadingTime: number;
|
||||
public readonly activateCallTime: number;
|
||||
public readonly activateResolvedTime: number;
|
||||
|
||||
constructor(startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number) {
|
||||
this.startup = startup;
|
||||
this.codeLoadingTime = codeLoadingTime;
|
||||
this.activateCallTime = activateCallTime;
|
||||
this.activateResolvedTime = activateResolvedTime;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtensionActivationTimesBuilder {
|
||||
|
||||
private readonly _startup: boolean;
|
||||
private _codeLoadingStart: number;
|
||||
private _codeLoadingStop: number;
|
||||
private _activateCallStart: number;
|
||||
private _activateCallStop: number;
|
||||
private _activateResolveStart: number;
|
||||
private _activateResolveStop: number;
|
||||
|
||||
constructor(startup: boolean) {
|
||||
this._startup = startup;
|
||||
this._codeLoadingStart = -1;
|
||||
this._codeLoadingStop = -1;
|
||||
this._activateCallStart = -1;
|
||||
this._activateCallStop = -1;
|
||||
this._activateResolveStart = -1;
|
||||
this._activateResolveStop = -1;
|
||||
}
|
||||
|
||||
private _delta(start: number, stop: number): number {
|
||||
if (start === -1 || stop === -1) {
|
||||
return -1;
|
||||
}
|
||||
return stop - start;
|
||||
}
|
||||
|
||||
public build(): ExtensionActivationTimes {
|
||||
return new ExtensionActivationTimes(
|
||||
this._startup,
|
||||
this._delta(this._codeLoadingStart, this._codeLoadingStop),
|
||||
this._delta(this._activateCallStart, this._activateCallStop),
|
||||
this._delta(this._activateResolveStart, this._activateResolveStop)
|
||||
);
|
||||
}
|
||||
|
||||
public codeLoadingStart(): void {
|
||||
this._codeLoadingStart = Date.now();
|
||||
}
|
||||
|
||||
public codeLoadingStop(): void {
|
||||
this._codeLoadingStop = Date.now();
|
||||
}
|
||||
|
||||
public activateCallStart(): void {
|
||||
this._activateCallStart = Date.now();
|
||||
}
|
||||
|
||||
public activateCallStop(): void {
|
||||
this._activateCallStop = Date.now();
|
||||
}
|
||||
|
||||
public activateResolveStart(): void {
|
||||
this._activateResolveStart = Date.now();
|
||||
}
|
||||
|
||||
public activateResolveStop(): void {
|
||||
this._activateResolveStop = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
export class ActivatedExtension {
|
||||
|
||||
public readonly activationFailed: boolean;
|
||||
public readonly activationTimes: ExtensionActivationTimes;
|
||||
public readonly module: IExtensionModule;
|
||||
public readonly exports: IExtensionAPI;
|
||||
public readonly subscriptions: IDisposable[];
|
||||
|
||||
constructor(
|
||||
activationFailed: boolean,
|
||||
activationTimes: ExtensionActivationTimes,
|
||||
module: IExtensionModule,
|
||||
exports: IExtensionAPI,
|
||||
subscriptions: IDisposable[]
|
||||
) {
|
||||
this.activationFailed = activationFailed;
|
||||
this.activationTimes = activationTimes;
|
||||
this.module = module;
|
||||
this.exports = exports;
|
||||
this.subscriptions = subscriptions;
|
||||
}
|
||||
}
|
||||
|
||||
export class EmptyExtension extends ActivatedExtension {
|
||||
constructor(activationTimes: ExtensionActivationTimes) {
|
||||
super(false, activationTimes, { activate: undefined, deactivate: undefined }, undefined, []);
|
||||
}
|
||||
}
|
||||
|
||||
export class FailedExtension extends ActivatedExtension {
|
||||
constructor(activationTimes: ExtensionActivationTimes) {
|
||||
super(true, activationTimes, { activate: undefined, deactivate: undefined }, undefined, []);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IExtensionsActivatorHost {
|
||||
showMessage(severity: Severity, message: string): void;
|
||||
|
||||
actualActivateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension>;
|
||||
}
|
||||
|
||||
export class ExtensionsActivator {
|
||||
|
||||
private readonly _registry: ExtensionDescriptionRegistry;
|
||||
private readonly _host: IExtensionsActivatorHost;
|
||||
private readonly _activatingExtensions: { [extensionId: string]: TPromise<void>; };
|
||||
private readonly _activatedExtensions: { [extensionId: string]: ActivatedExtension; };
|
||||
/**
|
||||
* A map of already activated events to speed things up if the same activation event is triggered multiple times.
|
||||
*/
|
||||
private readonly _alreadyActivatedEvents: { [activationEvent: string]: boolean; };
|
||||
|
||||
constructor(registry: ExtensionDescriptionRegistry, host: IExtensionsActivatorHost) {
|
||||
this._registry = registry;
|
||||
this._host = host;
|
||||
this._activatingExtensions = {};
|
||||
this._activatedExtensions = {};
|
||||
this._alreadyActivatedEvents = Object.create(null);
|
||||
}
|
||||
|
||||
public isActivated(extensionId: string): boolean {
|
||||
return hasOwnProperty.call(this._activatedExtensions, extensionId);
|
||||
}
|
||||
|
||||
public getActivatedExtension(extensionId: string): ActivatedExtension {
|
||||
if (!hasOwnProperty.call(this._activatedExtensions, extensionId)) {
|
||||
throw new Error('Extension `' + extensionId + '` is not known or not activated');
|
||||
}
|
||||
return this._activatedExtensions[extensionId];
|
||||
}
|
||||
|
||||
public activateByEvent(activationEvent: string, startup: boolean): TPromise<void> {
|
||||
if (this._alreadyActivatedEvents[activationEvent]) {
|
||||
return NO_OP_VOID_PROMISE;
|
||||
}
|
||||
let activateExtensions = this._registry.getExtensionDescriptionsForActivationEvent(activationEvent);
|
||||
return this._activateExtensions(activateExtensions, startup, 0).then(() => {
|
||||
this._alreadyActivatedEvents[activationEvent] = true;
|
||||
});
|
||||
}
|
||||
|
||||
public activateById(extensionId: string, startup: boolean): TPromise<void> {
|
||||
let desc = this._registry.getExtensionDescription(extensionId);
|
||||
if (!desc) {
|
||||
throw new Error('Extension `' + extensionId + '` is not known');
|
||||
}
|
||||
|
||||
return this._activateExtensions([desc], startup, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle semantics related to dependencies for `currentExtension`.
|
||||
* semantics: `redExtensions` must wait for `greenExtensions`.
|
||||
*/
|
||||
private _handleActivateRequest(currentExtension: IExtensionDescription, greenExtensions: { [id: string]: IExtensionDescription; }, redExtensions: IExtensionDescription[]): void {
|
||||
let depIds = (typeof currentExtension.extensionDependencies === 'undefined' ? [] : currentExtension.extensionDependencies);
|
||||
let currentExtensionGetsGreenLight = true;
|
||||
|
||||
for (let j = 0, lenJ = depIds.length; j < lenJ; j++) {
|
||||
let depId = depIds[j];
|
||||
let depDesc = this._registry.getExtensionDescription(depId);
|
||||
|
||||
if (!depDesc) {
|
||||
// Error condition 1: unknown dependency
|
||||
this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Extension `{1}` failed to activate. Reason: unknown dependency `{0}`.", depId, currentExtension.id));
|
||||
this._activatedExtensions[currentExtension.id] = new FailedExtension(ExtensionActivationTimes.NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasOwnProperty.call(this._activatedExtensions, depId)) {
|
||||
let dep = this._activatedExtensions[depId];
|
||||
if (dep.activationFailed) {
|
||||
// Error condition 2: a dependency has already failed activation
|
||||
this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Extension `{1}` failed to activate. Reason: dependency `{0}` failed to activate.", depId, currentExtension.id));
|
||||
this._activatedExtensions[currentExtension.id] = new FailedExtension(ExtensionActivationTimes.NONE);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// must first wait for the dependency to activate
|
||||
currentExtensionGetsGreenLight = false;
|
||||
greenExtensions[depId] = depDesc;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentExtensionGetsGreenLight) {
|
||||
greenExtensions[currentExtension.id] = currentExtension;
|
||||
} else {
|
||||
redExtensions.push(currentExtension);
|
||||
}
|
||||
}
|
||||
|
||||
private _activateExtensions(extensionDescriptions: IExtensionDescription[], startup: boolean, recursionLevel: number): TPromise<void> {
|
||||
// console.log(recursionLevel, '_activateExtensions: ', extensionDescriptions.map(p => p.id));
|
||||
if (extensionDescriptions.length === 0) {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
|
||||
extensionDescriptions = extensionDescriptions.filter((p) => !hasOwnProperty.call(this._activatedExtensions, p.id));
|
||||
if (extensionDescriptions.length === 0) {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
|
||||
if (recursionLevel > 10) {
|
||||
// More than 10 dependencies deep => most likely a dependency loop
|
||||
for (let i = 0, len = extensionDescriptions.length; i < len; i++) {
|
||||
// Error condition 3: dependency loop
|
||||
this._host.showMessage(Severity.Error, nls.localize('failedDep2', "Extension `{0}` failed to activate. Reason: more than 10 levels of dependencies (most likely a dependency loop).", extensionDescriptions[i].id));
|
||||
this._activatedExtensions[extensionDescriptions[i].id] = new FailedExtension(ExtensionActivationTimes.NONE);
|
||||
}
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
|
||||
let greenMap: { [id: string]: IExtensionDescription; } = Object.create(null),
|
||||
red: IExtensionDescription[] = [];
|
||||
|
||||
for (let i = 0, len = extensionDescriptions.length; i < len; i++) {
|
||||
this._handleActivateRequest(extensionDescriptions[i], greenMap, red);
|
||||
}
|
||||
|
||||
// Make sure no red is also green
|
||||
for (let i = 0, len = red.length; i < len; i++) {
|
||||
if (greenMap[red[i].id]) {
|
||||
delete greenMap[red[i].id];
|
||||
}
|
||||
}
|
||||
|
||||
let green = Object.keys(greenMap).map(id => greenMap[id]);
|
||||
|
||||
// console.log('greenExtensions: ', green.map(p => p.id));
|
||||
// console.log('redExtensions: ', red.map(p => p.id));
|
||||
|
||||
if (red.length === 0) {
|
||||
// Finally reached only leafs!
|
||||
return TPromise.join(green.map((p) => this._activateExtension(p, startup))).then(_ => void 0);
|
||||
}
|
||||
|
||||
return this._activateExtensions(green, startup, recursionLevel + 1).then(_ => {
|
||||
return this._activateExtensions(red, startup, recursionLevel + 1);
|
||||
});
|
||||
}
|
||||
|
||||
private _activateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<void> {
|
||||
if (hasOwnProperty.call(this._activatedExtensions, extensionDescription.id)) {
|
||||
return TPromise.as(void 0);
|
||||
}
|
||||
|
||||
if (hasOwnProperty.call(this._activatingExtensions, extensionDescription.id)) {
|
||||
return this._activatingExtensions[extensionDescription.id];
|
||||
}
|
||||
|
||||
this._activatingExtensions[extensionDescription.id] = this._host.actualActivateExtension(extensionDescription, startup).then(null, (err) => {
|
||||
this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension `{0}` failed: {1}.", extensionDescription.id, err.message));
|
||||
console.error('Activating extension `' + extensionDescription.id + '` failed: ', err.message);
|
||||
console.log('Here is the error stack: ', err.stack);
|
||||
// Treat the extension as being empty
|
||||
return new FailedExtension(ExtensionActivationTimes.NONE);
|
||||
}).then((x: ActivatedExtension) => {
|
||||
this._activatedExtensions[extensionDescription.id] = x;
|
||||
delete this._activatingExtensions[extensionDescription.id];
|
||||
});
|
||||
|
||||
return this._activatingExtensions[extensionDescription.id];
|
||||
}
|
||||
}
|
||||
422
src/vs/workbench/api/node/extHostExtensionService.ts
Normal file
422
src/vs/workbench/api/node/extHostExtensionService.ts
Normal file
@@ -0,0 +1,422 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { join } from 'path';
|
||||
import { mkdirp, dirExists } from 'vs/base/node/pfs';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { createApiFactory, initializeExtensionApi } from 'sql/workbench/api/node/sqlExtHost.api.impl';
|
||||
import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape } from './extHost.protocol';
|
||||
import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule, ExtensionActivationTimesBuilder, ExtensionActivationTimes } from 'vs/workbench/api/node/extHostExtensionActivator';
|
||||
import { Barrier } from 'vs/workbench/services/extensions/node/barrier';
|
||||
import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService';
|
||||
import { realpath } from 'fs';
|
||||
import { TrieMap } from 'vs/base/common/map';
|
||||
|
||||
class ExtensionMemento implements IExtensionMemento {
|
||||
|
||||
private _id: string;
|
||||
private _shared: boolean;
|
||||
private _storage: ExtHostStorage;
|
||||
|
||||
private _init: TPromise<ExtensionMemento>;
|
||||
private _value: { [n: string]: any; };
|
||||
|
||||
constructor(id: string, global: boolean, storage: ExtHostStorage) {
|
||||
this._id = id;
|
||||
this._shared = global;
|
||||
this._storage = storage;
|
||||
|
||||
this._init = this._storage.getValue(this._shared, this._id, Object.create(null)).then(value => {
|
||||
this._value = value;
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
get whenReady(): TPromise<ExtensionMemento> {
|
||||
return this._init;
|
||||
}
|
||||
|
||||
get<T>(key: string, defaultValue: T): T {
|
||||
let value = this._value[key];
|
||||
if (typeof value === 'undefined') {
|
||||
value = defaultValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
update(key: string, value: any): Thenable<boolean> {
|
||||
this._value[key] = value;
|
||||
return this._storage
|
||||
.setValue(this._shared, this._id, this._value)
|
||||
.then(() => true);
|
||||
}
|
||||
}
|
||||
|
||||
class ExtensionStoragePath {
|
||||
|
||||
private readonly _workspace: IWorkspaceData;
|
||||
private readonly _environment: IEnvironment;
|
||||
|
||||
private readonly _ready: TPromise<string>;
|
||||
private _value: string;
|
||||
|
||||
constructor(workspace: IWorkspaceData, environment: IEnvironment) {
|
||||
this._workspace = workspace;
|
||||
this._environment = environment;
|
||||
this._ready = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
|
||||
}
|
||||
|
||||
get whenReady(): TPromise<any> {
|
||||
return this._ready;
|
||||
}
|
||||
|
||||
value(extension: IExtensionDescription): string {
|
||||
if (this._value) {
|
||||
return join(this._value, extension.id);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _getOrCreateWorkspaceStoragePath(): TPromise<string> {
|
||||
if (!this._workspace) {
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
const storageName = this._workspace.id;
|
||||
const storagePath = join(this._environment.appSettingsHome, 'workspaceStorage', storageName);
|
||||
|
||||
return dirExists(storagePath).then(exists => {
|
||||
if (exists) {
|
||||
return storagePath;
|
||||
}
|
||||
|
||||
return mkdirp(storagePath).then(success => {
|
||||
return storagePath;
|
||||
}, err => {
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
|
||||
|
||||
private readonly _barrier: Barrier;
|
||||
private readonly _registry: ExtensionDescriptionRegistry;
|
||||
private readonly _threadService: ExtHostThreadService;
|
||||
private readonly _mainThreadTelemetry: MainThreadTelemetryShape;
|
||||
private readonly _storage: ExtHostStorage;
|
||||
private readonly _storagePath: ExtensionStoragePath;
|
||||
private readonly _proxy: MainThreadExtensionServiceShape;
|
||||
private _activator: ExtensionsActivator;
|
||||
private _extensionPathIndex: TPromise<TrieMap<IExtensionDescription>>;
|
||||
/**
|
||||
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
|
||||
*/
|
||||
constructor(initData: IInitData, threadService: ExtHostThreadService) {
|
||||
this._barrier = new Barrier();
|
||||
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
|
||||
this._threadService = threadService;
|
||||
this._mainThreadTelemetry = threadService.get(MainContext.MainThreadTelemetry);
|
||||
this._storage = new ExtHostStorage(threadService);
|
||||
this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment);
|
||||
this._proxy = this._threadService.get(MainContext.MainThreadExtensionService);
|
||||
this._activator = null;
|
||||
|
||||
// initialize API first (i.e. do not release barrier until the API is initialized)
|
||||
const apiFactory = createApiFactory(initData, threadService, this);
|
||||
|
||||
initializeExtensionApi(this, apiFactory).then(() => {
|
||||
|
||||
this._activator = new ExtensionsActivator(this._registry, {
|
||||
showMessage: (severity: Severity, message: string): void => {
|
||||
this._proxy.$localShowMessage(severity, message);
|
||||
|
||||
switch (severity) {
|
||||
case Severity.Error:
|
||||
console.error(message);
|
||||
break;
|
||||
case Severity.Warning:
|
||||
console.warn(message);
|
||||
break;
|
||||
default:
|
||||
console.log(message);
|
||||
}
|
||||
},
|
||||
|
||||
actualActivateExtension: (extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension> => {
|
||||
return this._activateExtension(extensionDescription, startup);
|
||||
}
|
||||
});
|
||||
|
||||
this._barrier.open();
|
||||
});
|
||||
}
|
||||
|
||||
public onExtensionAPIReady(): TPromise<boolean> {
|
||||
return this._barrier.wait();
|
||||
}
|
||||
|
||||
public isActivated(extensionId: string): boolean {
|
||||
if (this._barrier.isOpen()) {
|
||||
return this._activator.isActivated(extensionId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public activateByEvent(activationEvent: string, startup: boolean): TPromise<void> {
|
||||
if (this._barrier.isOpen()) {
|
||||
return this._activator.activateByEvent(activationEvent, startup);
|
||||
} else {
|
||||
return this._barrier.wait().then(() => this._activator.activateByEvent(activationEvent, startup));
|
||||
}
|
||||
}
|
||||
|
||||
public activateById(extensionId: string, startup: boolean): TPromise<void> {
|
||||
if (this._barrier.isOpen()) {
|
||||
return this._activator.activateById(extensionId, startup);
|
||||
} else {
|
||||
return this._barrier.wait().then(() => this._activator.activateById(extensionId, startup));
|
||||
}
|
||||
}
|
||||
|
||||
public getAllExtensionDescriptions(): IExtensionDescription[] {
|
||||
return this._registry.getAllExtensionDescriptions();
|
||||
}
|
||||
|
||||
public getExtensionDescription(extensionId: string): IExtensionDescription {
|
||||
return this._registry.getExtensionDescription(extensionId);
|
||||
}
|
||||
|
||||
public getExtensionExports(extensionId: string): IExtensionAPI {
|
||||
if (this._barrier.isOpen()) {
|
||||
return this._activator.getActivatedExtension(extensionId).exports;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// create trie to enable fast 'filename -> extension id' look up
|
||||
public getExtensionPathIndex(): TPromise<TrieMap<IExtensionDescription>> {
|
||||
if (!this._extensionPathIndex) {
|
||||
const trie = new TrieMap<IExtensionDescription>();
|
||||
const extensions = this.getAllExtensionDescriptions().map(ext => {
|
||||
if (!ext.main) {
|
||||
return undefined;
|
||||
}
|
||||
return new TPromise((resolve, reject) => {
|
||||
realpath(ext.extensionFolderPath, (err, path) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
trie.insert(path, ext);
|
||||
resolve(void 0);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
this._extensionPathIndex = TPromise.join(extensions).then(() => trie);
|
||||
}
|
||||
return this._extensionPathIndex;
|
||||
}
|
||||
|
||||
|
||||
public deactivate(extensionId: string): TPromise<void> {
|
||||
let result: TPromise<void> = TPromise.as(void 0);
|
||||
|
||||
if (!this._barrier.isOpen()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!this._activator.isActivated(extensionId)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
let extension = this._activator.getActivatedExtension(extensionId);
|
||||
if (!extension) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// call deactivate if available
|
||||
try {
|
||||
if (typeof extension.module.deactivate === 'function') {
|
||||
result = TPromise.wrap(extension.module.deactivate()).then(null, (err) => {
|
||||
// TODO: Do something with err if this is not the shutdown case
|
||||
return TPromise.as(void 0);
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
// TODO: Do something with err if this is not the shutdown case
|
||||
}
|
||||
|
||||
// clean up subscriptions
|
||||
try {
|
||||
dispose(extension.subscriptions);
|
||||
} catch (err) {
|
||||
// TODO: Do something with err if this is not the shutdown case
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// --- impl
|
||||
|
||||
private _activateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension> {
|
||||
return this._doActivateExtension(extensionDescription, startup).then((activatedExtension) => {
|
||||
const activationTimes = activatedExtension.activationTimes;
|
||||
this._proxy.$onExtensionActivated(extensionDescription.id, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime);
|
||||
return activatedExtension;
|
||||
}, (err) => {
|
||||
this._proxy.$onExtensionActivationFailed(extensionDescription.id);
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
private _doActivateExtension(extensionDescription: IExtensionDescription, startup: boolean): TPromise<ActivatedExtension> {
|
||||
let event = getTelemetryActivationEvent(extensionDescription);
|
||||
this._mainThreadTelemetry.$publicLog('activatePlugin', event);
|
||||
if (!extensionDescription.main) {
|
||||
// Treat the extension as being empty => NOT AN ERROR CASE
|
||||
return TPromise.as(new EmptyExtension(ExtensionActivationTimes.NONE));
|
||||
}
|
||||
|
||||
const activationTimesBuilder = new ExtensionActivationTimesBuilder(startup);
|
||||
return TPromise.join<any>([
|
||||
loadCommonJSModule(extensionDescription.main, activationTimesBuilder),
|
||||
this._loadExtensionContext(extensionDescription)
|
||||
]).then(values => {
|
||||
return ExtHostExtensionService._callActivate(<IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
|
||||
}, (errors: any[]) => {
|
||||
// Avoid failing with an array of errors, fail with a single error
|
||||
if (errors[0]) {
|
||||
return TPromise.wrapError<ActivatedExtension>(errors[0]);
|
||||
}
|
||||
if (errors[1]) {
|
||||
return TPromise.wrapError<ActivatedExtension>(errors[1]);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
private _loadExtensionContext(extensionDescription: IExtensionDescription): TPromise<IExtensionContext> {
|
||||
|
||||
let globalState = new ExtensionMemento(extensionDescription.id, true, this._storage);
|
||||
let workspaceState = new ExtensionMemento(extensionDescription.id, false, this._storage);
|
||||
|
||||
return TPromise.join([
|
||||
globalState.whenReady,
|
||||
workspaceState.whenReady,
|
||||
this._storagePath.whenReady
|
||||
]).then(() => {
|
||||
return Object.freeze(<IExtensionContext>{
|
||||
globalState,
|
||||
workspaceState,
|
||||
subscriptions: [],
|
||||
get extensionPath() { return extensionDescription.extensionFolderPath; },
|
||||
storagePath: this._storagePath.value(extensionDescription),
|
||||
asAbsolutePath: (relativePath: string) => { return join(extensionDescription.extensionFolderPath, relativePath); }
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static _callActivate(extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise<ActivatedExtension> {
|
||||
// Make sure the extension's surface is not undefined
|
||||
extensionModule = extensionModule || {
|
||||
activate: undefined,
|
||||
deactivate: undefined
|
||||
};
|
||||
|
||||
return this._callActivateOptional(extensionModule, context, activationTimesBuilder).then((extensionExports) => {
|
||||
return new ActivatedExtension(false, activationTimesBuilder.build(), extensionModule, extensionExports, context.subscriptions);
|
||||
});
|
||||
}
|
||||
|
||||
private static _callActivateOptional(extensionModule: IExtensionModule, context: IExtensionContext, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise<IExtensionAPI> {
|
||||
if (typeof extensionModule.activate === 'function') {
|
||||
try {
|
||||
activationTimesBuilder.activateCallStart();
|
||||
const activateResult = extensionModule.activate.apply(global, [context]);
|
||||
activationTimesBuilder.activateCallStop();
|
||||
|
||||
activationTimesBuilder.activateResolveStart();
|
||||
return TPromise.as(activateResult).then((value) => {
|
||||
activationTimesBuilder.activateResolveStop();
|
||||
return value;
|
||||
});
|
||||
} catch (err) {
|
||||
return TPromise.wrapError(err);
|
||||
}
|
||||
} else {
|
||||
// No activate found => the module is the extension's exports
|
||||
return TPromise.as<IExtensionAPI>(extensionModule);
|
||||
}
|
||||
}
|
||||
|
||||
// -- called by main thread
|
||||
|
||||
public $activateByEvent(activationEvent: string): TPromise<void> {
|
||||
return this.activateByEvent(activationEvent, false);
|
||||
}
|
||||
}
|
||||
|
||||
function loadCommonJSModule<T>(modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise<T> {
|
||||
let r: T = null;
|
||||
activationTimesBuilder.codeLoadingStart();
|
||||
try {
|
||||
r = require.__$__nodeRequire<T>(modulePath);
|
||||
} catch (e) {
|
||||
return TPromise.wrapError<T>(e);
|
||||
} finally {
|
||||
activationTimesBuilder.codeLoadingStop();
|
||||
}
|
||||
return TPromise.as(r);
|
||||
}
|
||||
|
||||
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription): any {
|
||||
let event = {
|
||||
id: extensionDescription.id,
|
||||
name: extensionDescription.name,
|
||||
publisherDisplayName: extensionDescription.publisher,
|
||||
activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,
|
||||
isBuiltin: extensionDescription.isBuiltin
|
||||
};
|
||||
|
||||
for (let contribution in extensionDescription.contributes) {
|
||||
let contributionDetails = extensionDescription.contributes[contribution];
|
||||
|
||||
if (!contributionDetails) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (contribution) {
|
||||
case 'debuggers':
|
||||
let types = contributionDetails.reduce((p, c) => p ? p + ',' + c['type'] : c['type'], '');
|
||||
event['contribution.debuggers'] = types;
|
||||
break;
|
||||
case 'grammars':
|
||||
let grammers = contributionDetails.reduce((p, c) => p ? p + ',' + c['language'] : c['language'], '');
|
||||
event['contribution.grammars'] = grammers;
|
||||
break;
|
||||
case 'languages':
|
||||
let languages = contributionDetails.reduce((p, c) => p ? p + ',' + c['id'] : c['id'], '');
|
||||
event['contribution.languages'] = languages;
|
||||
break;
|
||||
case 'tmSnippets':
|
||||
let tmSnippets = contributionDetails.reduce((p, c) => p ? p + ',' + c['languageId'] : c['languageId'], '');
|
||||
event['contribution.tmSnippets'] = tmSnippets;
|
||||
break;
|
||||
default:
|
||||
event[`contribution.${contribution}`] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
104
src/vs/workbench/api/node/extHostFileSystemEventService.ts
Normal file
104
src/vs/workbench/api/node/extHostFileSystemEventService.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from './extHostTypes';
|
||||
import { match } from 'vs/base/common/glob';
|
||||
import { Uri, FileSystemWatcher as _FileSystemWatcher } from 'vscode';
|
||||
import { FileSystemEvents, ExtHostFileSystemEventServiceShape } from './extHost.protocol';
|
||||
|
||||
class FileSystemWatcher implements _FileSystemWatcher {
|
||||
|
||||
private _onDidCreate = new Emitter<Uri>();
|
||||
private _onDidChange = new Emitter<Uri>();
|
||||
private _onDidDelete = new Emitter<Uri>();
|
||||
private _disposable: Disposable;
|
||||
private _config: number;
|
||||
|
||||
get ignoreCreateEvents(): boolean {
|
||||
return Boolean(this._config & 0b001);
|
||||
}
|
||||
|
||||
get ignoreChangeEvents(): boolean {
|
||||
return Boolean(this._config & 0b010);
|
||||
}
|
||||
|
||||
get ignoreDeleteEvents(): boolean {
|
||||
return Boolean(this._config & 0b100);
|
||||
}
|
||||
|
||||
constructor(dispatcher: Event<FileSystemEvents>, globPattern: string, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean) {
|
||||
|
||||
this._config = 0;
|
||||
if (ignoreCreateEvents) {
|
||||
this._config += 0b001;
|
||||
}
|
||||
if (ignoreChangeEvents) {
|
||||
this._config += 0b010;
|
||||
}
|
||||
if (ignoreDeleteEvents) {
|
||||
this._config += 0b100;
|
||||
}
|
||||
|
||||
let subscription = dispatcher(events => {
|
||||
if (!ignoreCreateEvents) {
|
||||
for (let created of events.created) {
|
||||
if (match(globPattern, created.fsPath)) {
|
||||
this._onDidCreate.fire(created);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ignoreChangeEvents) {
|
||||
for (let changed of events.changed) {
|
||||
if (match(globPattern, changed.fsPath)) {
|
||||
this._onDidChange.fire(changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ignoreDeleteEvents) {
|
||||
for (let deleted of events.deleted) {
|
||||
if (match(globPattern, deleted.fsPath)) {
|
||||
this._onDidDelete.fire(deleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._disposable = Disposable.from(this._onDidCreate, this._onDidChange, this._onDidDelete, subscription);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._disposable.dispose();
|
||||
}
|
||||
|
||||
get onDidCreate(): Event<Uri> {
|
||||
return this._onDidCreate.event;
|
||||
}
|
||||
|
||||
get onDidChange(): Event<Uri> {
|
||||
return this._onDidChange.event;
|
||||
}
|
||||
|
||||
get onDidDelete(): Event<Uri> {
|
||||
return this._onDidDelete.event;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServiceShape {
|
||||
|
||||
private _emitter = new Emitter<FileSystemEvents>();
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public createFileSystemWatcher(globPattern: string, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): _FileSystemWatcher {
|
||||
return new FileSystemWatcher(this._emitter.event, globPattern, ignoreCreateEvents, ignoreChangeEvents, ignoreDeleteEvents);
|
||||
}
|
||||
|
||||
$onFileEvent(events: FileSystemEvents) {
|
||||
this._emitter.fire(events);
|
||||
}
|
||||
}
|
||||
34
src/vs/workbench/api/node/extHostHeapService.ts
Normal file
34
src/vs/workbench/api/node/extHostHeapService.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { ExtHostHeapServiceShape } from './extHost.protocol';
|
||||
|
||||
export class ExtHostHeapService implements ExtHostHeapServiceShape {
|
||||
|
||||
private static _idPool = 0;
|
||||
|
||||
private _data = new Map<number, any>();
|
||||
|
||||
keep(obj: any): number {
|
||||
const id = ExtHostHeapService._idPool++;
|
||||
this._data.set(id, obj);
|
||||
return id;
|
||||
}
|
||||
|
||||
delete(id: number): boolean {
|
||||
return this._data.delete(id);
|
||||
}
|
||||
|
||||
get<T>(id: number): T {
|
||||
return this._data.get(id);
|
||||
}
|
||||
|
||||
$onGarbageCollection(ids: number[]): void {
|
||||
for (const id of ids) {
|
||||
this.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
1072
src/vs/workbench/api/node/extHostLanguageFeatures.ts
Normal file
1072
src/vs/workbench/api/node/extHostLanguageFeatures.ts
Normal file
File diff suppressed because it is too large
Load Diff
24
src/vs/workbench/api/node/extHostLanguages.ts
Normal file
24
src/vs/workbench/api/node/extHostLanguages.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { MainContext, MainThreadLanguagesShape, IMainContext } from './extHost.protocol';
|
||||
|
||||
export class ExtHostLanguages {
|
||||
|
||||
private _proxy: MainThreadLanguagesShape;
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext
|
||||
) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadLanguages);
|
||||
}
|
||||
|
||||
getLanguages(): TPromise<string[]> {
|
||||
return this._proxy.$getLanguages();
|
||||
}
|
||||
}
|
||||
|
||||
60
src/vs/workbench/api/node/extHostMessageService.ts
Normal file
60
src/vs/workbench/api/node/extHostMessageService.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import vscode = require('vscode');
|
||||
import { MainContext, MainThreadMessageServiceShape, MainThreadMessageOptions, IMainContext } from './extHost.protocol';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
|
||||
function isMessageItem<T>(item: any): item is vscode.MessageItem {
|
||||
return item && item.title;
|
||||
}
|
||||
|
||||
export class ExtHostMessageService {
|
||||
|
||||
private _proxy: MainThreadMessageServiceShape;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadMessageService);
|
||||
}
|
||||
|
||||
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string, rest: string[]): Thenable<string | undefined>;
|
||||
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | vscode.MessageItem, rest: vscode.MessageItem[]): Thenable<vscode.MessageItem | undefined>;
|
||||
showMessage(extension: IExtensionDescription, severity: Severity, message: string, optionsOrFirstItem: vscode.MessageOptions | string | vscode.MessageItem, rest: (string | vscode.MessageItem)[]): Thenable<string | vscode.MessageItem | undefined> {
|
||||
|
||||
let options: MainThreadMessageOptions = { extension };
|
||||
let items: (string | vscode.MessageItem)[];
|
||||
|
||||
if (typeof optionsOrFirstItem === 'string' || isMessageItem(optionsOrFirstItem)) {
|
||||
items = [optionsOrFirstItem, ...rest];
|
||||
} else {
|
||||
options.modal = optionsOrFirstItem && optionsOrFirstItem.modal;
|
||||
items = rest;
|
||||
}
|
||||
|
||||
const commands: { title: string; isCloseAffordance: boolean; handle: number; }[] = [];
|
||||
|
||||
for (let handle = 0; handle < items.length; handle++) {
|
||||
let command = items[handle];
|
||||
if (typeof command === 'string') {
|
||||
commands.push({ title: command, handle, isCloseAffordance: false });
|
||||
} else if (typeof command === 'object') {
|
||||
let { title, isCloseAffordance } = command;
|
||||
commands.push({ title, isCloseAffordance, handle });
|
||||
} else {
|
||||
console.warn('Invalid message item:', command);
|
||||
}
|
||||
}
|
||||
|
||||
return this._proxy.$showMessage(severity, message, options, commands).then(handle => {
|
||||
if (typeof handle === 'number') {
|
||||
return items[handle];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
78
src/vs/workbench/api/node/extHostOutputService.ts
Normal file
78
src/vs/workbench/api/node/extHostOutputService.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { MainContext, MainThreadOutputServiceShape, IMainContext } from './extHost.protocol';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export class ExtHostOutputChannel implements vscode.OutputChannel {
|
||||
|
||||
private static _idPool = 1;
|
||||
|
||||
private _proxy: MainThreadOutputServiceShape;
|
||||
private _name: string;
|
||||
private _id: string;
|
||||
private _disposed: boolean;
|
||||
|
||||
constructor(name: string, proxy: MainThreadOutputServiceShape) {
|
||||
this._name = name;
|
||||
this._id = 'extension-output-#' + (ExtHostOutputChannel._idPool++);
|
||||
this._proxy = proxy;
|
||||
}
|
||||
|
||||
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._proxy.$append(this._id, this._name, value);
|
||||
}
|
||||
|
||||
appendLine(value: string): void {
|
||||
this.append(value + '\n');
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this._proxy.$clear(this._id, this._name);
|
||||
}
|
||||
|
||||
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
|
||||
if (typeof columnOrPreserveFocus === 'boolean') {
|
||||
preserveFocus = columnOrPreserveFocus;
|
||||
}
|
||||
|
||||
this._proxy.$reveal(this._id, this._name, preserveFocus);
|
||||
}
|
||||
|
||||
hide(): void {
|
||||
this._proxy.$close(this._id);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostOutputService {
|
||||
|
||||
private _proxy: MainThreadOutputServiceShape;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadOutputService);
|
||||
}
|
||||
|
||||
createOutputChannel(name: string): vscode.OutputChannel {
|
||||
name = name.trim();
|
||||
if (!name) {
|
||||
throw new Error('illegal argument `name`. must not be falsy');
|
||||
} else {
|
||||
return new ExtHostOutputChannel(name, this._proxy);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/vs/workbench/api/node/extHostProgress.ts
Normal file
50
src/vs/workbench/api/node/extHostProgress.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Progress, ProgressOptions, CancellationToken } from 'vscode';
|
||||
import { MainThreadProgressShape } from './extHost.protocol';
|
||||
import { ProgressLocation } from './extHostTypeConverters';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { IProgressStep } from 'vs/platform/progress/common/progress';
|
||||
|
||||
export class ExtHostProgress {
|
||||
|
||||
private _proxy: MainThreadProgressShape;
|
||||
private _handles: number = 0;
|
||||
|
||||
constructor(proxy: MainThreadProgressShape) {
|
||||
this._proxy = proxy;
|
||||
}
|
||||
|
||||
withProgress<R>(extension: IExtensionDescription, options: ProgressOptions, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Thenable<R> {
|
||||
const handle = this._handles++;
|
||||
const { title, location } = options;
|
||||
this._proxy.$startProgress(handle, { location: ProgressLocation.from(location), title, tooltip: extension.name });
|
||||
return this._withProgress(handle, task);
|
||||
}
|
||||
|
||||
private _withProgress<R>(handle: number, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Thenable<R> {
|
||||
|
||||
const progress = {
|
||||
report: (p: IProgressStep) => {
|
||||
this._proxy.$progressReport(handle, p);
|
||||
}
|
||||
};
|
||||
|
||||
let p: Thenable<R>;
|
||||
|
||||
try {
|
||||
p = task(progress, null);
|
||||
} catch (err) {
|
||||
this._proxy.$progressEnd(handle);
|
||||
throw err;
|
||||
}
|
||||
|
||||
p.then(result => this._proxy.$progressEnd(handle), err => this._proxy.$progressEnd(handle));
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
120
src/vs/workbench/api/node/extHostQuickOpen.ts
Normal file
120
src/vs/workbench/api/node/extHostQuickOpen.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { wireCancellationToken } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { QuickPickOptions, QuickPickItem, InputBoxOptions } from 'vscode';
|
||||
import { MainContext, MainThreadQuickOpenShape, ExtHostQuickOpenShape, MyQuickPickItems, IMainContext } from './extHost.protocol';
|
||||
|
||||
export type Item = string | QuickPickItem;
|
||||
|
||||
export class ExtHostQuickOpen implements ExtHostQuickOpenShape {
|
||||
|
||||
private _proxy: MainThreadQuickOpenShape;
|
||||
private _onDidSelectItem: (handle: number) => void;
|
||||
private _validateInput: (input: string) => string;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadQuickOpen);
|
||||
}
|
||||
|
||||
showQuickPick(itemsOrItemsPromise: string[] | Thenable<string[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<string | undefined>;
|
||||
showQuickPick(itemsOrItemsPromise: QuickPickItem[] | Thenable<QuickPickItem[]>, options?: QuickPickOptions, token?: CancellationToken): Thenable<QuickPickItem | undefined>;
|
||||
showQuickPick(itemsOrItemsPromise: Item[] | Thenable<Item[]>, options?: QuickPickOptions, token: CancellationToken = CancellationToken.None): Thenable<Item | undefined> {
|
||||
|
||||
// clear state from last invocation
|
||||
this._onDidSelectItem = undefined;
|
||||
|
||||
const itemsPromise = <TPromise<Item[]>>TPromise.wrap(itemsOrItemsPromise);
|
||||
|
||||
const quickPickWidget = this._proxy.$show({
|
||||
autoFocus: { autoFocusFirstEntry: true },
|
||||
placeHolder: options && options.placeHolder,
|
||||
matchOnDescription: options && options.matchOnDescription,
|
||||
matchOnDetail: options && options.matchOnDetail,
|
||||
ignoreFocusLost: options && options.ignoreFocusOut
|
||||
});
|
||||
|
||||
const promise = TPromise.any(<TPromise<number | Item[]>[]>[quickPickWidget, itemsPromise]).then(values => {
|
||||
if (values.key === '0') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return itemsPromise.then(items => {
|
||||
|
||||
let pickItems: MyQuickPickItems[] = [];
|
||||
for (let handle = 0; handle < items.length; handle++) {
|
||||
|
||||
let item = items[handle];
|
||||
let label: string;
|
||||
let description: string;
|
||||
let detail: string;
|
||||
|
||||
if (typeof item === 'string') {
|
||||
label = item;
|
||||
} else {
|
||||
label = item.label;
|
||||
description = item.description;
|
||||
detail = item.detail;
|
||||
}
|
||||
pickItems.push({
|
||||
label,
|
||||
description,
|
||||
handle,
|
||||
detail
|
||||
});
|
||||
}
|
||||
|
||||
// handle selection changes
|
||||
if (options && typeof options.onDidSelectItem === 'function') {
|
||||
this._onDidSelectItem = (handle) => {
|
||||
options.onDidSelectItem(items[handle]);
|
||||
};
|
||||
}
|
||||
|
||||
// show items
|
||||
this._proxy.$setItems(pickItems);
|
||||
|
||||
return quickPickWidget.then(handle => {
|
||||
if (typeof handle === 'number') {
|
||||
return items[handle];
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}, (err) => {
|
||||
this._proxy.$setError(err);
|
||||
|
||||
return TPromise.wrapError(err);
|
||||
});
|
||||
});
|
||||
return wireCancellationToken<Item>(token, promise, true);
|
||||
}
|
||||
|
||||
$onItemSelected(handle: number): void {
|
||||
if (this._onDidSelectItem) {
|
||||
this._onDidSelectItem(handle);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- input
|
||||
|
||||
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(options, typeof this._validateInput === 'function');
|
||||
return wireCancellationToken(token, promise, true);
|
||||
}
|
||||
|
||||
$validateInput(input: string): TPromise<string> {
|
||||
if (this._validateInput) {
|
||||
return TPromise.as(this._validateInput(input));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
367
src/vs/workbench/api/node/extHostSCM.ts
Normal file
367
src/vs/workbench/api/node/extHostSCM.ts
Normal file
@@ -0,0 +1,367 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { asWinJsPromise } from 'vs/base/common/async';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
|
||||
import { MainContext, MainThreadSCMShape, SCMRawResource, IMainContext } from './extHost.protocol';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
function getIconPath(decorations: vscode.SourceControlResourceThemableDecorations) {
|
||||
if (!decorations) {
|
||||
return undefined;
|
||||
} else if (typeof decorations.iconPath === 'string') {
|
||||
return URI.file(decorations.iconPath).toString();
|
||||
} else if (decorations.iconPath) {
|
||||
return `${decorations.iconPath}`;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export class ExtHostSCMInputBox {
|
||||
|
||||
private _value: string = '';
|
||||
|
||||
get value(): string {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set value(value: string) {
|
||||
this._proxy.$setInputBoxValue(this._sourceControlHandle, value);
|
||||
this.updateValue(value);
|
||||
}
|
||||
|
||||
private _onDidChange = new Emitter<string>();
|
||||
|
||||
get onDidChange(): Event<string> {
|
||||
return this._onDidChange.event;
|
||||
}
|
||||
|
||||
constructor(private _proxy: MainThreadSCMShape, private _sourceControlHandle: number) {
|
||||
// noop
|
||||
}
|
||||
|
||||
$onInputBoxValueChange(value: string): void {
|
||||
this.updateValue(value);
|
||||
}
|
||||
|
||||
private updateValue(value: string): void {
|
||||
this._value = value;
|
||||
this._onDidChange.fire(value);
|
||||
}
|
||||
}
|
||||
|
||||
class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceGroup {
|
||||
|
||||
private static _handlePool: number = 0;
|
||||
private _resourceHandlePool: number = 0;
|
||||
private _resourceStates: vscode.SourceControlResourceState[] = [];
|
||||
private _resourceStatesRollingDisposables: { (): void }[] = [];
|
||||
private _resourceStatesMap: Map<ResourceStateHandle, vscode.SourceControlResourceState> = new Map<ResourceStateHandle, vscode.SourceControlResourceState>();
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get label(): string {
|
||||
return this._label;
|
||||
}
|
||||
|
||||
set label(label: string) {
|
||||
this._label = label;
|
||||
this._proxy.$updateGroupLabel(this._sourceControlHandle, this._handle, label);
|
||||
}
|
||||
|
||||
private _hideWhenEmpty: boolean | undefined = undefined;
|
||||
|
||||
get hideWhenEmpty(): boolean | undefined {
|
||||
return this._hideWhenEmpty;
|
||||
}
|
||||
|
||||
set hideWhenEmpty(hideWhenEmpty: boolean | undefined) {
|
||||
this._hideWhenEmpty = hideWhenEmpty;
|
||||
this._proxy.$updateGroup(this._sourceControlHandle, this._handle, { hideWhenEmpty });
|
||||
}
|
||||
|
||||
get resourceStates(): vscode.SourceControlResourceState[] {
|
||||
return [...this._resourceStates];
|
||||
}
|
||||
|
||||
set resourceStates(resources: vscode.SourceControlResourceState[]) {
|
||||
this._resourceStates = [...resources];
|
||||
|
||||
const handles: number[] = [];
|
||||
const rawResources = resources.map(r => {
|
||||
const handle = this._resourceHandlePool++;
|
||||
this._resourceStatesMap.set(handle, r);
|
||||
handles.push(handle);
|
||||
|
||||
const sourceUri = r.resourceUri.toString();
|
||||
const command = this._commands.toInternal(r.command);
|
||||
const iconPath = getIconPath(r.decorations);
|
||||
const lightIconPath = r.decorations && getIconPath(r.decorations.light) || iconPath;
|
||||
const darkIconPath = r.decorations && getIconPath(r.decorations.dark) || iconPath;
|
||||
const icons: string[] = [];
|
||||
|
||||
if (lightIconPath || darkIconPath) {
|
||||
icons.push(lightIconPath);
|
||||
}
|
||||
|
||||
if (darkIconPath !== lightIconPath) {
|
||||
icons.push(darkIconPath);
|
||||
}
|
||||
|
||||
const tooltip = (r.decorations && r.decorations.tooltip) || '';
|
||||
const strikeThrough = r.decorations && !!r.decorations.strikeThrough;
|
||||
const faded = r.decorations && !!r.decorations.faded;
|
||||
|
||||
return [handle, sourceUri, command, icons, tooltip, strikeThrough, faded] as SCMRawResource;
|
||||
});
|
||||
|
||||
const disposable = () => handles.forEach(handle => this._resourceStatesMap.delete(handle));
|
||||
this._resourceStatesRollingDisposables.push(disposable);
|
||||
|
||||
while (this._resourceStatesRollingDisposables.length >= 10) {
|
||||
this._resourceStatesRollingDisposables.shift()();
|
||||
}
|
||||
|
||||
this._proxy.$updateGroupResourceStates(this._sourceControlHandle, this._handle, rawResources);
|
||||
}
|
||||
|
||||
private _handle: GroupHandle = ExtHostSourceControlResourceGroup._handlePool++;
|
||||
get handle(): GroupHandle {
|
||||
return this._handle;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private _proxy: MainThreadSCMShape,
|
||||
private _commands: CommandsConverter,
|
||||
private _sourceControlHandle: number,
|
||||
private _id: string,
|
||||
private _label: string,
|
||||
) {
|
||||
this._proxy.$registerGroup(_sourceControlHandle, this._handle, _id, _label);
|
||||
}
|
||||
|
||||
getResourceState(handle: number): vscode.SourceControlResourceState | undefined {
|
||||
return this._resourceStatesMap.get(handle);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._proxy.$unregisterGroup(this._sourceControlHandle, this._handle);
|
||||
}
|
||||
}
|
||||
|
||||
class ExtHostSourceControl implements vscode.SourceControl {
|
||||
|
||||
private static _handlePool: number = 0;
|
||||
private _groups: Map<GroupHandle, ExtHostSourceControlResourceGroup> = new Map<GroupHandle, ExtHostSourceControlResourceGroup>();
|
||||
|
||||
get id(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get label(): string {
|
||||
return this._label;
|
||||
}
|
||||
|
||||
private _inputBox: ExtHostSCMInputBox;
|
||||
get inputBox(): ExtHostSCMInputBox { return this._inputBox; }
|
||||
|
||||
private _count: number | undefined = undefined;
|
||||
|
||||
get count(): number | undefined {
|
||||
return this._count;
|
||||
}
|
||||
|
||||
set count(count: number | undefined) {
|
||||
this._count = count;
|
||||
this._proxy.$updateSourceControl(this._handle, { count });
|
||||
}
|
||||
|
||||
private _quickDiffProvider: vscode.QuickDiffProvider | undefined = undefined;
|
||||
|
||||
get quickDiffProvider(): vscode.QuickDiffProvider | undefined {
|
||||
return this._quickDiffProvider;
|
||||
}
|
||||
|
||||
set quickDiffProvider(quickDiffProvider: vscode.QuickDiffProvider | undefined) {
|
||||
this._quickDiffProvider = quickDiffProvider;
|
||||
this._proxy.$updateSourceControl(this._handle, { hasQuickDiffProvider: !!quickDiffProvider });
|
||||
}
|
||||
|
||||
private _commitTemplate: string | undefined = undefined;
|
||||
|
||||
get commitTemplate(): string | undefined {
|
||||
return this._commitTemplate;
|
||||
}
|
||||
|
||||
set commitTemplate(commitTemplate: string | undefined) {
|
||||
this._commitTemplate = commitTemplate;
|
||||
this._proxy.$updateSourceControl(this._handle, { commitTemplate });
|
||||
}
|
||||
|
||||
private _acceptInputCommand: vscode.Command | undefined = undefined;
|
||||
|
||||
get acceptInputCommand(): vscode.Command | undefined {
|
||||
return this._acceptInputCommand;
|
||||
}
|
||||
|
||||
set acceptInputCommand(acceptInputCommand: vscode.Command | undefined) {
|
||||
this._acceptInputCommand = acceptInputCommand;
|
||||
|
||||
const internal = this._commands.toInternal(acceptInputCommand);
|
||||
this._proxy.$updateSourceControl(this._handle, { acceptInputCommand: internal });
|
||||
}
|
||||
|
||||
private _statusBarCommands: vscode.Command[] | undefined = undefined;
|
||||
|
||||
get statusBarCommands(): vscode.Command[] | undefined {
|
||||
return this._statusBarCommands;
|
||||
}
|
||||
|
||||
set statusBarCommands(statusBarCommands: vscode.Command[] | undefined) {
|
||||
this._statusBarCommands = statusBarCommands;
|
||||
|
||||
const internal = (statusBarCommands || []).map(c => this._commands.toInternal(c));
|
||||
this._proxy.$updateSourceControl(this._handle, { statusBarCommands: internal });
|
||||
}
|
||||
|
||||
private _handle: number = ExtHostSourceControl._handlePool++;
|
||||
|
||||
constructor(
|
||||
private _proxy: MainThreadSCMShape,
|
||||
private _commands: CommandsConverter,
|
||||
private _id: string,
|
||||
private _label: string,
|
||||
) {
|
||||
this._inputBox = new ExtHostSCMInputBox(this._proxy, this._handle);
|
||||
this._proxy.$registerSourceControl(this._handle, _id, _label);
|
||||
}
|
||||
|
||||
createResourceGroup(id: string, label: string): ExtHostSourceControlResourceGroup {
|
||||
const group = new ExtHostSourceControlResourceGroup(this._proxy, this._commands, this._handle, id, label);
|
||||
this._groups.set(group.handle, group);
|
||||
return group;
|
||||
}
|
||||
|
||||
getResourceGroup(handle: GroupHandle): ExtHostSourceControlResourceGroup | undefined {
|
||||
return this._groups.get(handle);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._proxy.$unregisterSourceControl(this._handle);
|
||||
}
|
||||
}
|
||||
|
||||
type ProviderHandle = number;
|
||||
type GroupHandle = number;
|
||||
type ResourceStateHandle = number;
|
||||
|
||||
export class ExtHostSCM {
|
||||
|
||||
private static _handlePool: number = 0;
|
||||
|
||||
private _proxy: MainThreadSCMShape;
|
||||
private _sourceControls: Map<ProviderHandle, ExtHostSourceControl> = new Map<ProviderHandle, ExtHostSourceControl>();
|
||||
private _sourceControlsByExtension: Map<string, ExtHostSourceControl[]> = new Map<string, ExtHostSourceControl[]>();
|
||||
|
||||
private _onDidChangeActiveProvider = new Emitter<vscode.SourceControl>();
|
||||
get onDidChangeActiveProvider(): Event<vscode.SourceControl> { return this._onDidChangeActiveProvider.event; }
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
private _commands: ExtHostCommands
|
||||
) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadSCM);
|
||||
|
||||
_commands.registerArgumentProcessor({
|
||||
processArgument: arg => {
|
||||
if (arg && arg.$mid === 3) {
|
||||
const sourceControl = this._sourceControls.get(arg.sourceControlHandle);
|
||||
|
||||
if (!sourceControl) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
const group = sourceControl.getResourceGroup(arg.groupHandle);
|
||||
|
||||
if (!group) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
return group.getResourceState(arg.handle);
|
||||
} else if (arg && arg.$mid === 4) {
|
||||
const sourceControl = this._sourceControls.get(arg.sourceControlHandle);
|
||||
|
||||
if (!sourceControl) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
return sourceControl.getResourceGroup(arg.groupHandle);
|
||||
} else if (arg && arg.$mid === 5) {
|
||||
const sourceControl = this._sourceControls.get(arg.handle);
|
||||
|
||||
if (!sourceControl) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
return sourceControl;
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createSourceControl(extension: IExtensionDescription, id: string, label: string): vscode.SourceControl {
|
||||
const handle = ExtHostSCM._handlePool++;
|
||||
const sourceControl = new ExtHostSourceControl(this._proxy, this._commands.converter, id, label);
|
||||
this._sourceControls.set(handle, sourceControl);
|
||||
|
||||
const sourceControls = this._sourceControlsByExtension.get(extension.id) || [];
|
||||
sourceControls.push(sourceControl);
|
||||
this._sourceControlsByExtension.set(extension.id, sourceControls);
|
||||
|
||||
return sourceControl;
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
getLastInputBox(extension: IExtensionDescription): ExtHostSCMInputBox {
|
||||
const sourceControls = this._sourceControlsByExtension.get(extension.id);
|
||||
const sourceControl = sourceControls && sourceControls[sourceControls.length - 1];
|
||||
const inputBox = sourceControl && sourceControl.inputBox;
|
||||
|
||||
return inputBox;
|
||||
}
|
||||
|
||||
$provideOriginalResource(sourceControlHandle: number, uri: URI): TPromise<URI> {
|
||||
const sourceControl = this._sourceControls.get(sourceControlHandle);
|
||||
|
||||
if (!sourceControl || !sourceControl.quickDiffProvider) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
return asWinJsPromise(token => {
|
||||
const result = sourceControl.quickDiffProvider.provideOriginalResource(uri, token);
|
||||
return result && URI.parse(result.toString());
|
||||
});
|
||||
}
|
||||
|
||||
$onInputBoxValueChange(sourceControlHandle: number, value: string): TPromise<void> {
|
||||
const sourceControl = this._sourceControls.get(sourceControlHandle);
|
||||
|
||||
if (!sourceControl || !sourceControl.quickDiffProvider) {
|
||||
return TPromise.as(null);
|
||||
}
|
||||
|
||||
sourceControl.inputBox.$onInputBoxValueChange(value);
|
||||
return TPromise.as(null);
|
||||
}
|
||||
}
|
||||
190
src/vs/workbench/api/node/extHostStatusBar.ts
Normal file
190
src/vs/workbench/api/node/extHostStatusBar.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { StatusbarAlignment as MainThreadStatusBarAlignment } from 'vs/platform/statusbar/common/statusbar';
|
||||
import { StatusBarAlignment as ExtHostStatusBarAlignment, Disposable, ThemeColor } from './extHostTypes';
|
||||
import { StatusBarItem, StatusBarAlignment } from 'vscode';
|
||||
import { MainContext, MainThreadStatusBarShape, IMainContext } from './extHost.protocol';
|
||||
|
||||
export class ExtHostStatusBarEntry implements StatusBarItem {
|
||||
private static ID_GEN = 0;
|
||||
|
||||
private _id: number;
|
||||
private _alignment: number;
|
||||
private _priority: number;
|
||||
private _disposed: boolean;
|
||||
private _visible: boolean;
|
||||
|
||||
private _text: string;
|
||||
private _tooltip: string;
|
||||
private _color: string | ThemeColor;
|
||||
private _command: string;
|
||||
|
||||
private _timeoutHandle: number;
|
||||
private _proxy: MainThreadStatusBarShape;
|
||||
|
||||
private _extensionId: string;
|
||||
|
||||
constructor(proxy: MainThreadStatusBarShape, extensionId: string, alignment: ExtHostStatusBarAlignment = ExtHostStatusBarAlignment.Left, priority?: number) {
|
||||
this._id = ExtHostStatusBarEntry.ID_GEN++;
|
||||
this._proxy = proxy;
|
||||
this._alignment = alignment;
|
||||
this._priority = priority;
|
||||
this._extensionId = extensionId;
|
||||
}
|
||||
|
||||
public get id(): number {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get alignment(): StatusBarAlignment {
|
||||
return this._alignment;
|
||||
}
|
||||
|
||||
public get priority(): number {
|
||||
return this._priority;
|
||||
}
|
||||
|
||||
public get text(): string {
|
||||
return this._text;
|
||||
}
|
||||
|
||||
public get tooltip(): string {
|
||||
return this._tooltip;
|
||||
}
|
||||
|
||||
public get color(): string | ThemeColor {
|
||||
return this._color;
|
||||
}
|
||||
|
||||
public get command(): string {
|
||||
return this._command;
|
||||
}
|
||||
|
||||
public set text(text: string) {
|
||||
this._text = text;
|
||||
this.update();
|
||||
}
|
||||
|
||||
public set tooltip(tooltip: string) {
|
||||
this._tooltip = tooltip;
|
||||
this.update();
|
||||
}
|
||||
|
||||
public set color(color: string | ThemeColor) {
|
||||
this._color = color;
|
||||
this.update();
|
||||
}
|
||||
|
||||
public set command(command: string) {
|
||||
this._command = command;
|
||||
this.update();
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
this._visible = true;
|
||||
this.update();
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
clearTimeout(this._timeoutHandle);
|
||||
this._visible = false;
|
||||
this._proxy.$dispose(this.id);
|
||||
}
|
||||
|
||||
private update(): void {
|
||||
if (this._disposed || !this._visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(this._timeoutHandle);
|
||||
|
||||
// Defer the update so that multiple changes to setters dont cause a redraw each
|
||||
this._timeoutHandle = setTimeout(() => {
|
||||
this._timeoutHandle = undefined;
|
||||
|
||||
// Set to status bar
|
||||
this._proxy.$setEntry(this.id, this._extensionId, this.text, this.tooltip, this.command, this.color,
|
||||
this._alignment === ExtHostStatusBarAlignment.Left ? MainThreadStatusBarAlignment.LEFT : MainThreadStatusBarAlignment.RIGHT,
|
||||
this._priority);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.hide();
|
||||
this._disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
class StatusBarMessage {
|
||||
|
||||
private _item: StatusBarItem;
|
||||
private _messages: { message: string }[] = [];
|
||||
|
||||
constructor(statusBar: ExtHostStatusBar) {
|
||||
this._item = statusBar.createStatusBarEntry(void 0, ExtHostStatusBarAlignment.Left, Number.MIN_VALUE);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._messages.length = 0;
|
||||
this._item.dispose();
|
||||
}
|
||||
|
||||
setMessage(message: string): Disposable {
|
||||
const data: { message: string } = { message }; // use object to not confuse equal strings
|
||||
this._messages.unshift(data);
|
||||
this._update();
|
||||
|
||||
return new Disposable(() => {
|
||||
let idx = this._messages.indexOf(data);
|
||||
if (idx >= 0) {
|
||||
this._messages.splice(idx, 1);
|
||||
this._update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _update() {
|
||||
if (this._messages.length > 0) {
|
||||
this._item.text = this._messages[0].message;
|
||||
this._item.show();
|
||||
} else {
|
||||
this._item.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostStatusBar {
|
||||
|
||||
private _proxy: MainThreadStatusBarShape;
|
||||
private _statusMessage: StatusBarMessage;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadStatusBar);
|
||||
this._statusMessage = new StatusBarMessage(this);
|
||||
}
|
||||
|
||||
createStatusBarEntry(extensionId: string, alignment?: ExtHostStatusBarAlignment, priority?: number): StatusBarItem {
|
||||
return new ExtHostStatusBarEntry(this._proxy, extensionId, alignment, priority);
|
||||
}
|
||||
|
||||
setStatusBarMessage(text: string, timeoutOrThenable?: number | Thenable<any>): Disposable {
|
||||
|
||||
let d = this._statusMessage.setMessage(text);
|
||||
let handle: number;
|
||||
|
||||
if (typeof timeoutOrThenable === 'number') {
|
||||
handle = setTimeout(() => d.dispose(), timeoutOrThenable);
|
||||
} else if (typeof timeoutOrThenable !== 'undefined') {
|
||||
timeoutOrThenable.then(() => d.dispose(), () => d.dispose());
|
||||
}
|
||||
|
||||
return new Disposable(() => {
|
||||
d.dispose();
|
||||
clearTimeout(handle);
|
||||
});
|
||||
}
|
||||
}
|
||||
25
src/vs/workbench/api/node/extHostStorage.ts
Normal file
25
src/vs/workbench/api/node/extHostStorage.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { MainContext, MainThreadStorageShape, IMainContext } from './extHost.protocol';
|
||||
|
||||
export class ExtHostStorage {
|
||||
|
||||
private _proxy: MainThreadStorageShape;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadStorage);
|
||||
}
|
||||
|
||||
getValue<T>(shared: boolean, key: string, defaultValue?: T): TPromise<T> {
|
||||
return this._proxy.$getValue<T>(shared, key).then(value => value || defaultValue);
|
||||
}
|
||||
|
||||
setValue(shared: boolean, key: string, value: any): TPromise<void> {
|
||||
return this._proxy.$setValue(shared, key, value);
|
||||
}
|
||||
}
|
||||
439
src/vs/workbench/api/node/extHostTask.ts
Normal file
439
src/vs/workbench/api/node/extHostTask.ts
Normal file
@@ -0,0 +1,439 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * 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 { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import * as TaskSystem from 'vs/workbench/parts/tasks/common/tasks';
|
||||
|
||||
import { MainContext, MainThreadTaskShape, ExtHostTaskShape, IMainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
|
||||
import * as types from 'vs/workbench/api/node/extHostTypes';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
interface StringMap<V> {
|
||||
[key: string]: V;
|
||||
}
|
||||
|
||||
/*
|
||||
namespace ProblemPattern {
|
||||
export function from(value: vscode.ProblemPattern | vscode.MultiLineProblemPattern): Problems.ProblemPattern | Problems.MultiLineProblemPattern {
|
||||
if (value === void 0 || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
let result: Problems.ProblemPattern[] = [];
|
||||
for (let pattern of value) {
|
||||
let converted = fromSingle(pattern);
|
||||
if (!converted) {
|
||||
return undefined;
|
||||
}
|
||||
result.push(converted);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return fromSingle(value);
|
||||
}
|
||||
}
|
||||
|
||||
function copyProperty(target: Problems.ProblemPattern, source: vscode.ProblemPattern, tk: keyof Problems.ProblemPattern) {
|
||||
let sk: keyof vscode.ProblemPattern = tk;
|
||||
let value = source[sk];
|
||||
if (typeof value === 'number') {
|
||||
target[tk] = value;
|
||||
}
|
||||
}
|
||||
|
||||
function getValue(value: number, defaultValue: number): number {
|
||||
if (value !== void 0 && value === null) {
|
||||
return value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
function fromSingle(problemPattern: vscode.ProblemPattern): Problems.ProblemPattern {
|
||||
if (problemPattern === void 0 || problemPattern === null || !(problemPattern.regexp instanceof RegExp)) {
|
||||
return undefined;
|
||||
}
|
||||
let result: Problems.ProblemPattern = {
|
||||
regexp: problemPattern.regexp
|
||||
};
|
||||
copyProperty(result, problemPattern, 'file');
|
||||
copyProperty(result, problemPattern, 'location');
|
||||
copyProperty(result, problemPattern, 'line');
|
||||
copyProperty(result, problemPattern, 'character');
|
||||
copyProperty(result, problemPattern, 'endLine');
|
||||
copyProperty(result, problemPattern, 'endCharacter');
|
||||
copyProperty(result, problemPattern, 'severity');
|
||||
copyProperty(result, problemPattern, 'code');
|
||||
copyProperty(result, problemPattern, 'message');
|
||||
if (problemPattern.loop === true || problemPattern.loop === false) {
|
||||
result.loop = problemPattern.loop;
|
||||
}
|
||||
if (result.location) {
|
||||
result.file = getValue(result.file, 1);
|
||||
result.message = getValue(result.message, 0);
|
||||
} else {
|
||||
result.file = getValue(result.file, 1);
|
||||
result.line = getValue(result.line, 2);
|
||||
result.character = getValue(result.character, 3);
|
||||
result.message = getValue(result.message, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ApplyTo {
|
||||
export function from(value: vscode.ApplyToKind): Problems.ApplyToKind {
|
||||
if (value === void 0 || value === null) {
|
||||
return Problems.ApplyToKind.allDocuments;
|
||||
}
|
||||
switch (value) {
|
||||
case types.ApplyToKind.OpenDocuments:
|
||||
return Problems.ApplyToKind.openDocuments;
|
||||
case types.ApplyToKind.ClosedDocuments:
|
||||
return Problems.ApplyToKind.closedDocuments;
|
||||
}
|
||||
return Problems.ApplyToKind.allDocuments;
|
||||
}
|
||||
}
|
||||
|
||||
namespace FileLocation {
|
||||
export function from(value: vscode.FileLocationKind | string): { kind: Problems.FileLocationKind; prefix?: string } {
|
||||
if (value === void 0 || value === null) {
|
||||
return { kind: Problems.FileLocationKind.Auto };
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return { kind: Problems.FileLocationKind.Relative, prefix: value };
|
||||
}
|
||||
switch (value) {
|
||||
case types.FileLocationKind.Absolute:
|
||||
return { kind: Problems.FileLocationKind.Absolute };
|
||||
case types.FileLocationKind.Relative:
|
||||
return { kind: Problems.FileLocationKind.Relative, prefix: '${workspaceRoot}' };
|
||||
}
|
||||
return { kind: Problems.FileLocationKind.Auto };
|
||||
}
|
||||
}
|
||||
|
||||
namespace WatchingPattern {
|
||||
export function from(value: RegExp | vscode.BackgroundPattern): Problems.WatchingPattern {
|
||||
if (value === void 0 || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
if (value instanceof RegExp) {
|
||||
return { regexp: value };
|
||||
}
|
||||
if (!(value.regexp instanceof RegExp)) {
|
||||
return undefined;
|
||||
}
|
||||
let result: Problems.WatchingPattern = {
|
||||
regexp: value.regexp
|
||||
};
|
||||
if (typeof value.file === 'number') {
|
||||
result.file = value.file;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace BackgroundMonitor {
|
||||
export function from(value: vscode.BackgroundMonitor): Problems.WatchingMatcher {
|
||||
if (value === void 0 || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let result: Problems.WatchingMatcher = {
|
||||
activeOnStart: !!value.activeOnStart,
|
||||
beginsPattern: WatchingPattern.from(value.beginsPattern),
|
||||
endsPattern: WatchingPattern.from(value.endsPattern)
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ProblemMatcher {
|
||||
export function from(values: (string | vscode.ProblemMatcher)[]): (string | Problems.ProblemMatcher)[] {
|
||||
if (values === void 0 || values === null) {
|
||||
return undefined;
|
||||
}
|
||||
let result: (string | Problems.ProblemMatcher)[] = [];
|
||||
for (let value of values) {
|
||||
let converted = typeof value === 'string' ? value : fromSingle(value);
|
||||
if (converted) {
|
||||
result.push(converted);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function fromSingle(problemMatcher: vscode.ProblemMatcher): Problems.ProblemMatcher {
|
||||
if (problemMatcher === void 0 || problemMatcher === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let location = FileLocation.from(problemMatcher.fileLocation);
|
||||
let result: Problems.ProblemMatcher = {
|
||||
owner: typeof problemMatcher.owner === 'string' ? problemMatcher.owner : UUID.generateUuid(),
|
||||
applyTo: ApplyTo.from(problemMatcher.applyTo),
|
||||
fileLocation: location.kind,
|
||||
filePrefix: location.prefix,
|
||||
pattern: ProblemPattern.from(problemMatcher.pattern),
|
||||
severity: fromDiagnosticSeverity(problemMatcher.severity),
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
namespace TaskRevealKind {
|
||||
export function from(value: vscode.TaskRevealKind): TaskSystem.RevealKind {
|
||||
if (value === void 0 || value === null) {
|
||||
return TaskSystem.RevealKind.Always;
|
||||
}
|
||||
switch (value) {
|
||||
case types.TaskRevealKind.Silent:
|
||||
return TaskSystem.RevealKind.Silent;
|
||||
case types.TaskRevealKind.Never:
|
||||
return TaskSystem.RevealKind.Never;
|
||||
}
|
||||
return TaskSystem.RevealKind.Always;
|
||||
}
|
||||
}
|
||||
|
||||
namespace TaskPanelKind {
|
||||
export function from(value: vscode.TaskPanelKind): TaskSystem.PanelKind {
|
||||
if (value === void 0 || value === null) {
|
||||
return TaskSystem.PanelKind.Shared;
|
||||
}
|
||||
switch (value) {
|
||||
case types.TaskPanelKind.Dedicated:
|
||||
return TaskSystem.PanelKind.Dedicated;
|
||||
case types.TaskPanelKind.New:
|
||||
return TaskSystem.PanelKind.New;
|
||||
default:
|
||||
return TaskSystem.PanelKind.Shared;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace PresentationOptions {
|
||||
export function from(value: vscode.TaskPresentationOptions): TaskSystem.PresentationOptions {
|
||||
if (value === void 0 || value === null) {
|
||||
return { reveal: TaskSystem.RevealKind.Always, echo: true, focus: false, panel: TaskSystem.PanelKind.Shared };
|
||||
}
|
||||
return {
|
||||
reveal: TaskRevealKind.from(value.reveal),
|
||||
echo: value.echo === void 0 ? true : !!value.echo,
|
||||
focus: !!value.focus,
|
||||
panel: TaskPanelKind.from(value.panel)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace Strings {
|
||||
export function from(value: string[]): string[] {
|
||||
if (value === void 0 || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
for (let element of value) {
|
||||
if (typeof element !== 'string') {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
namespace CommandOptions {
|
||||
function isShellConfiguration(value: any): value is { executable: string; shellArgs?: string[] } {
|
||||
return value && typeof value.executable === 'string';
|
||||
}
|
||||
export function from(value: vscode.ShellExecutionOptions | vscode.ProcessExecutionOptions): TaskSystem.CommandOptions {
|
||||
if (value === void 0 || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
let result: TaskSystem.CommandOptions = {
|
||||
};
|
||||
if (typeof value.cwd === 'string') {
|
||||
result.cwd = value.cwd;
|
||||
}
|
||||
if (value.env) {
|
||||
result.env = Object.create(null);
|
||||
Object.keys(value.env).forEach(key => {
|
||||
let envValue = value.env[key];
|
||||
if (typeof envValue === 'string') {
|
||||
result.env[key] = envValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (isShellConfiguration(value)) {
|
||||
result.shell = ShellConfiguration.from(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace ShellConfiguration {
|
||||
export function from(value: { executable?: string, shellArgs?: string[] }): TaskSystem.ShellConfiguration {
|
||||
if (value === void 0 || value === null || !value.executable) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let result: TaskSystem.ShellConfiguration = {
|
||||
executable: value.executable,
|
||||
args: Strings.from(value.shellArgs)
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Tasks {
|
||||
|
||||
export function from(tasks: vscode.Task[], extension: IExtensionDescription): TaskSystem.Task[] {
|
||||
if (tasks === void 0 || tasks === null) {
|
||||
return [];
|
||||
}
|
||||
let result: TaskSystem.Task[] = [];
|
||||
for (let task of tasks) {
|
||||
let converted = fromSingle(task, extension);
|
||||
if (converted) {
|
||||
result.push(converted);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function fromSingle(task: vscode.Task, extension: IExtensionDescription): TaskSystem.ContributedTask {
|
||||
if (typeof task.name !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
let command: TaskSystem.CommandConfiguration;
|
||||
let execution = task.execution;
|
||||
if (execution instanceof types.ProcessExecution) {
|
||||
command = getProcessCommand(execution);
|
||||
} else if (execution instanceof types.ShellExecution) {
|
||||
command = getShellCommand(execution);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
if (command === void 0) {
|
||||
return undefined;
|
||||
}
|
||||
command.presentation = PresentationOptions.from(task.presentationOptions);
|
||||
let source = {
|
||||
kind: TaskSystem.TaskSourceKind.Extension,
|
||||
label: typeof task.source === 'string' ? task.source : extension.name,
|
||||
extension: extension.id
|
||||
};
|
||||
let label = nls.localize('task.label', '{0}: {1}', source.label, task.name);
|
||||
let key = (task as types.Task).definitionKey;
|
||||
let kind = (task as types.Task).definition;
|
||||
let id = `${extension.id}.${key}`;
|
||||
let taskKind: TaskSystem.TaskIdentifier = {
|
||||
_key: key,
|
||||
type: kind.type
|
||||
};
|
||||
Objects.assign(taskKind, kind);
|
||||
let result: TaskSystem.ContributedTask = {
|
||||
_id: id, // uuidMap.getUUID(identifier),
|
||||
_source: source,
|
||||
_label: label,
|
||||
type: kind.type,
|
||||
defines: taskKind,
|
||||
name: task.name,
|
||||
identifier: label,
|
||||
group: task.group ? (task.group as types.TaskGroup).id : undefined,
|
||||
command: command,
|
||||
isBackground: !!task.isBackground,
|
||||
problemMatchers: task.problemMatchers.slice(),
|
||||
hasDefinedMatchers: (task as types.Task).hasDefinedMatchers
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
function getProcessCommand(value: vscode.ProcessExecution): TaskSystem.CommandConfiguration {
|
||||
if (typeof value.process !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
let result: TaskSystem.CommandConfiguration = {
|
||||
name: value.process,
|
||||
args: Strings.from(value.args),
|
||||
runtime: TaskSystem.RuntimeType.Process,
|
||||
suppressTaskName: true,
|
||||
presentation: undefined
|
||||
};
|
||||
if (value.options) {
|
||||
result.options = CommandOptions.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getShellCommand(value: vscode.ShellExecution): TaskSystem.CommandConfiguration {
|
||||
if (typeof value.commandLine !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
let result: TaskSystem.CommandConfiguration = {
|
||||
name: value.commandLine,
|
||||
runtime: TaskSystem.RuntimeType.Shell,
|
||||
presentation: undefined
|
||||
};
|
||||
if (value.options) {
|
||||
result.options = CommandOptions.from(value.options);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
interface HandlerData {
|
||||
provider: vscode.TaskProvider;
|
||||
extension: IExtensionDescription;
|
||||
}
|
||||
|
||||
export class ExtHostTask implements ExtHostTaskShape {
|
||||
|
||||
private _proxy: MainThreadTaskShape;
|
||||
private _handleCounter: number;
|
||||
private _handlers: Map<number, HandlerData>;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadTask);
|
||||
this._handleCounter = 0;
|
||||
this._handlers = new Map<number, HandlerData>();
|
||||
};
|
||||
|
||||
public registerTaskProvider(extension: IExtensionDescription, provider: vscode.TaskProvider): vscode.Disposable {
|
||||
if (!provider) {
|
||||
return new types.Disposable(() => { });
|
||||
}
|
||||
let handle = this.nextHandle();
|
||||
this._handlers.set(handle, { provider, extension });
|
||||
this._proxy.$registerTaskProvider(handle);
|
||||
return new types.Disposable(() => {
|
||||
this._handlers.delete(handle);
|
||||
this._proxy.$unregisterTaskProvider(handle);
|
||||
});
|
||||
}
|
||||
|
||||
public $provideTasks(handle: number): TPromise<TaskSystem.TaskSet> {
|
||||
let handler = this._handlers.get(handle);
|
||||
if (!handler) {
|
||||
return TPromise.wrapError<TaskSystem.TaskSet>(new Error('no handler found'));
|
||||
}
|
||||
return asWinJsPromise(token => handler.provider.provideTasks(token)).then(value => {
|
||||
return {
|
||||
tasks: Tasks.from(value, handler.extension),
|
||||
extension: handler.extension
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private nextHandle(): number {
|
||||
return this._handleCounter++;
|
||||
}
|
||||
}
|
||||
172
src/vs/workbench/api/node/extHostTerminalService.ts
Normal file
172
src/vs/workbench/api/node/extHostTerminalService.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import vscode = require('vscode');
|
||||
import { TPromise, TValueCallback } from 'vs/base/common/winjs.base';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext } from './extHost.protocol';
|
||||
|
||||
export class ExtHostTerminal implements vscode.Terminal {
|
||||
|
||||
private _name: string;
|
||||
private _id: number;
|
||||
private _proxy: MainThreadTerminalServiceShape;
|
||||
private _disposed: boolean;
|
||||
private _queuedRequests: ApiRequest[];
|
||||
private _pidPromise: TPromise<number>;
|
||||
private _pidPromiseComplete: TValueCallback<number>;
|
||||
|
||||
constructor(
|
||||
proxy: MainThreadTerminalServiceShape,
|
||||
name?: string,
|
||||
shellPath?: string,
|
||||
shellArgs?: string[],
|
||||
waitOnExit?: boolean
|
||||
) {
|
||||
this._name = name;
|
||||
this._queuedRequests = [];
|
||||
this._proxy = proxy;
|
||||
this._pidPromise = new TPromise<number>(c => {
|
||||
this._pidPromiseComplete = c;
|
||||
});
|
||||
this._proxy.$createTerminal(name, shellPath, shellArgs, waitOnExit).then((id) => {
|
||||
this._id = id;
|
||||
this._queuedRequests.forEach((r) => {
|
||||
r.run(this._proxy, this._id);
|
||||
});
|
||||
this._queuedRequests = [];
|
||||
});
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
this._checkDisposed();
|
||||
return this._name;
|
||||
}
|
||||
|
||||
public get processId(): Thenable<number> {
|
||||
this._checkDisposed();
|
||||
return this._pidPromise;
|
||||
}
|
||||
|
||||
public sendText(text: string, addNewLine: boolean = true): void {
|
||||
this._checkDisposed();
|
||||
this._queueApiRequest(this._proxy.$sendText, [text, addNewLine]);
|
||||
}
|
||||
|
||||
public show(preserveFocus: boolean): void {
|
||||
this._checkDisposed();
|
||||
this._queueApiRequest(this._proxy.$show, [preserveFocus]);
|
||||
}
|
||||
|
||||
public hide(): void {
|
||||
this._checkDisposed();
|
||||
this._queueApiRequest(this._proxy.$hide, []);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (!this._disposed) {
|
||||
this._disposed = true;
|
||||
this._queueApiRequest(this._proxy.$dispose, []);
|
||||
}
|
||||
}
|
||||
|
||||
public _setProcessId(processId: number): void {
|
||||
this._pidPromiseComplete(processId);
|
||||
this._pidPromiseComplete = null;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
private _checkDisposed() {
|
||||
if (this._disposed) {
|
||||
throw new Error('Terminal has already been disposed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
||||
|
||||
private _onDidCloseTerminal: Emitter<vscode.Terminal>;
|
||||
private _proxy: MainThreadTerminalServiceShape;
|
||||
private _terminals: ExtHostTerminal[];
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._onDidCloseTerminal = new Emitter<vscode.Terminal>();
|
||||
this._proxy = mainContext.get(MainContext.MainThreadTerminalService);
|
||||
this._terminals = [];
|
||||
}
|
||||
|
||||
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
|
||||
let terminal = new ExtHostTerminal(this._proxy, name, shellPath, shellArgs);
|
||||
this._terminals.push(terminal);
|
||||
return terminal;
|
||||
}
|
||||
|
||||
public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal {
|
||||
let terminal = new ExtHostTerminal(this._proxy, options.name, options.shellPath, options.shellArgs/*, options.waitOnExit*/);
|
||||
this._terminals.push(terminal);
|
||||
return terminal;
|
||||
}
|
||||
|
||||
public get onDidCloseTerminal(): Event<vscode.Terminal> {
|
||||
return this._onDidCloseTerminal && this._onDidCloseTerminal.event;
|
||||
}
|
||||
|
||||
public $acceptTerminalClosed(id: number): void {
|
||||
let index = this._getTerminalIndexById(id);
|
||||
if (index === null) {
|
||||
// The terminal was not created by the terminal API, ignore it
|
||||
return;
|
||||
}
|
||||
let terminal = this._terminals.splice(index, 1)[0];
|
||||
this._onDidCloseTerminal.fire(terminal);
|
||||
}
|
||||
|
||||
public $acceptTerminalProcessId(id: number, processId: number): void {
|
||||
let terminal = this._getTerminalById(id);
|
||||
terminal._setProcessId(processId);
|
||||
}
|
||||
|
||||
private _getTerminalById(id: number): ExtHostTerminal {
|
||||
let index = this._getTerminalIndexById(id);
|
||||
return index !== null ? this._terminals[index] : null;
|
||||
}
|
||||
|
||||
private _getTerminalIndexById(id: number): number {
|
||||
let index: number = null;
|
||||
this._terminals.some((terminal, i) => {
|
||||
let thisId = (<any>terminal)._id;
|
||||
if (thisId === id) {
|
||||
index = i;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
class ApiRequest {
|
||||
|
||||
private _callback: (...args: any[]) => void;
|
||||
private _args: any[];
|
||||
|
||||
constructor(callback: (...args: any[]) => void, args: any[]) {
|
||||
this._callback = callback;
|
||||
this._args = args;
|
||||
}
|
||||
|
||||
public run(proxy: MainThreadTerminalServiceShape, id: number) {
|
||||
this._callback.apply(proxy, [id].concat(this._args));
|
||||
}
|
||||
}
|
||||
623
src/vs/workbench/api/node/extHostTextEditor.ts
Normal file
623
src/vs/workbench/api/node/extHostTextEditor.ts
Normal file
@@ -0,0 +1,623 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
|
||||
import { ok } from 'vs/base/common/assert';
|
||||
import { readonly, illegalArgument, V8CallSite } from 'vs/base/common/errors';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData';
|
||||
import { Selection, Range, Position, EndOfLine, TextEditorRevealType, TextEditorLineNumbersStyle, SnippetString } from './extHostTypes';
|
||||
import { ISingleEditOperation } from 'vs/editor/common/editorCommon';
|
||||
import * as TypeConverters from './extHostTypeConverters';
|
||||
import { MainThreadEditorsShape, MainThreadTelemetryShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from './extHost.protocol';
|
||||
import * as vscode from 'vscode';
|
||||
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { containsCommandLink } from 'vs/base/common/htmlContent';
|
||||
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
|
||||
|
||||
export class TextEditorDecorationType implements vscode.TextEditorDecorationType {
|
||||
|
||||
private static _Keys = new IdGenerator('TextEditorDecorationType');
|
||||
|
||||
private _proxy: MainThreadEditorsShape;
|
||||
public key: string;
|
||||
|
||||
constructor(proxy: MainThreadEditorsShape, options: vscode.DecorationRenderOptions) {
|
||||
this.key = TextEditorDecorationType._Keys.nextId();
|
||||
this._proxy = proxy;
|
||||
this._proxy.$registerTextEditorDecorationType(this.key, <any>/* URI vs Uri */ options);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._proxy.$removeTextEditorDecorationType(this.key);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITextEditOperation {
|
||||
range: vscode.Range;
|
||||
text: string;
|
||||
forceMoveMarkers: boolean;
|
||||
}
|
||||
|
||||
export interface IEditData {
|
||||
documentVersionId: number;
|
||||
edits: ITextEditOperation[];
|
||||
setEndOfLine: EndOfLine;
|
||||
undoStopBefore: boolean;
|
||||
undoStopAfter: boolean;
|
||||
}
|
||||
|
||||
export class TextEditorEdit {
|
||||
|
||||
private readonly _document: vscode.TextDocument;
|
||||
private readonly _documentVersionId: number;
|
||||
private _collectedEdits: ITextEditOperation[];
|
||||
private _setEndOfLine: EndOfLine;
|
||||
private readonly _undoStopBefore: boolean;
|
||||
private readonly _undoStopAfter: boolean;
|
||||
|
||||
constructor(document: vscode.TextDocument, options: { undoStopBefore: boolean; undoStopAfter: boolean; }) {
|
||||
this._document = document;
|
||||
this._documentVersionId = document.version;
|
||||
this._collectedEdits = [];
|
||||
this._setEndOfLine = 0;
|
||||
this._undoStopBefore = options.undoStopBefore;
|
||||
this._undoStopAfter = options.undoStopAfter;
|
||||
}
|
||||
|
||||
finalize(): IEditData {
|
||||
return {
|
||||
documentVersionId: this._documentVersionId,
|
||||
edits: this._collectedEdits,
|
||||
setEndOfLine: this._setEndOfLine,
|
||||
undoStopBefore: this._undoStopBefore,
|
||||
undoStopAfter: this._undoStopAfter
|
||||
};
|
||||
}
|
||||
|
||||
replace(location: Position | Range | Selection, value: string): void {
|
||||
let range: Range = null;
|
||||
|
||||
if (location instanceof Position) {
|
||||
range = new Range(location, location);
|
||||
} else if (location instanceof Range) {
|
||||
range = location;
|
||||
} else {
|
||||
throw new Error('Unrecognized location');
|
||||
}
|
||||
|
||||
this._pushEdit(range, value, false);
|
||||
}
|
||||
|
||||
insert(location: Position, value: string): void {
|
||||
this._pushEdit(new Range(location, location), value, true);
|
||||
}
|
||||
|
||||
delete(location: Range | Selection): void {
|
||||
let range: Range = null;
|
||||
|
||||
if (location instanceof Range) {
|
||||
range = location;
|
||||
} else {
|
||||
throw new Error('Unrecognized location');
|
||||
}
|
||||
|
||||
this._pushEdit(range, null, true);
|
||||
}
|
||||
|
||||
private _pushEdit(range: Range, text: string, forceMoveMarkers: boolean): void {
|
||||
let validRange = this._document.validateRange(range);
|
||||
this._collectedEdits.push({
|
||||
range: validRange,
|
||||
text: text,
|
||||
forceMoveMarkers: forceMoveMarkers
|
||||
});
|
||||
}
|
||||
|
||||
setEndOfLine(endOfLine: EndOfLine): void {
|
||||
if (endOfLine !== EndOfLine.LF && endOfLine !== EndOfLine.CRLF) {
|
||||
throw illegalArgument('endOfLine');
|
||||
}
|
||||
|
||||
this._setEndOfLine = endOfLine;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function deprecated(name: string, message: string = 'Refer to the documentation for further details.') {
|
||||
return (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) => {
|
||||
const originalMethod = descriptor.value;
|
||||
descriptor.value = function (...args: any[]) {
|
||||
console.warn(`[Deprecation Warning] method '${name}' is deprecated and should no longer be used. ${message}`);
|
||||
return originalMethod.apply(this, args);
|
||||
};
|
||||
|
||||
return descriptor;
|
||||
};
|
||||
}
|
||||
|
||||
export class ExtHostTextEditorOptions implements vscode.TextEditorOptions {
|
||||
|
||||
private _proxy: MainThreadEditorsShape;
|
||||
private _id: string;
|
||||
|
||||
private _tabSize: number;
|
||||
private _insertSpaces: boolean;
|
||||
private _cursorStyle: TextEditorCursorStyle;
|
||||
private _lineNumbers: TextEditorLineNumbersStyle;
|
||||
|
||||
constructor(proxy: MainThreadEditorsShape, id: string, source: IResolvedTextEditorConfiguration) {
|
||||
this._proxy = proxy;
|
||||
this._id = id;
|
||||
this._accept(source);
|
||||
}
|
||||
|
||||
public _accept(source: IResolvedTextEditorConfiguration): void {
|
||||
this._tabSize = source.tabSize;
|
||||
this._insertSpaces = source.insertSpaces;
|
||||
this._cursorStyle = source.cursorStyle;
|
||||
this._lineNumbers = source.lineNumbers;
|
||||
}
|
||||
|
||||
public get tabSize(): number | string {
|
||||
return this._tabSize;
|
||||
}
|
||||
|
||||
private _validateTabSize(value: number | string): number | 'auto' | null {
|
||||
if (value === 'auto') {
|
||||
return 'auto';
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
let r = Math.floor(value);
|
||||
return (r > 0 ? r : null);
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
let r = parseInt(value, 10);
|
||||
if (isNaN(r)) {
|
||||
return null;
|
||||
}
|
||||
return (r > 0 ? r : null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public set tabSize(value: number | string) {
|
||||
let tabSize = this._validateTabSize(value);
|
||||
if (tabSize === null) {
|
||||
// ignore invalid call
|
||||
return;
|
||||
}
|
||||
if (typeof tabSize === 'number') {
|
||||
if (this._tabSize === tabSize) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
// reflect the new tabSize value immediately
|
||||
this._tabSize = tabSize;
|
||||
}
|
||||
warnOnError(this._proxy.$trySetOptions(this._id, {
|
||||
tabSize: tabSize
|
||||
}));
|
||||
}
|
||||
|
||||
public get insertSpaces(): boolean | string {
|
||||
return this._insertSpaces;
|
||||
}
|
||||
|
||||
private _validateInsertSpaces(value: boolean | string): boolean | 'auto' {
|
||||
if (value === 'auto') {
|
||||
return 'auto';
|
||||
}
|
||||
return (value === 'false' ? false : Boolean(value));
|
||||
}
|
||||
|
||||
public set insertSpaces(value: boolean | string) {
|
||||
let insertSpaces = this._validateInsertSpaces(value);
|
||||
if (typeof insertSpaces === 'boolean') {
|
||||
if (this._insertSpaces === insertSpaces) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
// reflect the new insertSpaces value immediately
|
||||
this._insertSpaces = insertSpaces;
|
||||
}
|
||||
warnOnError(this._proxy.$trySetOptions(this._id, {
|
||||
insertSpaces: insertSpaces
|
||||
}));
|
||||
}
|
||||
|
||||
public get cursorStyle(): TextEditorCursorStyle {
|
||||
return this._cursorStyle;
|
||||
}
|
||||
|
||||
public set cursorStyle(value: TextEditorCursorStyle) {
|
||||
if (this._cursorStyle === value) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
this._cursorStyle = value;
|
||||
warnOnError(this._proxy.$trySetOptions(this._id, {
|
||||
cursorStyle: value
|
||||
}));
|
||||
}
|
||||
|
||||
public get lineNumbers(): TextEditorLineNumbersStyle {
|
||||
return this._lineNumbers;
|
||||
}
|
||||
|
||||
public set lineNumbers(value: TextEditorLineNumbersStyle) {
|
||||
if (this._lineNumbers === value) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
this._lineNumbers = value;
|
||||
warnOnError(this._proxy.$trySetOptions(this._id, {
|
||||
lineNumbers: value
|
||||
}));
|
||||
}
|
||||
|
||||
public assign(newOptions: vscode.TextEditorOptions) {
|
||||
let bulkConfigurationUpdate: ITextEditorConfigurationUpdate = {};
|
||||
let hasUpdate = false;
|
||||
|
||||
if (typeof newOptions.tabSize !== 'undefined') {
|
||||
let tabSize = this._validateTabSize(newOptions.tabSize);
|
||||
if (tabSize === 'auto') {
|
||||
hasUpdate = true;
|
||||
bulkConfigurationUpdate.tabSize = tabSize;
|
||||
} else if (typeof tabSize === 'number' && this._tabSize !== tabSize) {
|
||||
// reflect the new tabSize value immediately
|
||||
this._tabSize = tabSize;
|
||||
hasUpdate = true;
|
||||
bulkConfigurationUpdate.tabSize = tabSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof newOptions.insertSpaces !== 'undefined') {
|
||||
let insertSpaces = this._validateInsertSpaces(newOptions.insertSpaces);
|
||||
if (insertSpaces === 'auto') {
|
||||
hasUpdate = true;
|
||||
bulkConfigurationUpdate.insertSpaces = insertSpaces;
|
||||
} else if (this._insertSpaces !== insertSpaces) {
|
||||
// reflect the new insertSpaces value immediately
|
||||
this._insertSpaces = insertSpaces;
|
||||
hasUpdate = true;
|
||||
bulkConfigurationUpdate.insertSpaces = insertSpaces;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof newOptions.cursorStyle !== 'undefined') {
|
||||
if (this._cursorStyle !== newOptions.cursorStyle) {
|
||||
this._cursorStyle = newOptions.cursorStyle;
|
||||
hasUpdate = true;
|
||||
bulkConfigurationUpdate.cursorStyle = newOptions.cursorStyle;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof newOptions.lineNumbers !== 'undefined') {
|
||||
if (this._lineNumbers !== newOptions.lineNumbers) {
|
||||
this._lineNumbers = newOptions.lineNumbers;
|
||||
hasUpdate = true;
|
||||
bulkConfigurationUpdate.lineNumbers = newOptions.lineNumbers;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasUpdate) {
|
||||
warnOnError(this._proxy.$trySetOptions(this._id, bulkConfigurationUpdate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostTextEditor implements vscode.TextEditor {
|
||||
|
||||
private readonly _proxy: MainThreadEditorsShape;
|
||||
private readonly _id: string;
|
||||
private readonly _documentData: ExtHostDocumentData;
|
||||
|
||||
private _selections: Selection[];
|
||||
private _options: ExtHostTextEditorOptions;
|
||||
private _viewColumn: vscode.ViewColumn;
|
||||
private _disposed: boolean = false;
|
||||
|
||||
get id(): string { return this._id; }
|
||||
|
||||
constructor(proxy: MainThreadEditorsShape, id: string, document: ExtHostDocumentData, selections: Selection[], options: IResolvedTextEditorConfiguration, viewColumn: vscode.ViewColumn) {
|
||||
this._proxy = proxy;
|
||||
this._id = id;
|
||||
this._documentData = document;
|
||||
this._selections = selections;
|
||||
this._options = new ExtHostTextEditorOptions(this._proxy, this._id, options);
|
||||
this._viewColumn = viewColumn;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
ok(!this._disposed);
|
||||
this._disposed = true;
|
||||
}
|
||||
|
||||
@deprecated('TextEditor.show') show(column: vscode.ViewColumn) {
|
||||
this._proxy.$tryShowEditor(this._id, TypeConverters.fromViewColumn(column));
|
||||
}
|
||||
|
||||
@deprecated('TextEditor.hide') hide() {
|
||||
this._proxy.$tryHideEditor(this._id);
|
||||
}
|
||||
|
||||
// ---- the document
|
||||
|
||||
get document(): vscode.TextDocument {
|
||||
return this._documentData.document;
|
||||
}
|
||||
|
||||
set document(value) {
|
||||
throw readonly('document');
|
||||
}
|
||||
|
||||
// ---- options
|
||||
|
||||
get options(): vscode.TextEditorOptions {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
set options(value: vscode.TextEditorOptions) {
|
||||
if (!this._disposed) {
|
||||
this._options.assign(value);
|
||||
}
|
||||
}
|
||||
|
||||
_acceptOptions(options: IResolvedTextEditorConfiguration): void {
|
||||
ok(!this._disposed);
|
||||
this._options._accept(options);
|
||||
}
|
||||
|
||||
// ---- view column
|
||||
|
||||
get viewColumn(): vscode.ViewColumn {
|
||||
return this._viewColumn;
|
||||
}
|
||||
|
||||
set viewColumn(value) {
|
||||
throw readonly('viewColumn');
|
||||
}
|
||||
|
||||
_acceptViewColumn(value: vscode.ViewColumn) {
|
||||
ok(!this._disposed);
|
||||
this._viewColumn = value;
|
||||
}
|
||||
|
||||
// ---- selections
|
||||
|
||||
get selection(): Selection {
|
||||
return this._selections && this._selections[0];
|
||||
}
|
||||
|
||||
set selection(value: Selection) {
|
||||
if (!(value instanceof Selection)) {
|
||||
throw illegalArgument('selection');
|
||||
}
|
||||
this._selections = [value];
|
||||
this._trySetSelection();
|
||||
}
|
||||
|
||||
get selections(): Selection[] {
|
||||
return this._selections;
|
||||
}
|
||||
|
||||
set selections(value: Selection[]) {
|
||||
if (!Array.isArray(value) || value.some(a => !(a instanceof Selection))) {
|
||||
throw illegalArgument('selections');
|
||||
}
|
||||
this._selections = value;
|
||||
this._trySetSelection();
|
||||
}
|
||||
|
||||
setDecorations(decorationType: vscode.TextEditorDecorationType, ranges: Range[] | vscode.DecorationOptions[]): void {
|
||||
this._runOnProxy(
|
||||
() => this._proxy.$trySetDecorations(
|
||||
this._id,
|
||||
decorationType.key,
|
||||
TypeConverters.fromRangeOrRangeWithMessage(ranges)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
revealRange(range: Range, revealType: vscode.TextEditorRevealType): void {
|
||||
this._runOnProxy(
|
||||
() => this._proxy.$tryRevealRange(
|
||||
this._id,
|
||||
TypeConverters.fromRange(range),
|
||||
(revealType || TextEditorRevealType.Default)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _trySetSelection(): TPromise<vscode.TextEditor> {
|
||||
let selection = this._selections.map(TypeConverters.fromSelection);
|
||||
return this._runOnProxy(() => this._proxy.$trySetSelections(this._id, selection));
|
||||
}
|
||||
|
||||
_acceptSelections(selections: Selection[]): void {
|
||||
ok(!this._disposed);
|
||||
this._selections = selections;
|
||||
}
|
||||
|
||||
// ---- editing
|
||||
|
||||
edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Thenable<boolean> {
|
||||
if (this._disposed) {
|
||||
return TPromise.wrapError<boolean>(new Error('TextEditor#edit not possible on closed editors'));
|
||||
}
|
||||
let edit = new TextEditorEdit(this._documentData.document, options);
|
||||
callback(edit);
|
||||
return this._applyEdit(edit);
|
||||
}
|
||||
|
||||
private _applyEdit(editBuilder: TextEditorEdit): TPromise<boolean> {
|
||||
let editData = editBuilder.finalize();
|
||||
|
||||
// check that the edits are not overlapping (i.e. illegal)
|
||||
let editRanges = editData.edits.map(edit => edit.range);
|
||||
|
||||
// sort ascending (by end and then by start)
|
||||
editRanges.sort((a, b) => {
|
||||
if (a.end.line === b.end.line) {
|
||||
if (a.end.character === b.end.character) {
|
||||
if (a.start.line === b.start.line) {
|
||||
return a.start.character - b.start.character;
|
||||
}
|
||||
return a.start.line - b.start.line;
|
||||
}
|
||||
return a.end.character - b.end.character;
|
||||
}
|
||||
return a.end.line - b.end.line;
|
||||
});
|
||||
|
||||
// check that no edits are overlapping
|
||||
for (let i = 0, count = editRanges.length - 1; i < count; i++) {
|
||||
const rangeEnd = editRanges[i].end;
|
||||
const nextRangeStart = editRanges[i + 1].start;
|
||||
|
||||
if (nextRangeStart.isBefore(rangeEnd)) {
|
||||
// overlapping ranges
|
||||
return TPromise.wrapError<boolean>(
|
||||
new Error('Overlapping ranges are not allowed!')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare data for serialization
|
||||
let edits: ISingleEditOperation[] = editData.edits.map((edit) => {
|
||||
return {
|
||||
range: TypeConverters.fromRange(edit.range),
|
||||
text: edit.text,
|
||||
forceMoveMarkers: edit.forceMoveMarkers
|
||||
};
|
||||
});
|
||||
|
||||
return this._proxy.$tryApplyEdits(this._id, editData.documentVersionId, edits, {
|
||||
setEndOfLine: editData.setEndOfLine,
|
||||
undoStopBefore: editData.undoStopBefore,
|
||||
undoStopAfter: editData.undoStopAfter
|
||||
});
|
||||
}
|
||||
|
||||
insertSnippet(snippet: SnippetString, where?: Position | Position[] | Range | Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Thenable<boolean> {
|
||||
if (this._disposed) {
|
||||
return TPromise.wrapError<boolean>(new Error('TextEditor#insertSnippet not possible on closed editors'));
|
||||
}
|
||||
let ranges: IRange[];
|
||||
|
||||
if (!where || (Array.isArray(where) && where.length === 0)) {
|
||||
ranges = this._selections.map(TypeConverters.fromRange);
|
||||
|
||||
} else if (where instanceof Position) {
|
||||
const { lineNumber, column } = TypeConverters.fromPosition(where);
|
||||
ranges = [{ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column }];
|
||||
|
||||
} else if (where instanceof Range) {
|
||||
ranges = [TypeConverters.fromRange(where)];
|
||||
} else {
|
||||
ranges = [];
|
||||
for (const posOrRange of where) {
|
||||
if (posOrRange instanceof Range) {
|
||||
ranges.push(TypeConverters.fromRange(posOrRange));
|
||||
} else {
|
||||
const { lineNumber, column } = TypeConverters.fromPosition(posOrRange);
|
||||
ranges.push({ startLineNumber: lineNumber, startColumn: column, endLineNumber: lineNumber, endColumn: column });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this._proxy.$tryInsertSnippet(this._id, snippet.value, ranges, options);
|
||||
}
|
||||
|
||||
// ---- util
|
||||
|
||||
private _runOnProxy(callback: () => TPromise<any>): TPromise<ExtHostTextEditor> {
|
||||
if (this._disposed) {
|
||||
console.warn('TextEditor is closed/disposed');
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
return callback().then(() => this, err => {
|
||||
if (!(err instanceof Error && err.name === 'DISPOSED')) {
|
||||
console.warn(err);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostTextEditor2 extends ExtHostTextEditor {
|
||||
|
||||
constructor(
|
||||
private readonly _extHostExtensions: ExtHostExtensionService,
|
||||
private readonly _mainThreadTelemetry: MainThreadTelemetryShape,
|
||||
proxy: MainThreadEditorsShape,
|
||||
id: string,
|
||||
document: ExtHostDocumentData,
|
||||
selections: Selection[],
|
||||
options: IResolvedTextEditorConfiguration,
|
||||
viewColumn: vscode.ViewColumn
|
||||
) {
|
||||
super(proxy, id, document, selections, options, viewColumn);
|
||||
}
|
||||
|
||||
setDecorations(decorationType: vscode.TextEditorDecorationType, rangesOrOptions: Range[] | vscode.DecorationOptions[]): void {
|
||||
// (1) find out if this decoration is important for us
|
||||
let usesCommandLink = false;
|
||||
outer: for (const rangeOrOption of rangesOrOptions) {
|
||||
if (Range.isRange(rangeOrOption)) {
|
||||
break;
|
||||
}
|
||||
if (typeof rangeOrOption.hoverMessage === 'string' && containsCommandLink(rangeOrOption.hoverMessage)) {
|
||||
usesCommandLink = true;
|
||||
break;
|
||||
} else if (Array.isArray(rangeOrOption.hoverMessage)) {
|
||||
for (const message of rangeOrOption.hoverMessage) {
|
||||
if (typeof message === 'string' && containsCommandLink(message)) {
|
||||
usesCommandLink = true;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// (2) send event for important decorations
|
||||
if (usesCommandLink) {
|
||||
let tag = new Error();
|
||||
this._extHostExtensions.getExtensionPathIndex().then(index => {
|
||||
const oldHandler = (<any>Error).prepareStackTrace;
|
||||
(<any>Error).prepareStackTrace = (error: Error, stackTrace: V8CallSite[]) => {
|
||||
for (const call of stackTrace) {
|
||||
const extension = index.findSubstr(call.getFileName());
|
||||
if (extension) {
|
||||
this._mainThreadTelemetry.$publicLog('usesCommandLink', {
|
||||
extension: extension.id,
|
||||
from: 'decoration',
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
// it all happens here...
|
||||
// tslint:disable-next-line:no-unused-expression
|
||||
tag.stack;
|
||||
(<any>Error).prepareStackTrace = oldHandler;
|
||||
});
|
||||
}
|
||||
|
||||
// (3) do it
|
||||
super.setDecorations(decorationType, rangesOrOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function warnOnError(promise: TPromise<any>): void {
|
||||
promise.then(null, (err) => {
|
||||
console.warn(err);
|
||||
});
|
||||
}
|
||||
131
src/vs/workbench/api/node/extHostTextEditors.ts
Normal file
131
src/vs/workbench/api/node/extHostTextEditors.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import 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 { Position as EditorPosition } from 'vs/platform/editor/common/editor';
|
||||
import { MainContext, MainThreadEditorsShape, ExtHostEditorsShape, ITextDocumentShowOptions, ITextEditorPositionData, IResolvedTextEditorConfiguration, ISelectionChangeEvent, IMainContext } from './extHost.protocol';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export class ExtHostEditors implements ExtHostEditorsShape {
|
||||
|
||||
private readonly _onDidChangeTextEditorSelection = new Emitter<vscode.TextEditorSelectionChangeEvent>();
|
||||
private readonly _onDidChangeTextEditorOptions = new Emitter<vscode.TextEditorOptionsChangeEvent>();
|
||||
private readonly _onDidChangeTextEditorViewColumn = new Emitter<vscode.TextEditorViewColumnChangeEvent>();
|
||||
private readonly _onDidChangeActiveTextEditor = new Emitter<vscode.TextEditor>();
|
||||
private readonly _onDidChangeVisibleTextEditors = new Emitter<vscode.TextEditor[]>();
|
||||
|
||||
readonly onDidChangeTextEditorSelection: Event<vscode.TextEditorSelectionChangeEvent> = this._onDidChangeTextEditorSelection.event;
|
||||
readonly onDidChangeTextEditorOptions: Event<vscode.TextEditorOptionsChangeEvent> = this._onDidChangeTextEditorOptions.event;
|
||||
readonly onDidChangeTextEditorViewColumn: Event<vscode.TextEditorViewColumnChangeEvent> = this._onDidChangeTextEditorViewColumn.event;
|
||||
readonly onDidChangeActiveTextEditor: Event<vscode.TextEditor> = this._onDidChangeActiveTextEditor.event;
|
||||
readonly onDidChangeVisibleTextEditors: Event<vscode.TextEditor[]> = this._onDidChangeVisibleTextEditors.event;
|
||||
|
||||
|
||||
private _proxy: MainThreadEditorsShape;
|
||||
private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors;
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadEditors);
|
||||
this._extHostDocumentsAndEditors = extHostDocumentsAndEditors;
|
||||
|
||||
this._extHostDocumentsAndEditors.onDidChangeVisibleTextEditors(e => this._onDidChangeVisibleTextEditors.fire(e));
|
||||
this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(e => this._onDidChangeActiveTextEditor.fire(e));
|
||||
}
|
||||
|
||||
getActiveTextEditor(): ExtHostTextEditor {
|
||||
return this._extHostDocumentsAndEditors.activeEditor();
|
||||
}
|
||||
|
||||
getVisibleTextEditors(): vscode.TextEditor[] {
|
||||
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> {
|
||||
let options: ITextDocumentShowOptions;
|
||||
if (typeof columnOrOptions === 'number') {
|
||||
options = {
|
||||
position: TypeConverters.fromViewColumn(columnOrOptions),
|
||||
preserveFocus
|
||||
};
|
||||
} else if (typeof columnOrOptions === 'object') {
|
||||
options = {
|
||||
position: TypeConverters.fromViewColumn(columnOrOptions.viewColumn),
|
||||
preserveFocus: columnOrOptions.preserveFocus,
|
||||
selection: typeof columnOrOptions.selection === 'object' ? TypeConverters.fromRange(columnOrOptions.selection) : undefined,
|
||||
pinned: typeof columnOrOptions.preview === 'boolean' ? !columnOrOptions.preview : undefined
|
||||
};
|
||||
} else {
|
||||
options = {
|
||||
position: EditorPosition.ONE,
|
||||
preserveFocus: false
|
||||
};
|
||||
}
|
||||
|
||||
return this._proxy.$tryShowTextDocument(<URI>document.uri, options).then(id => {
|
||||
let editor = this._extHostDocumentsAndEditors.getEditor(id);
|
||||
if (editor) {
|
||||
return editor;
|
||||
} else {
|
||||
throw new Error(`Failed to show text document ${document.uri.toString()}, should show in editor #${id}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType {
|
||||
return new TextEditorDecorationType(this._proxy, options);
|
||||
}
|
||||
|
||||
// --- called from main thread
|
||||
|
||||
$acceptOptionsChanged(id: string, opts: IResolvedTextEditorConfiguration): void {
|
||||
let editor = this._extHostDocumentsAndEditors.getEditor(id);
|
||||
editor._acceptOptions(opts);
|
||||
this._onDidChangeTextEditorOptions.fire({
|
||||
textEditor: editor,
|
||||
options: opts
|
||||
});
|
||||
}
|
||||
|
||||
$acceptSelectionsChanged(id: string, event: ISelectionChangeEvent): void {
|
||||
const kind = TextEditorSelectionChangeKind.fromValue(event.source);
|
||||
const selections = event.selections.map(TypeConverters.toSelection);
|
||||
const textEditor = this._extHostDocumentsAndEditors.getEditor(id);
|
||||
textEditor._acceptSelections(selections);
|
||||
this._onDidChangeTextEditorSelection.fire({
|
||||
textEditor,
|
||||
selections,
|
||||
kind
|
||||
});
|
||||
}
|
||||
|
||||
$acceptEditorPositionData(data: ITextEditorPositionData): void {
|
||||
for (let id in data) {
|
||||
let textEditor = this._extHostDocumentsAndEditors.getEditor(id);
|
||||
let viewColumn = TypeConverters.toViewColumn(data[id]);
|
||||
if (textEditor.viewColumn !== viewColumn) {
|
||||
textEditor._acceptViewColumn(viewColumn);
|
||||
this._onDidChangeTextEditorViewColumn.fire({ textEditor, viewColumn });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getDiffInformation(id: string): Thenable<vscode.LineChange[]> {
|
||||
return toThenable(this._proxy.$getDiffInformation(id));
|
||||
}
|
||||
}
|
||||
227
src/vs/workbench/api/node/extHostTreeViews.ts
Normal file
227
src/vs/workbench/api/node/extHostTreeViews.ts
Normal file
@@ -0,0 +1,227 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import * as vscode from 'vscode';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { debounceEvent } 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/parts/views/common/views';
|
||||
import { TreeItemCollapsibleState } from './extHostTypes';
|
||||
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
|
||||
import { asWinJsPromise } from 'vs/base/common/async';
|
||||
|
||||
type TreeItemHandle = number;
|
||||
|
||||
export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||
|
||||
private treeViews: Map<string, ExtHostTreeView<any>> = new Map<string, ExtHostTreeView<any>>();
|
||||
|
||||
constructor(
|
||||
private _proxy: MainThreadTreeViewsShape,
|
||||
private commands: ExtHostCommands
|
||||
) {
|
||||
commands.registerArgumentProcessor({
|
||||
processArgument: arg => {
|
||||
if (arg && arg.$treeViewId && arg.$treeItemHandle) {
|
||||
return this.convertArgument(arg);
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
registerTreeDataProvider<T>(id: string, treeDataProvider: vscode.TreeDataProvider<T>): vscode.Disposable {
|
||||
const treeView = new ExtHostTreeView<T>(id, treeDataProvider, this._proxy, this.commands.converter);
|
||||
this.treeViews.set(id, treeView);
|
||||
return {
|
||||
dispose: () => {
|
||||
this.treeViews.delete(id);
|
||||
treeView.dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$getElements(treeViewId: string): TPromise<ITreeItem[]> {
|
||||
const treeView = this.treeViews.get(treeViewId);
|
||||
if (!treeView) {
|
||||
return TPromise.wrapError<ITreeItem[]>(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)));
|
||||
}
|
||||
return treeView.getTreeItems();
|
||||
}
|
||||
|
||||
$getChildren(treeViewId: string, treeItemHandle?: number): TPromise<ITreeItem[]> {
|
||||
const treeView = this.treeViews.get(treeViewId);
|
||||
if (!treeView) {
|
||||
return TPromise.wrapError<ITreeItem[]>(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)));
|
||||
}
|
||||
return treeView.getChildren(treeItemHandle);
|
||||
}
|
||||
|
||||
private convertArgument(arg: TreeViewItemHandleArg): any {
|
||||
const treeView = this.treeViews.get(arg.$treeViewId);
|
||||
return treeView ? treeView.getExtensionElement(arg.$treeItemHandle) : null;
|
||||
}
|
||||
}
|
||||
|
||||
class ExtHostTreeView<T> extends Disposable {
|
||||
|
||||
private _itemHandlePool = 0;
|
||||
|
||||
private extElementsMap: Map<TreeItemHandle, T> = new Map<TreeItemHandle, T>();
|
||||
private itemHandlesMap: Map<T, TreeItemHandle> = new Map<T, TreeItemHandle>();
|
||||
private extChildrenElementsMap: Map<T, T[]> = new Map<T, T[]>();
|
||||
|
||||
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter) {
|
||||
super();
|
||||
this.proxy.$registerView(viewId);
|
||||
if (dataProvider.onDidChangeTreeData) {
|
||||
this._register(debounceEvent<T, T[]>(dataProvider.onDidChangeTreeData, (last, current) => last ? [...last, current] : [current], 200)(elements => this._refresh(elements)));
|
||||
}
|
||||
}
|
||||
|
||||
getTreeItems(): TPromise<ITreeItem[]> {
|
||||
this.extChildrenElementsMap.clear();
|
||||
this.extElementsMap.clear();
|
||||
this.itemHandlesMap.clear();
|
||||
|
||||
return asWinJsPromise(() => this.dataProvider.getChildren())
|
||||
.then(elements => this.processAndMapElements(elements));
|
||||
}
|
||||
|
||||
getChildren(treeItemHandle: TreeItemHandle): TPromise<ITreeItem[]> {
|
||||
let extElement = this.getExtensionElement(treeItemHandle);
|
||||
if (extElement) {
|
||||
this.clearChildren(extElement);
|
||||
} else {
|
||||
return TPromise.wrapError<ITreeItem[]>(new Error(localize('treeItem.notFound', 'No tree item with id \'{0}\' found.', treeItemHandle)));
|
||||
}
|
||||
|
||||
return asWinJsPromise(() => this.dataProvider.getChildren(extElement))
|
||||
.then(childrenElements => this.processAndMapElements(childrenElements));
|
||||
}
|
||||
|
||||
getExtensionElement(treeItemHandle: TreeItemHandle): T {
|
||||
return this.extElementsMap.get(treeItemHandle);
|
||||
}
|
||||
|
||||
private _refresh(elements: T[]): void {
|
||||
const hasRoot = elements.some(element => !element);
|
||||
if (hasRoot) {
|
||||
this.proxy.$refresh(this.viewId, []);
|
||||
} else {
|
||||
const itemHandles = distinct(elements.map(element => this.itemHandlesMap.get(element))
|
||||
.filter(itemHandle => !!itemHandle));
|
||||
if (itemHandles.length) {
|
||||
this.proxy.$refresh(this.viewId, itemHandles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private processAndMapElements(elements: T[]): TPromise<ITreeItem[]> {
|
||||
if (elements && elements.length) {
|
||||
return TPromise.join(
|
||||
elements.filter(element => !!element)
|
||||
.map(element => {
|
||||
if (this.extChildrenElementsMap.has(element)) {
|
||||
return TPromise.wrapError<ITreeItem>(new Error(localize('treeView.duplicateElement', 'Element {0} is already registered', element)));
|
||||
}
|
||||
return this.resolveElement(element);
|
||||
}))
|
||||
.then(treeItems => treeItems.filter(treeItem => !!treeItem));
|
||||
}
|
||||
return TPromise.as([]);
|
||||
}
|
||||
|
||||
private resolveElement(element: T): TPromise<ITreeItem> {
|
||||
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
|
||||
.then(extTreeItem => {
|
||||
const treeItem = this.massageTreeItem(extTreeItem);
|
||||
if (treeItem) {
|
||||
this.itemHandlesMap.set(element, treeItem.handle);
|
||||
this.extElementsMap.set(treeItem.handle, element);
|
||||
if (treeItem.collapsibleState === TreeItemCollapsibleState.Expanded) {
|
||||
return this.getChildren(treeItem.handle).then(children => {
|
||||
treeItem.children = children;
|
||||
return treeItem;
|
||||
});
|
||||
} else {
|
||||
return treeItem;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private massageTreeItem(extensionTreeItem: vscode.TreeItem): ITreeItem {
|
||||
if (!extensionTreeItem) {
|
||||
return null;
|
||||
}
|
||||
const icon = this.getLightIconPath(extensionTreeItem);
|
||||
return {
|
||||
handle: ++this._itemHandlePool,
|
||||
label: extensionTreeItem.label,
|
||||
command: extensionTreeItem.command ? this.commands.toInternal(extensionTreeItem.command) : void 0,
|
||||
contextValue: extensionTreeItem.contextValue,
|
||||
icon,
|
||||
iconDark: this.getDarkIconPath(extensionTreeItem) || icon,
|
||||
collapsibleState: extensionTreeItem.collapsibleState,
|
||||
};
|
||||
}
|
||||
|
||||
private getLightIconPath(extensionTreeItem: vscode.TreeItem) {
|
||||
if (extensionTreeItem.iconPath) {
|
||||
if (typeof extensionTreeItem.iconPath === 'string' || extensionTreeItem.iconPath instanceof URI) {
|
||||
return this.getIconPath(extensionTreeItem.iconPath);
|
||||
}
|
||||
return this.getIconPath(extensionTreeItem.iconPath['light']);
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
||||
private getDarkIconPath(extensionTreeItem: vscode.TreeItem) {
|
||||
if (extensionTreeItem.iconPath && extensionTreeItem.iconPath['dark']) {
|
||||
return this.getIconPath(extensionTreeItem.iconPath['dark']);
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
|
||||
private getIconPath(iconPath: string | URI): string {
|
||||
if (iconPath instanceof URI) {
|
||||
return iconPath.toString();
|
||||
}
|
||||
return URI.file(iconPath).toString();
|
||||
}
|
||||
|
||||
private clearChildren(extElement: T): void {
|
||||
const children = this.extChildrenElementsMap.get(extElement);
|
||||
if (children) {
|
||||
for (const child of children) {
|
||||
this.clearElement(child);
|
||||
}
|
||||
this.extChildrenElementsMap.delete(extElement);
|
||||
}
|
||||
}
|
||||
|
||||
private clearElement(extElement: T): void {
|
||||
this.clearChildren(extElement);
|
||||
|
||||
const treeItemhandle = this.itemHandlesMap.get(extElement);
|
||||
this.itemHandlesMap.delete(extElement);
|
||||
if (treeItemhandle) {
|
||||
this.extElementsMap.delete(treeItemhandle);
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.extElementsMap.clear();
|
||||
this.itemHandlesMap.clear();
|
||||
this.extChildrenElementsMap.clear();
|
||||
}
|
||||
}
|
||||
454
src/vs/workbench/api/node/extHostTypeConverters.ts
Normal file
454
src/vs/workbench/api/node/extHostTypeConverters.ts
Normal file
@@ -0,0 +1,454 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import * as types from './extHostTypes';
|
||||
import { Position as EditorPosition } from 'vs/platform/editor/common/editor';
|
||||
import { IDecorationOptions, EndOfLineSequence } from 'vs/editor/common/editorCommon';
|
||||
import * as vscode from 'vscode';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
import * as htmlContent from 'vs/base/common/htmlContent';
|
||||
|
||||
export interface PositionLike {
|
||||
line: number;
|
||||
character: number;
|
||||
}
|
||||
|
||||
export interface RangeLike {
|
||||
start: PositionLike;
|
||||
end: PositionLike;
|
||||
}
|
||||
|
||||
export interface SelectionLike extends RangeLike {
|
||||
anchor: PositionLike;
|
||||
active: PositionLike;
|
||||
}
|
||||
|
||||
export function toSelection(selection: ISelection): types.Selection {
|
||||
let { selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn } = selection;
|
||||
let start = new types.Position(selectionStartLineNumber - 1, selectionStartColumn - 1);
|
||||
let end = new types.Position(positionLineNumber - 1, positionColumn - 1);
|
||||
return new types.Selection(start, end);
|
||||
}
|
||||
|
||||
export function fromSelection(selection: SelectionLike): ISelection {
|
||||
let { anchor, active } = selection;
|
||||
return {
|
||||
selectionStartLineNumber: anchor.line + 1,
|
||||
selectionStartColumn: anchor.character + 1,
|
||||
positionLineNumber: active.line + 1,
|
||||
positionColumn: active.character + 1
|
||||
};
|
||||
}
|
||||
|
||||
export function fromRange(range: RangeLike): IRange {
|
||||
if (!range) {
|
||||
return undefined;
|
||||
}
|
||||
let { start, end } = range;
|
||||
return {
|
||||
startLineNumber: start.line + 1,
|
||||
startColumn: start.character + 1,
|
||||
endLineNumber: end.line + 1,
|
||||
endColumn: end.character + 1
|
||||
};
|
||||
}
|
||||
|
||||
export function toRange(range: IRange): types.Range {
|
||||
if (!range) {
|
||||
return undefined;
|
||||
}
|
||||
let { startLineNumber, startColumn, endLineNumber, endColumn } = range;
|
||||
return new types.Range(startLineNumber - 1, startColumn - 1, endLineNumber - 1, endColumn - 1);
|
||||
}
|
||||
|
||||
export function toPosition(position: IPosition): types.Position {
|
||||
return new types.Position(position.lineNumber - 1, position.column - 1);
|
||||
}
|
||||
|
||||
export function fromPosition(position: types.Position): IPosition {
|
||||
return { lineNumber: position.line + 1, column: position.character + 1 };
|
||||
}
|
||||
|
||||
export function fromDiagnosticSeverity(value: number): Severity {
|
||||
switch (value) {
|
||||
case types.DiagnosticSeverity.Error:
|
||||
return Severity.Error;
|
||||
case types.DiagnosticSeverity.Warning:
|
||||
return Severity.Warning;
|
||||
case types.DiagnosticSeverity.Information:
|
||||
return Severity.Info;
|
||||
case types.DiagnosticSeverity.Hint:
|
||||
return Severity.Ignore;
|
||||
}
|
||||
return Severity.Error;
|
||||
}
|
||||
|
||||
export function toDiagnosticSeverty(value: Severity): types.DiagnosticSeverity {
|
||||
switch (value) {
|
||||
case Severity.Info:
|
||||
return types.DiagnosticSeverity.Information;
|
||||
case Severity.Warning:
|
||||
return types.DiagnosticSeverity.Warning;
|
||||
case Severity.Error:
|
||||
return types.DiagnosticSeverity.Error;
|
||||
case Severity.Ignore:
|
||||
return types.DiagnosticSeverity.Hint;
|
||||
}
|
||||
return types.DiagnosticSeverity.Error;
|
||||
}
|
||||
|
||||
export function fromViewColumn(column?: vscode.ViewColumn): EditorPosition {
|
||||
let editorColumn = EditorPosition.ONE;
|
||||
if (typeof column !== 'number') {
|
||||
// stick with ONE
|
||||
} else if (column === <number>types.ViewColumn.Two) {
|
||||
editorColumn = EditorPosition.TWO;
|
||||
} else if (column === <number>types.ViewColumn.Three) {
|
||||
editorColumn = EditorPosition.THREE;
|
||||
}
|
||||
return editorColumn;
|
||||
}
|
||||
|
||||
export function toViewColumn(position?: EditorPosition): vscode.ViewColumn {
|
||||
if (typeof position !== 'number') {
|
||||
return undefined;
|
||||
}
|
||||
if (position === EditorPosition.ONE) {
|
||||
return <number>types.ViewColumn.One;
|
||||
} else if (position === EditorPosition.TWO) {
|
||||
return <number>types.ViewColumn.Two;
|
||||
} else if (position === EditorPosition.THREE) {
|
||||
return <number>types.ViewColumn.Three;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isDecorationOptions(something: any): something is vscode.DecorationOptions {
|
||||
return (typeof something.range !== 'undefined');
|
||||
}
|
||||
|
||||
function isDecorationOptionsArr(something: vscode.Range[] | vscode.DecorationOptions[]): something is vscode.DecorationOptions[] {
|
||||
if (something.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return isDecorationOptions(something[0]) ? true : false;
|
||||
}
|
||||
|
||||
export namespace MarkdownString {
|
||||
|
||||
export function fromMany(markup: (vscode.MarkdownString | vscode.MarkedString)[]): htmlContent.IMarkdownString[] {
|
||||
return markup.map(MarkdownString.from);
|
||||
}
|
||||
|
||||
interface Codeblock {
|
||||
language: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
function isCodeblock(thing: any): thing is Codeblock {
|
||||
return typeof thing === 'object'
|
||||
&& typeof (<Codeblock>thing).language === 'string'
|
||||
&& typeof (<Codeblock>thing).value === 'string';
|
||||
}
|
||||
|
||||
export function from(markup: vscode.MarkdownString | vscode.MarkedString): htmlContent.IMarkdownString {
|
||||
if (isCodeblock(markup)) {
|
||||
const { language, value } = markup;
|
||||
return { value: '```' + language + '\n' + value + '\n```\n' };
|
||||
} else if (htmlContent.isMarkdownString(markup)) {
|
||||
return markup;
|
||||
} else if (typeof markup === 'string') {
|
||||
return { value: <string>markup, isTrusted: true };
|
||||
} else {
|
||||
return { value: '' };
|
||||
}
|
||||
}
|
||||
export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString {
|
||||
const ret = new htmlContent.MarkdownString(value.value);
|
||||
ret.isTrusted = value.isTrusted;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.DecorationOptions[]): IDecorationOptions[] {
|
||||
if (isDecorationOptionsArr(ranges)) {
|
||||
return ranges.map(r => {
|
||||
return {
|
||||
range: fromRange(r.range),
|
||||
hoverMessage: Array.isArray(r.hoverMessage) ? MarkdownString.fromMany(r.hoverMessage) : r.hoverMessage && MarkdownString.from(r.hoverMessage),
|
||||
renderOptions: <any> /* URI vs Uri */r.renderOptions
|
||||
};
|
||||
});
|
||||
} else {
|
||||
return ranges.map((r): IDecorationOptions => {
|
||||
return {
|
||||
range: fromRange(r)
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const TextEdit = {
|
||||
|
||||
from(edit: vscode.TextEdit): modes.TextEdit {
|
||||
return <modes.TextEdit>{
|
||||
text: edit.newText,
|
||||
eol: EndOfLine.from(edit.newEol),
|
||||
range: fromRange(edit.range)
|
||||
};
|
||||
},
|
||||
to(edit: modes.TextEdit): vscode.TextEdit {
|
||||
let result = new types.TextEdit(toRange(edit.range), edit.text);
|
||||
result.newEol = EndOfLine.to(edit.eol);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export namespace SymbolKind {
|
||||
|
||||
const _fromMapping: { [kind: number]: modes.SymbolKind } = Object.create(null);
|
||||
_fromMapping[types.SymbolKind.File] = modes.SymbolKind.File;
|
||||
_fromMapping[types.SymbolKind.Module] = modes.SymbolKind.Module;
|
||||
_fromMapping[types.SymbolKind.Namespace] = modes.SymbolKind.Namespace;
|
||||
_fromMapping[types.SymbolKind.Package] = modes.SymbolKind.Package;
|
||||
_fromMapping[types.SymbolKind.Class] = modes.SymbolKind.Class;
|
||||
_fromMapping[types.SymbolKind.Method] = modes.SymbolKind.Method;
|
||||
_fromMapping[types.SymbolKind.Property] = modes.SymbolKind.Property;
|
||||
_fromMapping[types.SymbolKind.Field] = modes.SymbolKind.Field;
|
||||
_fromMapping[types.SymbolKind.Constructor] = modes.SymbolKind.Constructor;
|
||||
_fromMapping[types.SymbolKind.Enum] = modes.SymbolKind.Enum;
|
||||
_fromMapping[types.SymbolKind.Interface] = modes.SymbolKind.Interface;
|
||||
_fromMapping[types.SymbolKind.Function] = modes.SymbolKind.Function;
|
||||
_fromMapping[types.SymbolKind.Variable] = modes.SymbolKind.Variable;
|
||||
_fromMapping[types.SymbolKind.Constant] = modes.SymbolKind.Constant;
|
||||
_fromMapping[types.SymbolKind.String] = modes.SymbolKind.String;
|
||||
_fromMapping[types.SymbolKind.Number] = modes.SymbolKind.Number;
|
||||
_fromMapping[types.SymbolKind.Boolean] = modes.SymbolKind.Boolean;
|
||||
_fromMapping[types.SymbolKind.Array] = modes.SymbolKind.Array;
|
||||
_fromMapping[types.SymbolKind.Object] = modes.SymbolKind.Object;
|
||||
_fromMapping[types.SymbolKind.Key] = modes.SymbolKind.Key;
|
||||
_fromMapping[types.SymbolKind.Null] = modes.SymbolKind.Null;
|
||||
_fromMapping[types.SymbolKind.EnumMember] = modes.SymbolKind.EnumMember;
|
||||
_fromMapping[types.SymbolKind.Struct] = modes.SymbolKind.Struct;
|
||||
_fromMapping[types.SymbolKind.Event] = modes.SymbolKind.Event;
|
||||
_fromMapping[types.SymbolKind.Operator] = modes.SymbolKind.Operator;
|
||||
_fromMapping[types.SymbolKind.TypeParameter] = modes.SymbolKind.TypeParameter;
|
||||
|
||||
export function from(kind: vscode.SymbolKind): modes.SymbolKind {
|
||||
return _fromMapping[kind] || modes.SymbolKind.Property;
|
||||
}
|
||||
|
||||
export function to(kind: modes.SymbolKind): vscode.SymbolKind {
|
||||
for (let k in _fromMapping) {
|
||||
if (_fromMapping[k] === kind) {
|
||||
return Number(k);
|
||||
}
|
||||
}
|
||||
return types.SymbolKind.Property;
|
||||
}
|
||||
}
|
||||
|
||||
export function fromSymbolInformation(info: vscode.SymbolInformation): modes.SymbolInformation {
|
||||
return <modes.SymbolInformation>{
|
||||
name: info.name,
|
||||
kind: SymbolKind.from(info.kind),
|
||||
containerName: info.containerName,
|
||||
location: location.from(info.location)
|
||||
};
|
||||
}
|
||||
|
||||
export function toSymbolInformation(bearing: modes.SymbolInformation): types.SymbolInformation {
|
||||
return new types.SymbolInformation(
|
||||
bearing.name,
|
||||
SymbolKind.to(bearing.kind),
|
||||
bearing.containerName,
|
||||
location.to(bearing.location)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export const location = {
|
||||
from(value: vscode.Location): modes.Location {
|
||||
return {
|
||||
range: value.range && fromRange(value.range),
|
||||
uri: <URI>value.uri
|
||||
};
|
||||
},
|
||||
to(value: modes.Location): types.Location {
|
||||
return new types.Location(value.uri, toRange(value.range));
|
||||
}
|
||||
};
|
||||
|
||||
export function fromHover(hover: vscode.Hover): modes.Hover {
|
||||
return <modes.Hover>{
|
||||
range: fromRange(hover.range),
|
||||
contents: MarkdownString.fromMany(hover.contents)
|
||||
};
|
||||
}
|
||||
|
||||
export function toHover(info: modes.Hover): types.Hover {
|
||||
return new types.Hover(info.contents.map(MarkdownString.to), toRange(info.range));
|
||||
}
|
||||
|
||||
export function toDocumentHighlight(occurrence: modes.DocumentHighlight): types.DocumentHighlight {
|
||||
return new types.DocumentHighlight(toRange(occurrence.range), occurrence.kind);
|
||||
}
|
||||
|
||||
export const CompletionItemKind = {
|
||||
|
||||
from(kind: types.CompletionItemKind): modes.SuggestionType {
|
||||
switch (kind) {
|
||||
case types.CompletionItemKind.Method: return 'method';
|
||||
case types.CompletionItemKind.Function: return 'function';
|
||||
case types.CompletionItemKind.Constructor: return 'constructor';
|
||||
case types.CompletionItemKind.Field: return 'field';
|
||||
case types.CompletionItemKind.Variable: return 'variable';
|
||||
case types.CompletionItemKind.Class: return 'class';
|
||||
case types.CompletionItemKind.Interface: return 'interface';
|
||||
case types.CompletionItemKind.Struct: return 'struct';
|
||||
case types.CompletionItemKind.Module: return 'module';
|
||||
case types.CompletionItemKind.Property: return 'property';
|
||||
case types.CompletionItemKind.Unit: return 'unit';
|
||||
case types.CompletionItemKind.Value: return 'value';
|
||||
case types.CompletionItemKind.Constant: return 'constant';
|
||||
case types.CompletionItemKind.Enum: return 'enum';
|
||||
case types.CompletionItemKind.EnumMember: return 'enum-member';
|
||||
case types.CompletionItemKind.Keyword: return 'keyword';
|
||||
case types.CompletionItemKind.Snippet: return 'snippet';
|
||||
case types.CompletionItemKind.Text: return 'text';
|
||||
case types.CompletionItemKind.Color: return 'color';
|
||||
case types.CompletionItemKind.File: return 'file';
|
||||
case types.CompletionItemKind.Reference: return 'reference';
|
||||
case types.CompletionItemKind.Folder: return 'folder';
|
||||
case types.CompletionItemKind.Event: return 'event';
|
||||
case types.CompletionItemKind.Operator: return 'operator';
|
||||
case types.CompletionItemKind.TypeParameter: return 'type-parameter';
|
||||
}
|
||||
return 'property';
|
||||
},
|
||||
|
||||
to(type: modes.SuggestionType): types.CompletionItemKind {
|
||||
if (!type) {
|
||||
return types.CompletionItemKind.Property;
|
||||
} else {
|
||||
return types.CompletionItemKind[type.charAt(0).toUpperCase() + type.substr(1)];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export namespace Suggest {
|
||||
|
||||
export function to(position: types.Position, suggestion: modes.ISuggestion): types.CompletionItem {
|
||||
const result = new types.CompletionItem(suggestion.label);
|
||||
result.insertText = suggestion.insertText;
|
||||
result.kind = CompletionItemKind.to(suggestion.type);
|
||||
result.detail = suggestion.detail;
|
||||
result.documentation = suggestion.documentation;
|
||||
result.sortText = suggestion.sortText;
|
||||
result.filterText = suggestion.filterText;
|
||||
|
||||
// 'overwrite[Before|After]'-logic
|
||||
let overwriteBefore = (typeof suggestion.overwriteBefore === 'number') ? suggestion.overwriteBefore : 0;
|
||||
let startPosition = new types.Position(position.line, Math.max(0, position.character - overwriteBefore));
|
||||
let endPosition = position;
|
||||
if (typeof suggestion.overwriteAfter === 'number') {
|
||||
endPosition = new types.Position(position.line, position.character + suggestion.overwriteAfter);
|
||||
}
|
||||
result.range = new types.Range(startPosition, endPosition);
|
||||
|
||||
// 'inserText'-logic
|
||||
if (suggestion.snippetType === 'textmate') {
|
||||
result.insertText = new types.SnippetString(suggestion.insertText);
|
||||
} else {
|
||||
result.insertText = suggestion.insertText;
|
||||
result.textEdit = new types.TextEdit(result.range, result.insertText);
|
||||
}
|
||||
|
||||
// TODO additionalEdits, command
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
export namespace SignatureHelp {
|
||||
|
||||
export function from(signatureHelp: types.SignatureHelp): modes.SignatureHelp {
|
||||
return signatureHelp;
|
||||
}
|
||||
|
||||
export function to(hints: modes.SignatureHelp): types.SignatureHelp {
|
||||
return hints;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace DocumentLink {
|
||||
|
||||
export function from(link: vscode.DocumentLink): modes.ILink {
|
||||
return {
|
||||
range: fromRange(link.range),
|
||||
url: link.target && link.target.toString()
|
||||
};
|
||||
}
|
||||
|
||||
export function to(link: modes.ILink): vscode.DocumentLink {
|
||||
return new types.DocumentLink(toRange(link.range), link.url && URI.parse(link.url));
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TextDocumentSaveReason {
|
||||
|
||||
export function to(reason: SaveReason): vscode.TextDocumentSaveReason {
|
||||
switch (reason) {
|
||||
case SaveReason.AUTO:
|
||||
return types.TextDocumentSaveReason.AfterDelay;
|
||||
case SaveReason.EXPLICIT:
|
||||
return types.TextDocumentSaveReason.Manual;
|
||||
case SaveReason.FOCUS_CHANGE:
|
||||
case SaveReason.WINDOW_CHANGE:
|
||||
return types.TextDocumentSaveReason.FocusOut;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export namespace EndOfLine {
|
||||
|
||||
export function from(eol: vscode.EndOfLine): EndOfLineSequence {
|
||||
if (eol === types.EndOfLine.CRLF) {
|
||||
return EndOfLineSequence.CRLF;
|
||||
} else if (eol === types.EndOfLine.LF) {
|
||||
return EndOfLineSequence.LF;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function to(eol: EndOfLineSequence): vscode.EndOfLine {
|
||||
if (eol === EndOfLineSequence.CRLF) {
|
||||
return types.EndOfLine.CRLF;
|
||||
} else if (eol === EndOfLineSequence.LF) {
|
||||
return types.EndOfLine.LF;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ProgressLocation {
|
||||
export function from(loc: vscode.ProgressLocation): MainProgressLocation {
|
||||
switch (loc) {
|
||||
case types.ProgressLocation.SourceControl: return MainProgressLocation.Scm;
|
||||
case types.ProgressLocation.Window: return MainProgressLocation.Window;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
1385
src/vs/workbench/api/node/extHostTypes.ts
Normal file
1385
src/vs/workbench/api/node/extHostTypes.ts
Normal file
File diff suppressed because it is too large
Load Diff
39
src/vs/workbench/api/node/extHostWindow.ts
Normal file
39
src/vs/workbench/api/node/extHostWindow.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
|
||||
import { ExtHostWindowShape, MainContext, MainThreadWindowShape } from './extHost.protocol';
|
||||
import { WindowState } from 'vscode';
|
||||
|
||||
export class ExtHostWindow implements ExtHostWindowShape {
|
||||
|
||||
private static InitialState: WindowState = {
|
||||
focused: true
|
||||
};
|
||||
|
||||
private _proxy: MainThreadWindowShape;
|
||||
|
||||
private _onDidChangeWindowState = new Emitter<WindowState>();
|
||||
readonly onDidChangeWindowState: Event<WindowState> = this._onDidChangeWindowState.event;
|
||||
|
||||
private _state = ExtHostWindow.InitialState;
|
||||
get state(): WindowState { return this._state; }
|
||||
|
||||
constructor(threadService: IThreadService) {
|
||||
this._proxy = threadService.get(MainContext.MainThreadWindow);
|
||||
this._proxy.$getWindowVisibility().then(isFocused => this.$onDidChangeWindowFocus(isFocused));
|
||||
}
|
||||
|
||||
$onDidChangeWindowFocus(focused: boolean): void {
|
||||
if (focused === this._state.focused) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._state = { ...this._state, focused };
|
||||
this._onDidChangeWindowState.fire(this._state);
|
||||
}
|
||||
}
|
||||
259
src/vs/workbench/api/node/extHostWorkspace.ts
Normal file
259
src/vs/workbench/api/node/extHostWorkspace.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { normalize } from 'vs/base/common/paths';
|
||||
import { delta } from 'vs/base/common/arrays';
|
||||
import { relative, basename } from 'path';
|
||||
import { Workspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { fromRange, EndOfLine } from 'vs/workbench/api/node/extHostTypeConverters';
|
||||
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext } from './extHost.protocol';
|
||||
import * as vscode from 'vscode';
|
||||
import { compare } from 'vs/base/common/strings';
|
||||
import { asWinJsPromise } from 'vs/base/common/async';
|
||||
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
|
||||
import { TrieMap } from 'vs/base/common/map';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Progress } from 'vs/platform/progress/common/progress';
|
||||
|
||||
class Workspace2 extends Workspace {
|
||||
|
||||
static fromData(data: IWorkspaceData) {
|
||||
return data ? new Workspace2(data) : null;
|
||||
}
|
||||
|
||||
private readonly _folder: vscode.WorkspaceFolder[] = [];
|
||||
private readonly _structure = new TrieMap<vscode.WorkspaceFolder>(s => s.split('/'));
|
||||
|
||||
private constructor(data: IWorkspaceData) {
|
||||
super(data.id, data.name, data.roots);
|
||||
|
||||
// setup the workspace folder data structure
|
||||
this.roots.forEach((uri, index) => {
|
||||
const folder = {
|
||||
name: basename(uri.fsPath),
|
||||
uri,
|
||||
index
|
||||
};
|
||||
this._folder.push(folder);
|
||||
this._structure.insert(folder.uri.toString(), folder);
|
||||
});
|
||||
}
|
||||
|
||||
get folders(): vscode.WorkspaceFolder[] {
|
||||
return this._folder.slice(0);
|
||||
}
|
||||
|
||||
getWorkspaceFolder(uri: URI): vscode.WorkspaceFolder {
|
||||
let str = uri.toString();
|
||||
let folder = this._structure.lookUp(str);
|
||||
if (folder) {
|
||||
// `uri` is a workspace folder so we
|
||||
let parts = str.split('/');
|
||||
while (parts.length) {
|
||||
if (parts.pop()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
str = parts.join('/');
|
||||
}
|
||||
return this._structure.findSubstr(str);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostWorkspace implements ExtHostWorkspaceShape {
|
||||
|
||||
private static _requestIdPool = 0;
|
||||
|
||||
private readonly _onDidChangeWorkspace = new Emitter<vscode.WorkspaceFoldersChangeEvent>();
|
||||
private readonly _proxy: MainThreadWorkspaceShape;
|
||||
private _workspace: Workspace2;
|
||||
|
||||
readonly onDidChangeWorkspace: Event<vscode.WorkspaceFoldersChangeEvent> = this._onDidChangeWorkspace.event;
|
||||
|
||||
constructor(mainContext: IMainContext, data: IWorkspaceData) {
|
||||
this._proxy = mainContext.get(MainContext.MainThreadWorkspace);
|
||||
this._workspace = Workspace2.fromData(data);
|
||||
}
|
||||
|
||||
// --- workspace ---
|
||||
|
||||
get workspace(): Workspace {
|
||||
return this._workspace;
|
||||
}
|
||||
|
||||
getWorkspaceFolders(): vscode.WorkspaceFolder[] {
|
||||
if (!this._workspace) {
|
||||
return undefined;
|
||||
} else {
|
||||
return this._workspace.folders.slice(0);
|
||||
}
|
||||
}
|
||||
|
||||
getWorkspaceFolder(uri: vscode.Uri): vscode.WorkspaceFolder {
|
||||
if (!this._workspace) {
|
||||
return undefined;
|
||||
}
|
||||
return this._workspace.getWorkspaceFolder(<URI>uri);
|
||||
}
|
||||
|
||||
getPath(): string {
|
||||
// this is legacy from the days before having
|
||||
// multi-root and we keep it only alive if there
|
||||
// is just one workspace folder.
|
||||
if (!this._workspace) {
|
||||
return undefined;
|
||||
}
|
||||
const { roots } = this._workspace;
|
||||
if (roots.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return roots[0].fsPath;
|
||||
}
|
||||
|
||||
getRelativePath(pathOrUri: string | vscode.Uri, includeWorkspace?: boolean): string {
|
||||
|
||||
let path: string;
|
||||
if (typeof pathOrUri === 'string') {
|
||||
path = pathOrUri;
|
||||
} else if (typeof pathOrUri !== 'undefined') {
|
||||
path = pathOrUri.fsPath;
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
const folder = this.getWorkspaceFolder(typeof pathOrUri === 'string'
|
||||
? URI.file(pathOrUri)
|
||||
: pathOrUri
|
||||
);
|
||||
|
||||
if (!folder) {
|
||||
return normalize(path);
|
||||
}
|
||||
|
||||
if (typeof includeWorkspace === 'undefined') {
|
||||
includeWorkspace = this.workspace.roots.length > 1;
|
||||
}
|
||||
|
||||
let result = relative(folder.uri.fsPath, path);
|
||||
if (includeWorkspace) {
|
||||
result = `${folder.name}/${result}`;
|
||||
}
|
||||
return normalize(result);
|
||||
}
|
||||
|
||||
$acceptWorkspaceData(data: IWorkspaceData): void {
|
||||
|
||||
// keep old workspace folder, build new workspace, and
|
||||
// capture new workspace folders. Compute delta between
|
||||
// them send that as event
|
||||
const oldRoots = this._workspace ? this._workspace.folders.sort(ExtHostWorkspace._compareWorkspaceFolder) : [];
|
||||
|
||||
this._workspace = Workspace2.fromData(data);
|
||||
const newRoots = this._workspace ? this._workspace.folders.sort(ExtHostWorkspace._compareWorkspaceFolder) : [];
|
||||
|
||||
const { added, removed } = delta(oldRoots, newRoots, ExtHostWorkspace._compareWorkspaceFolder);
|
||||
|
||||
// {{SQL CARBON EDIT}} - fix build break Argument of type 'Readonly<...
|
||||
this._onDidChangeWorkspace.fire({
|
||||
added: added,
|
||||
removed: removed
|
||||
});
|
||||
}
|
||||
|
||||
private static _compareWorkspaceFolder(a: vscode.WorkspaceFolder, b: vscode.WorkspaceFolder): number {
|
||||
return compare(a.uri.toString(), b.uri.toString());
|
||||
}
|
||||
|
||||
// --- search ---
|
||||
|
||||
findFiles(include: string, exclude: string, maxResults?: number, token?: vscode.CancellationToken): Thenable<vscode.Uri[]> {
|
||||
const requestId = ExtHostWorkspace._requestIdPool++;
|
||||
const result = this._proxy.$startSearch(include, exclude, maxResults, requestId);
|
||||
if (token) {
|
||||
token.onCancellationRequested(() => this._proxy.$cancelSearch(requestId));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
saveAll(includeUntitled?: boolean): Thenable<boolean> {
|
||||
return this._proxy.$saveAll(includeUntitled);
|
||||
}
|
||||
|
||||
appyEdit(edit: vscode.WorkspaceEdit): TPromise<boolean> {
|
||||
|
||||
let resourceEdits: IResourceEdit[] = [];
|
||||
|
||||
let entries = edit.entries();
|
||||
for (let entry of entries) {
|
||||
let [uri, edits] = entry;
|
||||
|
||||
for (let edit of edits) {
|
||||
resourceEdits.push({
|
||||
resource: <URI>uri,
|
||||
newText: edit.newText,
|
||||
newEol: EndOfLine.from(edit.newEol),
|
||||
range: edit.range && fromRange(edit.range)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this._proxy.$applyWorkspaceEdit(resourceEdits);
|
||||
}
|
||||
|
||||
// --- EXPERIMENT: workspace resolver
|
||||
|
||||
|
||||
private _handlePool = 0;
|
||||
private readonly _fsProvider = new Map<number, vscode.FileSystemProvider>();
|
||||
private readonly _searchSession = new Map<number, CancellationTokenSource>();
|
||||
|
||||
registerFileSystemProvider(authority: string, provider: vscode.FileSystemProvider): vscode.Disposable {
|
||||
const handle = ++this._handlePool;
|
||||
this._fsProvider.set(handle, provider);
|
||||
const reg = provider.onDidChange(e => this._proxy.$onFileSystemChange(handle, <URI>e));
|
||||
this._proxy.$registerFileSystemProvider(handle, authority);
|
||||
return new Disposable(() => {
|
||||
this._fsProvider.delete(handle);
|
||||
reg.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
$resolveFile(handle: number, resource: URI): TPromise<string> {
|
||||
const provider = this._fsProvider.get(handle);
|
||||
return asWinJsPromise(token => provider.resolveContents(resource));
|
||||
}
|
||||
|
||||
$storeFile(handle: number, resource: URI, content: string): TPromise<any> {
|
||||
const provider = this._fsProvider.get(handle);
|
||||
return asWinJsPromise(token => provider.writeContents(resource, content));
|
||||
}
|
||||
|
||||
$startSearch(handle: number, session: number, query: string): void {
|
||||
const provider = this._fsProvider.get(handle);
|
||||
const source = new CancellationTokenSource();
|
||||
const progress = new Progress<any>(chunk => this._proxy.$updateSearchSession(session, chunk));
|
||||
|
||||
this._searchSession.set(session, source);
|
||||
TPromise.wrap(provider.findFiles(query, progress, source.token)).then(() => {
|
||||
this._proxy.$finishSearchSession(session);
|
||||
}, err => {
|
||||
this._proxy.$finishSearchSession(session, err);
|
||||
});
|
||||
}
|
||||
|
||||
$cancelSearch(handle: number, session: number): void {
|
||||
if (this._searchSession.has(session)) {
|
||||
this._searchSession.get(session).cancel();
|
||||
this._searchSession.delete(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user