Merge branch 'master' into alexr00/terminalExitRace

This commit is contained in:
Alex Ross
2019-06-06 09:43:15 +02:00
committed by GitHub
560 changed files with 22209 additions and 7358 deletions

View File

@@ -1,70 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
// --- other interested parties
import { JSONValidationExtensionPoint } from 'vs/workbench/api/common/jsonValidationExtensionPoint';
import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorExtensionPoint';
import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint';
// --- mainThread participants
import './mainThreadClipboard';
import './mainThreadCommands';
import './mainThreadConfiguration';
import './mainThreadConsole';
import './mainThreadDebugService';
import './mainThreadDecorations';
import './mainThreadDiagnostics';
import './mainThreadDialogs';
import './mainThreadDocumentContentProviders';
import './mainThreadDocuments';
import './mainThreadDocumentsAndEditors';
import './mainThreadEditor';
import './mainThreadEditors';
import './mainThreadErrors';
import './mainThreadExtensionService';
import './mainThreadFileSystem';
import './mainThreadFileSystemEventService';
import './mainThreadHeapService';
import './mainThreadKeytar';
import './mainThreadLanguageFeatures';
import './mainThreadLanguages';
import './mainThreadLogService';
import './mainThreadMessageService';
import './mainThreadOutputService';
import './mainThreadProgress';
import './mainThreadQuickOpen';
import './mainThreadSaveParticipant';
import './mainThreadSCM';
import './mainThreadSearch';
import './mainThreadStatusBar';
import './mainThreadStorage';
import './mainThreadTelemetry';
import './mainThreadTerminalService';
import './mainThreadTreeViews';
import './mainThreadUrls';
import './mainThreadWindow';
import './mainThreadWorkspace';
import './mainThreadComments';
import './mainThreadTask';
import 'vs/workbench/api/common/apiCommands';
export class ExtensionPoints implements IWorkbenchContribution {
constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
// Classes that handle extension points...
this.instantiationService.createInstance(JSONValidationExtensionPoint);
this.instantiationService.createInstance(ColorExtensionPoint);
this.instantiationService.createInstance(LanguageConfigurationFileHandler);
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, LifecyclePhase.Starting);

View File

@@ -3,5 +3,70 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import './extensionHost.contribution.common';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
// --- other interested parties
import { JSONValidationExtensionPoint } from 'vs/workbench/api/common/jsonValidationExtensionPoint';
import { ColorExtensionPoint } from 'vs/workbench/services/themes/common/colorExtensionPoint';
import { LanguageConfigurationFileHandler } from 'vs/workbench/contrib/codeEditor/browser/languageConfigurationExtensionPoint';
// --- mainThread participants
import './mainThreadCodeInsets';
import './mainThreadClipboard';
import './mainThreadCommands';
import './mainThreadConfiguration';
import './mainThreadConsole';
import './mainThreadDebugService';
import './mainThreadDecorations';
import './mainThreadDiagnostics';
import './mainThreadDialogs';
import './mainThreadDocumentContentProviders';
import './mainThreadDocuments';
import './mainThreadDocumentsAndEditors';
import './mainThreadEditor';
import './mainThreadEditors';
import './mainThreadErrors';
import './mainThreadExtensionService';
import './mainThreadFileSystem';
import './mainThreadFileSystemEventService';
import './mainThreadHeapService';
import './mainThreadKeytar';
import './mainThreadLanguageFeatures';
import './mainThreadLanguages';
import './mainThreadLogService';
import './mainThreadMessageService';
import './mainThreadOutputService';
import './mainThreadProgress';
import './mainThreadQuickOpen';
import './mainThreadSaveParticipant';
import './mainThreadSCM';
import './mainThreadSearch';
import './mainThreadStatusBar';
import './mainThreadStorage';
import './mainThreadTelemetry';
import './mainThreadTerminalService';
import './mainThreadTreeViews';
import './mainThreadUrls';
import './mainThreadWindow';
import './mainThreadWebview';
import './mainThreadWorkspace';
import './mainThreadComments';
import './mainThreadTask';
import 'vs/workbench/api/common/apiCommands';
export class ExtensionPoints implements IWorkbenchContribution {
constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
// Classes that handle extension points...
this.instantiationService.createInstance(JSONValidationExtensionPoint);
this.instantiationService.createInstance(ColorExtensionPoint);
this.instantiationService.createInstance(LanguageConfigurationFileHandler);
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExtensionPoints, LifecyclePhase.Starting);

View File

@@ -0,0 +1,144 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { UriComponents, URI } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
import { MainContext, MainThreadEditorInsetsShape, IExtHostContext, ExtHostEditorInsetsShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from '../common/extHostCustomers';
import { IRange } from 'vs/editor/common/core/range';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/common/webview';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
// todo@joh move these things back into something like contrib/insets
class EditorWebviewZone implements IViewZone {
readonly domNode: HTMLElement;
readonly afterLineNumber: number;
readonly afterColumn: number;
readonly heightInLines: number;
private _id: number;
// suppressMouseDown?: boolean | undefined;
// heightInPx?: number | undefined;
// minWidthInPx?: number | undefined;
// marginDomNode?: HTMLElement | null | undefined;
// onDomNodeTop?: ((top: number) => void) | undefined;
// onComputedHeight?: ((height: number) => void) | undefined;
constructor(
readonly editor: IActiveCodeEditor,
readonly range: IRange,
readonly webview: Webview,
) {
this.domNode = document.createElement('div');
this.afterLineNumber = range.startLineNumber;
this.afterColumn = range.startColumn;
this.heightInLines = range.endLineNumber - range.startLineNumber;
editor.changeViewZones(accessor => this._id = accessor.addZone(this));
webview.mountTo(this.domNode);
}
dispose(): void {
this.editor.changeViewZones(accessor => accessor.removeZone(this._id));
}
}
@extHostNamedCustomer(MainContext.MainThreadEditorInsets)
export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
private readonly _proxy: ExtHostEditorInsetsShape;
private readonly _disposables = new DisposableStore();
private readonly _insets = new Map<number, EditorWebviewZone>();
constructor(
context: IExtHostContext,
@ICodeEditorService private readonly _editorService: ICodeEditorService,
@IWebviewService private readonly _webviewService: IWebviewService,
) {
this._proxy = context.getProxy(ExtHostContext.ExtHostEditorInsets);
}
dispose(): void {
this._disposables.dispose();
}
async $createEditorInset(handle: number, id: string, uri: UriComponents, range: IRange, options: modes.IWebviewOptions): Promise<void> {
let editor: IActiveCodeEditor | undefined;
id = id.substr(0, id.indexOf(',')); //todo@joh HACK
for (const candidate of this._editorService.listCodeEditors()) {
if (candidate.getId() === id && candidate.hasModel() && candidate.getModel()!.uri.toString() === URI.revive(uri).toString()) {
editor = candidate;
break;
}
}
if (!editor) {
setTimeout(() => this._proxy.$onDidDispose(handle));
return;
}
const disposables = new DisposableStore();
const webview = this._webviewService.createWebview({
enableFindWidget: false,
allowSvgs: false,
extension: undefined
}, {
allowScripts: options.enableScripts
});
const webviewZone = new EditorWebviewZone(editor, range, webview);
const remove = () => {
disposables.dispose();
this._proxy.$onDidDispose(handle);
this._insets.delete(handle);
};
disposables.add(editor.onDidChangeModel(remove));
disposables.add(editor.onDidDispose(remove));
disposables.add(webviewZone);
disposables.add(webview);
disposables.add(webview.onMessage(msg => this._proxy.$onDidReceiveMessage(handle, msg)));
this._insets.set(handle, webviewZone);
}
$disposeEditorInset(handle: number): void {
const inset = this._insets.get(handle);
if (inset) {
this._insets.delete(handle);
inset.dispose();
}
}
$setHtml(handle: number, value: string): void {
const inset = this._insets.get(handle);
if (inset) {
inset.webview.html = value;
}
}
$setOptions(handle: number, options: modes.IWebviewOptions): void {
const inset = this._insets.get(handle);
if (inset) {
inset.webview.options = options;
}
}
$postMessage(handle: number, value: any): Promise<boolean> {
const inset = this._insets.get(handle);
if (inset) {
inset.webview.sendMessage(value);
return Promise.resolve(true);
}
return Promise.resolve(false);
}
}

View File

@@ -12,7 +12,7 @@ import { revive } from 'vs/base/common/marshalling';
@extHostNamedCustomer(MainContext.MainThreadCommands)
export class MainThreadCommands implements MainThreadCommandsShape {
private readonly _disposables = new Map<string, IDisposable>();
private readonly _commandRegistrations = new Map<string, IDisposable>();
private readonly _generateCommandsDocumentationRegistration: IDisposable;
private readonly _proxy: ExtHostCommandsShape;
@@ -26,8 +26,8 @@ export class MainThreadCommands implements MainThreadCommandsShape {
}
dispose() {
this._disposables.forEach(value => value.dispose());
this._disposables.clear();
this._commandRegistrations.forEach(value => value.dispose());
this._commandRegistrations.clear();
this._generateCommandsDocumentationRegistration.dispose();
}
@@ -53,7 +53,7 @@ export class MainThreadCommands implements MainThreadCommandsShape {
}
$registerCommand(id: string): void {
this._disposables.set(
this._commandRegistrations.set(
id,
CommandsRegistry.registerCommand(id, (accessor, ...args) => {
return this._proxy.$executeContributedCommand(id, ...args).then(result => {
@@ -64,10 +64,10 @@ export class MainThreadCommands implements MainThreadCommandsShape {
}
$unregisterCommand(id: string): void {
const command = this._disposables.get(id);
const command = this._commandRegistrations.get(id);
if (command) {
command.dispose();
this._disposables.delete(id);
this._commandRegistrations.delete(id);
}
}

View File

@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { ICodeEditor, isCodeEditor, isDiffEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
import * as modes from 'vs/editor/common/modes';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
@@ -299,7 +299,8 @@ export class MainThreadCommentController {
this._features = features;
}
createCommentThread(commentThreadHandle: number,
createCommentThread(extensionId: string,
commentThreadHandle: number,
threadId: string,
resource: UriComponents,
range: IRange,
@@ -307,7 +308,7 @@ export class MainThreadCommentController {
let thread = new MainThreadCommentThread(
commentThreadHandle,
this.handle,
'',
extensionId,
threadId,
URI.revive(resource).toString(),
range
@@ -451,6 +452,10 @@ export class MainThreadCommentController {
this._proxy.$createCommentThreadTemplate(this.handle, resource, range);
}
async updateCommentThreadTemplate(threadHandle: number, range: IRange) {
await this._proxy.$updateCommentThreadTemplate(this.handle, threadHandle, range);
}
toJSON(): any {
return {
$mid: 6,
@@ -461,8 +466,6 @@ export class MainThreadCommentController {
@extHostNamedCustomer(MainContext.MainThreadComments)
export class MainThreadComments extends Disposable implements MainThreadCommentsShape {
private _disposables: IDisposable[];
private _activeCommentThreadDisposables: IDisposable[];
private readonly _proxy: ExtHostCommentsShape;
private _documentProviders = new Map<number, IDisposable>();
private _workspaceProviders = new Map<number, IDisposable>();
@@ -470,10 +473,12 @@ export class MainThreadComments extends Disposable implements MainThreadComments
private _commentControllers = new Map<number, MainThreadCommentController>();
private _activeCommentThread?: MainThreadCommentThread;
private readonly _activeCommentThreadDisposables = this._register(new DisposableStore());
private _input?: modes.CommentInput;
private _openPanelListener: IDisposable | null;
constructor(
extHostContext: IExtHostContext,
@IEditorService private readonly _editorService: IEditorService,
@@ -483,11 +488,9 @@ export class MainThreadComments extends Disposable implements MainThreadComments
@IConfigurationService private readonly _configurationService: IConfigurationService,
) {
super();
this._disposables = [];
this._activeCommentThreadDisposables = [];
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments);
this._disposables.push(this._commentService.onDidChangeActiveCommentThread(async thread => {
this._register(this._commentService.onDidChangeActiveCommentThread(async thread => {
let handle = (thread as MainThreadCommentThread).controllerHandle;
let controller = this._commentControllers.get(handle);
@@ -495,11 +498,11 @@ export class MainThreadComments extends Disposable implements MainThreadComments
return;
}
this._activeCommentThreadDisposables = dispose(this._activeCommentThreadDisposables);
this._activeCommentThreadDisposables.clear();
this._activeCommentThread = thread as MainThreadCommentThread;
controller.activeCommentThread = this._activeCommentThread;
this._activeCommentThreadDisposables.push(this._activeCommentThread.onDidChangeInput(input => { // todo, dispose
this._activeCommentThreadDisposables.add(this._activeCommentThread.onDidChangeInput(input => { // todo, dispose
this._input = input;
this._proxy.$onCommentWidgetInputChange(handle, URI.parse(this._activeCommentThread!.resource), this._activeCommentThread!.range, this._input ? this._input.value : undefined);
}));
@@ -548,7 +551,8 @@ export class MainThreadComments extends Disposable implements MainThreadComments
commentThreadHandle: number,
threadId: string,
resource: UriComponents,
range: IRange
range: IRange,
extensionId: ExtensionIdentifier
): modes.CommentThread2 | undefined {
let provider = this._commentControllers.get(handle);
@@ -556,7 +560,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
return undefined;
}
return provider.createCommentThread(commentThreadHandle, threadId, resource, range);
return provider.createCommentThread(extensionId.value, commentThreadHandle, threadId, resource, range);
}
$updateCommentThread(handle: number,
@@ -776,8 +780,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
}
dispose(): void {
this._disposables = dispose(this._disposables);
this._activeCommentThreadDisposables = dispose(this._activeCommentThreadDisposables);
super.dispose();
this._workspaceProviders.forEach(value => dispose(value));
this._workspaceProviders.clear();
this._documentProviders.forEach(value => dispose(value));

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle';
import { values } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor, isCodeEditor, isDiffEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -142,9 +142,8 @@ const enum ActiveEditorOrder {
Editor, Panel
}
class MainThreadDocumentAndEditorStateComputer {
class MainThreadDocumentAndEditorStateComputer extends Disposable {
private _toDispose: IDisposable[] = [];
private _toDisposeOnEditorRemove = new Map<string, IDisposable>();
private _currentState: DocumentAndEditorState;
private _activeEditorOrder: ActiveEditorOrder = ActiveEditorOrder.Editor;
@@ -156,25 +155,22 @@ class MainThreadDocumentAndEditorStateComputer {
@IEditorService private readonly _editorService: IEditorService,
@IPanelService private readonly _panelService: IPanelService
) {
this._modelService.onModelAdded(this._updateStateOnModelAdd, this, this._toDispose);
this._modelService.onModelRemoved(_ => this._updateState(), this, this._toDispose);
this._editorService.onDidActiveEditorChange(_ => this._updateState(), this, this._toDispose);
super();
this._register(this._modelService.onModelAdded(this._updateStateOnModelAdd, this));
this._register(this._modelService.onModelRemoved(_ => this._updateState(), this));
this._register(this._editorService.onDidActiveEditorChange(_ => this._updateState(), this));
this._codeEditorService.onCodeEditorAdd(this._onDidAddEditor, this, this._toDispose);
this._codeEditorService.onCodeEditorRemove(this._onDidRemoveEditor, this, this._toDispose);
this._register(this._codeEditorService.onCodeEditorAdd(this._onDidAddEditor, this));
this._register(this._codeEditorService.onCodeEditorRemove(this._onDidRemoveEditor, this));
this._codeEditorService.listCodeEditors().forEach(this._onDidAddEditor, this);
this._panelService.onDidPanelOpen(_ => this._activeEditorOrder = ActiveEditorOrder.Panel, undefined, this._toDispose);
this._panelService.onDidPanelClose(_ => this._activeEditorOrder = ActiveEditorOrder.Editor, undefined, this._toDispose);
this._editorService.onDidVisibleEditorsChange(_ => this._activeEditorOrder = ActiveEditorOrder.Editor, undefined, this._toDispose);
this._register(this._panelService.onDidPanelOpen(_ => this._activeEditorOrder = ActiveEditorOrder.Panel, undefined));
this._register(this._panelService.onDidPanelClose(_ => this._activeEditorOrder = ActiveEditorOrder.Editor, undefined));
this._register(this._editorService.onDidVisibleEditorsChange(_ => this._activeEditorOrder = ActiveEditorOrder.Editor, undefined));
this._updateState();
}
dispose(): void {
this._toDispose = dispose(this._toDispose);
}
private _onDidAddEditor(e: ICodeEditor): void {
this._toDisposeOnEditorRemove.set(e.getId(), combinedDisposable(
e.onDidChangeModel(() => this._updateState()),
@@ -302,17 +298,15 @@ class MainThreadDocumentAndEditorStateComputer {
}
@extHostCustomer
export class MainThreadDocumentsAndEditors {
export class MainThreadDocumentsAndEditors extends Disposable {
private _toDispose: IDisposable[];
private readonly _proxy: ExtHostDocumentsAndEditorsShape;
private readonly _stateComputer: MainThreadDocumentAndEditorStateComputer;
private _textEditors = <{ [id: string]: MainThreadTextEditor }>Object.create(null);
private _onTextEditorAdd = new Emitter<MainThreadTextEditor[]>();
private _onTextEditorRemove = new Emitter<string[]>();
private _onDocumentAdd = new Emitter<ITextModel[]>();
private _onDocumentRemove = new Emitter<URI[]>();
private _onTextEditorAdd = this._register(new Emitter<MainThreadTextEditor[]>());
private _onTextEditorRemove = this._register(new Emitter<string[]>());
private _onDocumentAdd = this._register(new Emitter<ITextModel[]>());
private _onDocumentRemove = this._register(new Emitter<URI[]>());
readonly onTextEditorAdd: Event<MainThreadTextEditor[]> = this._onTextEditorAdd.event;
readonly onTextEditorRemove: Event<string[]> = this._onTextEditorRemove.event;
@@ -334,30 +328,17 @@ export class MainThreadDocumentsAndEditors {
@IPanelService panelService: IPanelService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentsAndEditors);
const mainThreadDocuments = new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService, environmentService);
const mainThreadDocuments = this._register(new MainThreadDocuments(this, extHostContext, this._modelService, modeService, this._textFileService, fileService, textModelResolverService, untitledEditorService, environmentService));
extHostContext.set(MainContext.MainThreadDocuments, mainThreadDocuments);
const mainThreadTextEditors = new MainThreadTextEditors(this, extHostContext, codeEditorService, bulkEditService, this._editorService, this._editorGroupService);
const mainThreadTextEditors = this._register(new MainThreadTextEditors(this, extHostContext, codeEditorService, bulkEditService, this._editorService, this._editorGroupService));
extHostContext.set(MainContext.MainThreadTextEditors, mainThreadTextEditors);
// It is expected that the ctor of the state computer calls our `_onDelta`.
this._stateComputer = new MainThreadDocumentAndEditorStateComputer(delta => this._onDelta(delta), _modelService, codeEditorService, this._editorService, panelService);
this._toDispose = [
mainThreadDocuments,
mainThreadTextEditors,
this._stateComputer,
this._onTextEditorAdd,
this._onTextEditorRemove,
this._onDocumentAdd,
this._onDocumentRemove,
];
}
dispose(): void {
this._toDispose = dispose(this._toDispose);
this._register(new MainThreadDocumentAndEditorStateComputer(delta => this._onDelta(delta), _modelService, codeEditorService, this._editorService, panelService));
}
private _onDelta(delta: DocumentAndEditorStateDelta): void {

View File

@@ -5,7 +5,7 @@
import { localize } from 'vs/nls';
import { disposed } from 'vs/base/common/errors';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { equals as objectEquals } from 'vs/base/common/objects';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
@@ -25,14 +25,13 @@ import { EditorViewColumn, editorGroupToViewColumn, viewColumnToEditorGroup } fr
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
export class MainThreadTextEditors implements MainThreadTextEditorsShape {
export class MainThreadTextEditors extends Disposable implements MainThreadTextEditorsShape {
private static INSTANCE_COUNT: number = 0;
private readonly _instanceId: string;
private readonly _proxy: ExtHostEditorsShape;
private readonly _documentsAndEditors: MainThreadDocumentsAndEditors;
private _toDispose: IDisposable[];
private _textEditorsListenersMap: { [editorId: string]: IDisposable[]; };
private _editorPositionData: ITextEditorPositionData | null;
private _registeredDecorationTypes: { [decorationType: string]: boolean; };
@@ -45,29 +44,29 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
@IEditorService private readonly _editorService: IEditorService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService
) {
super();
this._instanceId = String(++MainThreadTextEditors.INSTANCE_COUNT);
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostEditors);
this._documentsAndEditors = documentsAndEditors;
this._toDispose = [];
this._textEditorsListenersMap = Object.create(null);
this._editorPositionData = null;
this._toDispose.push(documentsAndEditors.onTextEditorAdd(editors => editors.forEach(this._onTextEditorAdd, this)));
this._toDispose.push(documentsAndEditors.onTextEditorRemove(editors => editors.forEach(this._onTextEditorRemove, this)));
this._register(documentsAndEditors.onTextEditorAdd(editors => editors.forEach(this._onTextEditorAdd, this)));
this._register(documentsAndEditors.onTextEditorRemove(editors => editors.forEach(this._onTextEditorRemove, this)));
this._toDispose.push(this._editorService.onDidVisibleEditorsChange(() => this._updateActiveAndVisibleTextEditors()));
this._toDispose.push(this._editorGroupService.onDidRemoveGroup(() => this._updateActiveAndVisibleTextEditors()));
this._toDispose.push(this._editorGroupService.onDidMoveGroup(() => this._updateActiveAndVisibleTextEditors()));
this._register(this._editorService.onDidVisibleEditorsChange(() => this._updateActiveAndVisibleTextEditors()));
this._register(this._editorGroupService.onDidRemoveGroup(() => this._updateActiveAndVisibleTextEditors()));
this._register(this._editorGroupService.onDidMoveGroup(() => this._updateActiveAndVisibleTextEditors()));
this._registeredDecorationTypes = Object.create(null);
}
public dispose(): void {
super.dispose();
Object.keys(this._textEditorsListenersMap).forEach((editorId) => {
dispose(this._textEditorsListenersMap[editorId]);
});
this._textEditorsListenersMap = Object.create(null);
this._toDispose = dispose(this._toDispose);
for (let decorationType in this._registeredDecorationTypes) {
this._codeEditorService.removeDecorationType(decorationType);
}

View File

@@ -16,16 +16,14 @@ interface IKeytarModule {
@extHostNamedCustomer(MainContext.MainThreadKeytar)
export class MainThreadKeytar implements MainThreadKeytarShape {
private _keytar: IKeytarModule | null;
private _keytar: Promise<IKeytarModule | null>;
constructor(
extHostContext: IExtHostContext
) {
try {
this._keytar = <IKeytarModule>require.__$__nodeRequire('keytar');
} catch (e) {
this._keytar = null;
}
// tslint:disable-next-line:import-patterns
this._keytar = import('keytar')
.catch(e => null);
}
dispose(): void {
@@ -33,28 +31,32 @@ export class MainThreadKeytar implements MainThreadKeytarShape {
}
async $getPassword(service: string, account: string): Promise<string | null> {
if (this._keytar) {
return this._keytar.getPassword(service, account);
const keytar = await this._keytar;
if (keytar) {
return keytar.getPassword(service, account);
}
return null;
}
async $setPassword(service: string, account: string, password: string): Promise<void> {
if (this._keytar) {
return this._keytar.setPassword(service, account, password);
const keytar = await this._keytar;
if (keytar) {
return keytar.setPassword(service, account, password);
}
}
async $deletePassword(service: string, account: string): Promise<boolean> {
if (this._keytar) {
return this._keytar.deletePassword(service, account);
const keytar = await this._keytar;
if (keytar) {
return keytar.deletePassword(service, account);
}
return false;
}
async $findPassword(service: string): Promise<string | null> {
if (this._keytar) {
return this._keytar.findPassword(service);
const keytar = await this._keytar;
if (keytar) {
return keytar.findPassword(service);
}
return null;
}

View File

@@ -11,14 +11,13 @@ import * as search from 'vs/workbench/contrib/search/common/search';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange, IRange } from 'vs/editor/common/core/range';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, CodeInsetDto, LinkDto, CallHierarchyDto, SuggestDataDto } from '../common/extHost.protocol';
import { ExtHostContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, MainContext, IExtHostContext, ISerializedLanguageConfiguration, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, LocationDto, WorkspaceSymbolDto, CodeActionDto, reviveWorkspaceEditDto, ISerializedDocumentFilter, DefinitionLinkDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CallHierarchyDto, SuggestDataDto } from '../common/extHost.protocol';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { LanguageConfiguration, IndentationRule, OnEnterRule } from 'vs/editor/common/modes/languageConfiguration';
import { IModeService } from 'vs/editor/common/services/modeService';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { URI } from 'vs/base/common/uri';
import { Selection } from 'vs/editor/common/core/selection';
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import * as callh from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { IHeapService } from 'vs/workbench/services/heap/common/heap';
@@ -140,25 +139,19 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void {
const provider = <modes.CodeLensProvider>{
provideCodeLenses: (model: ITextModel, token: CancellationToken): modes.ICodeLensSymbol[] | Promise<modes.ICodeLensSymbol[]> => {
return this._proxy.$provideCodeLenses(handle, model.uri, token).then(dto => {
if (dto) {
dto.forEach(obj => {
this._heapService.trackObject(obj);
this._heapService.trackObject(obj.command);
});
provideCodeLenses: (model: ITextModel, token: CancellationToken): Promise<modes.CodeLensList | undefined> => {
return this._proxy.$provideCodeLenses(handle, model.uri, token).then(listDto => {
if (!listDto) {
return undefined;
}
return dto;
return {
lenses: listDto.lenses,
dispose: () => listDto.cacheId && this._proxy.$releaseCodeLenses(handle, listDto.cacheId)
};
});
},
resolveCodeLens: (_model: ITextModel, codeLens: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol | undefined> => {
return this._proxy.$resolveCodeLens(handle, codeLens, token).then(obj => {
if (obj) {
this._heapService.trackObject(obj);
this._heapService.trackObject(obj.command);
}
return obj;
});
resolveCodeLens: (_model: ITextModel, codeLens: modes.CodeLens, token: CancellationToken): Promise<modes.CodeLens | undefined> => {
return this._proxy.$resolveCodeLens(handle, codeLens, token);
}
};
@@ -178,35 +171,6 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
}
}
// -- code inset
$registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void {
const provider = <codeInset.CodeInsetProvider>{
provideCodeInsets: (model: ITextModel, token: CancellationToken): CodeInsetDto[] | Thenable<CodeInsetDto[]> => {
return this._proxy.$provideCodeInsets(handle, model.uri, token).then(dto => {
if (dto) { dto.forEach(obj => this._heapService.trackObject(obj)); }
return dto;
});
},
resolveCodeInset: (model: ITextModel, codeInset: CodeInsetDto, token: CancellationToken): CodeInsetDto | Thenable<CodeInsetDto> => {
return this._proxy.$resolveCodeInset(handle, model.uri, codeInset, token).then(obj => {
this._heapService.trackObject(obj);
return obj;
});
}
};
if (typeof eventHandle === 'number') {
const emitter = new Emitter<codeInset.CodeInsetProvider>();
this._registrations[eventHandle] = emitter;
provider.onDidChange = emitter.event;
}
const langSelector = selector;
this._registrations[handle] = codeInset.CodeInsetProviderRegistry.register(langSelector, provider);
}
// --- declaration
$registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void {

View File

@@ -90,7 +90,8 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
// if promise has not been resolved yet, now is the time to ensure a return value
// otherwise if already resolved it means the user clicked one of the buttons
Event.once(messageHandle.onDidClose)(() => {
dispose(...primaryActions, ...secondaryActions);
dispose(primaryActions);
dispose(secondaryActions);
resolve(undefined);
});
});

View File

@@ -3,46 +3,365 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { UriComponents } from 'vs/base/common/uri';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as map from 'vs/base/common/map';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
import { localize } from 'vs/nls';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { MainContext, MainThreadWebviewsShape, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol';
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor';
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { extHostNamedCustomer } from '../common/extHostCustomers';
import { IProductService } from 'vs/platform/product/common/product';
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape {
$createWebviewPanel(handle: string, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void {
throw new Error('Method not implemented.');
private static readonly standardSupportedLinkSchemes = new Set([
'http',
'https',
'mailto',
'vscode',
'vscode-insider',
]);
private static revivalPool = 0;
private readonly _proxy: ExtHostWebviewsShape;
private readonly _webviews = new Map<WebviewPanelHandle, WebviewEditorInput>();
private readonly _revivers = new Map<string, IDisposable>();
private _activeWebview: WebviewPanelHandle | undefined = undefined;
constructor(
context: IExtHostContext,
@ILifecycleService lifecycleService: ILifecycleService,
@IExtensionService extensionService: IExtensionService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IEditorService private readonly _editorService: IEditorService,
@IWebviewEditorService private readonly _webviewService: IWebviewEditorService,
@IOpenerService private readonly _openerService: IOpenerService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IProductService private readonly _productService: IProductService,
) {
super();
this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews);
this._register(_editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this));
this._register(_editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this));
// This reviver's only job is to activate webview extensions
// This should trigger the real reviver to be registered from the extension host side.
this._register(_webviewService.registerReviver({
canRevive: (webview) => {
const viewType = webview.state.viewType;
if (viewType) {
extensionService.activateByEvent(`onWebviewPanel:${viewType}`);
}
return false;
},
reviveWebview: () => { throw new Error('not implemented'); }
}));
this._register(lifecycleService.onBeforeShutdown(e => {
e.veto(this._onBeforeShutdown());
}, this));
}
$createWebviewCodeInset(handle: number, symbolId: string, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier | undefined, extensionLocation: UriComponents | undefined): void {
throw new Error('Method not implemented.');
public $createWebviewPanel(
handle: WebviewPanelHandle,
viewType: string,
title: string,
showOptions: { viewColumn?: EditorViewColumn, preserveFocus?: boolean },
options: WebviewInputOptions,
extensionId: ExtensionIdentifier,
extensionLocation: UriComponents
): void {
const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null);
if (showOptions) {
mainThreadShowOptions.preserveFocus = !!showOptions.preserveFocus;
mainThreadShowOptions.group = viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn);
}
const webview = this._webviewService.createWebview(this.getInternalWebviewId(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), {
location: URI.revive(extensionLocation),
id: extensionId
}, this.createWebviewEventDelegate(handle));
webview.state = {
viewType: viewType,
state: undefined
};
this._webviews.set(handle, webview);
/* __GDPR__
"webviews:createWebviewPanel" : {
"extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value });
}
$disposeWebview(handle: string): void {
throw new Error('Method not implemented.');
public $disposeWebview(handle: WebviewPanelHandle): void {
const webview = this.getWebview(handle);
webview.dispose();
}
$reveal(handle: string, showOptions: WebviewPanelShowOptions): void {
throw new Error('Method not implemented.');
public $setTitle(handle: WebviewPanelHandle, value: string): void {
const webview = this.getWebview(handle);
webview.setName(value);
}
$setTitle(handle: string, value: string): void {
throw new Error('Method not implemented.');
public $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void {
const webview = this.getWebview(handle);
webview.iconPath = reviveWebviewIcon(value);
}
$setIconPath(handle: string, value: { light: UriComponents; dark: UriComponents; } | undefined): void {
throw new Error('Method not implemented.');
public $setHtml(handle: WebviewPanelHandle, value: string): void {
const webview = this.getWebview(handle);
webview.html = value;
}
$setHtml(handle: string | number, value: string): void {
throw new Error('Method not implemented.');
public $setOptions(handle: WebviewPanelHandle, options: modes.IWebviewOptions): void {
const webview = this.getWebview(handle);
webview.setOptions(reviveWebviewOptions(options as any /*todo@mat */));
}
$setOptions(handle: string | number, options: modes.IWebviewOptions): void {
throw new Error('Method not implemented.');
public $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void {
const webview = this.getWebview(handle);
if (webview.isDisposed()) {
return;
}
const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn)) || this._editorGroupService.getGroup(webview.group || 0);
if (targetGroup) {
this._webviewService.revealWebview(webview, targetGroup, !!showOptions.preserveFocus);
}
}
$postMessage(handle: string | number, value: any): Promise<boolean> {
throw new Error('Method not implemented.');
public async $postMessage(handle: WebviewPanelHandle, message: any): Promise<boolean> {
const webview = this.getWebview(handle);
const editors = this._editorService.visibleControls
.filter(e => e instanceof WebviewEditor)
.map(e => e as WebviewEditor)
.filter(e => e.input!.matches(webview));
if (editors.length > 0) {
editors[0].sendMessage(message);
return true;
}
if (webview.webview) {
webview.webview.sendMessage(message);
return true;
}
return false;
}
$registerSerializer(viewType: string): void {
throw new Error('Method not implemented.');
public $registerSerializer(viewType: string): void {
if (this._revivers.has(viewType)) {
throw new Error(`Reviver for ${viewType} already registered`);
}
this._revivers.set(viewType, this._webviewService.registerReviver({
canRevive: (webview) => {
return webview.state && webview.state.viewType === viewType;
},
reviveWebview: async (webview): Promise<void> => {
const viewType = webview.state.viewType;
const handle = 'revival-' + MainThreadWebviews.revivalPool++;
this._webviews.set(handle, webview);
webview._events = this.createWebviewEventDelegate(handle);
let state = undefined;
if (webview.state.state) {
try {
state = JSON.parse(webview.state.state);
} catch {
// noop
}
}
try {
await this._proxy.$deserializeWebviewPanel(handle, viewType, webview.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webview.group || 0), webview.options);
} catch (error) {
onUnexpectedError(error);
webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
}
}
}));
}
$unregisterSerializer(viewType: string): void {
throw new Error('Method not implemented.');
public $unregisterSerializer(viewType: string): void {
const reviver = this._revivers.get(viewType);
if (!reviver) {
throw new Error(`No reviver for ${viewType} registered`);
}
reviver.dispose();
this._revivers.delete(viewType);
}
private getInternalWebviewId(viewType: string): string {
return `mainThreadWebview-${viewType}`;
}
private _onBeforeShutdown(): boolean {
this._webviews.forEach((webview) => {
if (!webview.isDisposed() && webview.state && this._revivers.has(webview.state.viewType)) {
webview.state.state = webview.webviewState;
}
});
return false; // Don't veto shutdown
}
private createWebviewEventDelegate(handle: WebviewPanelHandle) {
return {
onDidClickLink: (uri: URI) => this.onDidClickLink(handle, uri),
onMessage: (message: any) => this._proxy.$onMessage(handle, message),
onDispose: () => {
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
this._webviews.delete(handle);
});
}
};
}
private onActiveEditorChanged() {
const activeEditor = this._editorService.activeControl;
let newActiveWebview: { input: WebviewEditorInput, handle: WebviewPanelHandle } | undefined = undefined;
if (activeEditor && activeEditor.input instanceof WebviewEditorInput) {
for (const handle of map.keys(this._webviews)) {
const input = this._webviews.get(handle)!;
if (input.matches(activeEditor.input)) {
newActiveWebview = { input, handle };
break;
}
}
}
if (newActiveWebview && newActiveWebview.handle === this._activeWebview) {
// Webview itself unchanged but position may have changed
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, {
active: true,
visible: true,
position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group || 0)
});
return;
}
// Broadcast view state update for currently active
if (typeof this._activeWebview !== 'undefined') {
const oldActiveWebview = this._webviews.get(this._activeWebview);
if (oldActiveWebview) {
this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, {
active: false,
visible: this._editorService.visibleControls.some(editor => !!editor.input && editor.input.matches(oldActiveWebview)),
position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group || 0),
});
}
}
// Then for newly active
if (newActiveWebview) {
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, {
active: true,
visible: true,
position: editorGroupToViewColumn(this._editorGroupService, activeEditor ? activeEditor.group : ACTIVE_GROUP),
});
this._activeWebview = newActiveWebview.handle;
} else {
this._activeWebview = undefined;
}
}
private onVisibleEditorsChanged(): void {
this._webviews.forEach((input, handle) => {
for (const workbenchEditor of this._editorService.visibleControls) {
if (workbenchEditor.input && workbenchEditor.input.matches(input)) {
const editorPosition = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group!);
input.updateGroup(workbenchEditor.group!.id);
this._proxy.$onDidChangeWebviewPanelViewState(handle, {
active: handle === this._activeWebview,
visible: true,
position: editorPosition
});
break;
}
}
});
}
private onDidClickLink(handle: WebviewPanelHandle, link: URI): void {
if (!link) {
return;
}
const webview = this.getWebview(handle);
if (this.isSupportedLink(webview, link)) {
this._openerService.open(link);
}
}
private isSupportedLink(webview: WebviewEditorInput, link: URI): boolean {
if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) {
return true;
}
if (this._productService.urlProtocol === link.scheme) {
return true;
}
return !!webview.options.enableCommandUris && link.scheme === 'command';
}
private getWebview(handle: WebviewPanelHandle): WebviewEditorInput {
const webview = this._webviews.get(handle);
if (!webview) {
throw new Error('Unknown webview handle:' + handle);
}
return webview;
}
private static getDeserializationFailedContents(viewType: string) {
return `<!DOCTYPE html>
<html>
<head>
<base href="https://code.visualstudio.com/raw/">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https: data:; media-src https:; script-src 'none'; style-src vscode-core-resource: https: 'unsafe-inline'; child-src 'none'; frame-src 'none';">
</head>
<body>${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)}</body>
</html>`;
}
}
function reviveWebviewOptions(options: WebviewInputOptions): WebviewInputOptions {
return {
...options,
localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(r => URI.revive(r)) : undefined,
};
}
function reviveWebviewIcon(
value: { light: UriComponents, dark: UriComponents } | undefined
): { light: URI, dark: URI } | undefined {
if (!value) {
return undefined;
}
return {
light: URI.revive(value.light),
dark: URI.revive(value.dark)
};
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
@@ -13,10 +13,9 @@ import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@extHostNamedCustomer(MainContext.MainThreadWindow)
export class MainThreadWindow implements MainThreadWindowShape {
export class MainThreadWindow extends Disposable implements MainThreadWindowShape {
private readonly proxy: ExtHostWindowShape;
private disposables: IDisposable[] = [];
private readonly _tunnels = new Map<number, Promise<RemoteTunnel>>();
constructor(
@@ -26,14 +25,15 @@ export class MainThreadWindow implements MainThreadWindowShape {
@ITunnelService private readonly tunnelService: ITunnelService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) {
super();
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostWindow);
Event.latch(windowService.onDidChangeFocus)
(this.proxy.$onDidChangeWindowFocus, this.proxy, this.disposables);
this._register(Event.latch(windowService.onDidChangeFocus)
(this.proxy.$onDidChangeWindowFocus, this.proxy));
}
dispose(): void {
this.disposables = dispose(this.disposables);
super.dispose();
for (const tunnel of this._tunnels.values()) {
tunnel.then(tunnel => tunnel.dispose());

View File

@@ -13,7 +13,6 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ILabelService } from 'vs/platform/label/common/label';
import { IFileMatch, IPatternInfo, ISearchProgressItem, ISearchService } from 'vs/workbench/services/search/common/search';
import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
@@ -24,6 +23,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape, IWorkspaceData, ITextSearchComplete } from '../common/extHost.protocol';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { isEqualOrParent } from 'vs/base/common/resources';
import { INotificationService } from 'vs/platform/notification/common/notification';
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@@ -39,7 +39,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@ITextFileService private readonly _textFileService: ITextFileService,
@IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService,
@IStatusbarService private readonly _statusbarService: IStatusbarService,
@INotificationService private readonly _notificationService: INotificationService,
@IWindowService private readonly _windowService: IWindowService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ILabelService private readonly _labelService: ILabelService,
@@ -66,7 +66,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
const workspaceFoldersToAdd = foldersToAdd.map(f => ({ uri: URI.revive(f.uri), name: f.name }));
// Indicate in status message
this._statusbarService.setStatusMessage(this.getStatusMessage(extensionName, workspaceFoldersToAdd.length, deleteCount), 10 * 1000 /* 10s */);
this._notificationService.status(this.getStatusMessage(extensionName, workspaceFoldersToAdd.length, deleteCount), { hideAfter: 10 * 1000 /* 10s */ });
return this._workspaceEditingService.updateFolders(index, deleteCount, workspaceFoldersToAdd, true);
}

View File

@@ -42,7 +42,6 @@ import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { ResolvedAuthority, RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { IRelativePattern } from 'vs/base/common/glob';
import { IRemoteConsoleLog } from 'vs/base/common/console';
@@ -137,7 +136,7 @@ export interface MainThreadCommentsShape extends IDisposable {
$registerCommentController(handle: number, id: string, label: string): void;
$unregisterCommentController(handle: number): void;
$updateCommentControllerFeatures(handle: number, features: CommentProviderFeatures): void;
$createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange): modes.CommentThread2 | undefined;
$createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, extensionId: ExtensionIdentifier): modes.CommentThread2 | undefined;
$updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange, label: string, contextValue: string | undefined, comments: modes.Comment[], acceptInputCommand: modes.Command | undefined, additionalCommands: modes.Command[], deleteCommand: modes.Command | undefined, collapseState: modes.CommentThreadCollapsibleState): void;
$deleteCommentThread(handle: number, commentThreadHandle: number): void;
$setInputValue(handle: number, input: string): void;
@@ -333,7 +332,6 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
$unregister(handle: number): void;
$registerDocumentSymbolProvider(handle: number, selector: ISerializedDocumentFilter[], label: string): void;
$registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void;
$registerCodeInsetSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number | undefined): void;
$emitCodeLensEvent(eventHandle: number, event?: any): void;
$registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
$registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void;
@@ -510,9 +508,21 @@ export interface MainThreadTelemetryShape extends IDisposable {
$publicLog(eventName: string, data?: any): void;
}
export type WebviewPanelHandle = string;
export interface MainThreadEditorInsetsShape extends IDisposable {
$createEditorInset(handle: number, editorId: string, document: UriComponents, range: IRange, options: modes.IWebviewOptions): Promise<void>;
$disposeEditorInset(handle: number): void;
export type WebviewInsetHandle = number;
$setHtml(handle: number, value: string): void;
$setOptions(handle: number, options: modes.IWebviewOptions): void;
$postMessage(handle: number, value: any): Promise<boolean>;
}
export interface ExtHostEditorInsetsShape {
$onDidDispose(handle: number): void;
$onDidReceiveMessage(handle: number, message: any): void;
}
export type WebviewPanelHandle = string;
export interface WebviewPanelShowOptions {
readonly viewColumn?: EditorViewColumn;
@@ -521,15 +531,14 @@ export interface WebviewPanelShowOptions {
export interface MainThreadWebviewsShape extends IDisposable {
$createWebviewPanel(handle: WebviewPanelHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): void;
$createWebviewCodeInset(handle: WebviewInsetHandle, symbolId: string, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier | undefined, extensionLocation: UriComponents | undefined): void;
$disposeWebview(handle: WebviewPanelHandle): void;
$reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void;
$setTitle(handle: WebviewPanelHandle, value: string): void;
$setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void;
$setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void;
$setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: modes.IWebviewOptions): void;
$postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, value: any): Promise<boolean>;
$setHtml(handle: WebviewPanelHandle, value: string): void;
$setOptions(handle: WebviewPanelHandle, options: modes.IWebviewOptions): void;
$postMessage(handle: WebviewPanelHandle, value: any): Promise<boolean>;
$registerSerializer(viewType: string): void;
$unregisterSerializer(viewType: string): void;
@@ -999,13 +1008,16 @@ export interface LinkDto {
tooltip?: string;
}
export interface CodeLensDto extends ObjectIdentifier {
range: IRange;
id?: string;
command?: CommandDto;
export interface CodeLensListDto {
cacheId?: number;
lenses: CodeLensDto[];
}
export type CodeInsetDto = ObjectIdentifier & codeInset.ICodeInsetSymbol;
export interface CodeLensDto {
cacheId?: ChainedCacheId;
range: IRange;
command?: CommandDto;
}
export interface CallHierarchyDto {
_id: number;
@@ -1019,10 +1031,9 @@ export interface CallHierarchyDto {
export interface ExtHostLanguageFeaturesShape {
$provideDocumentSymbols(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.DocumentSymbol[] | undefined>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensDto[]>;
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensListDto | undefined>;
$resolveCodeLens(handle: number, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined>;
$provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeInsetDto[] | undefined>;
$resolveCodeInset(handle: number, resource: UriComponents, symbol: CodeInsetDto, token: CancellationToken): Promise<CodeInsetDto>;
$releaseCodeLenses(handle: number, id: number): void;
$provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<DefinitionLinkDto[]>;
@@ -1210,6 +1221,7 @@ export interface ExtHostCommentsShape {
$provideDocumentComments(handle: number, document: UriComponents): Promise<modes.CommentInfo | null>;
$createNewCommentThread(handle: number, document: UriComponents, range: IRange, text: string): Promise<modes.CommentThread | null>;
$createCommentThreadTemplate(commentControllerHandle: number, uriComponents: UriComponents, range: IRange): void;
$updateCommentThreadTemplate(commentControllerHandle: number, threadHandle: number, range: IRange): Promise<void>;
$onCommentWidgetInputChange(commentControllerHandle: number, document: UriComponents, range: IRange, input: string | undefined): Promise<number | undefined>;
$deleteCommentThread(commentControllerHandle: number, commentThreadHandle: number): void;
$provideCommentingRanges(commentControllerHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise<IRange[] | undefined>;
@@ -1247,6 +1259,7 @@ export const MainContext = {
MainThreadDocuments: createMainId<MainThreadDocumentsShape>('MainThreadDocuments'),
MainThreadDocumentContentProviders: createMainId<MainThreadDocumentContentProvidersShape>('MainThreadDocumentContentProviders'),
MainThreadTextEditors: createMainId<MainThreadTextEditorsShape>('MainThreadTextEditors'),
MainThreadEditorInsets: createMainId<MainThreadEditorInsetsShape>('MainThreadEditorInsets'),
MainThreadErrors: createMainId<MainThreadErrorsShape>('MainThreadErrors'),
MainThreadTreeViews: createMainId<MainThreadTreeViewsShape>('MainThreadTreeViews'),
MainThreadKeytar: createMainId<MainThreadKeytarShape>('MainThreadKeytar'),
@@ -1297,6 +1310,7 @@ export const ExtHostContext = {
ExtHostWorkspace: createExtId<ExtHostWorkspaceShape>('ExtHostWorkspace'),
ExtHostWindow: createExtId<ExtHostWindowShape>('ExtHostWindow'),
ExtHostWebviews: createExtId<ExtHostWebviewsShape>('ExtHostWebviews'),
ExtHostEditorInsets: createExtId<ExtHostEditorInsetsShape>('ExtHostEditorInsets'),
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress'),
ExtHostComments: createMainId<ExtHostCommentsShape>('ExtHostComments'),
ExtHostStorage: createMainId<ExtHostStorageShape>('ExtHostStorage'),

View File

@@ -504,7 +504,7 @@ export class ExtHostApiCommands {
private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Promise<vscode.CodeLens[] | undefined> {
const args = { resource, itemResolveCount };
return this._commands.executeCommand<modes.ICodeLensSymbol[]>('_executeCodeLensProvider', args)
return this._commands.executeCommand<modes.CodeLens[]>('_executeCodeLensProvider', args)
.then(tryMapWith(item => {
return new types.CodeLens(
typeConverters.Range.to(item.range),

View File

@@ -0,0 +1,130 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter } from 'vs/base/common/event';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as vscode from 'vscode';
import { MainThreadEditorInsetsShape } from './extHost.protocol';
import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor';
export class ExtHostEditorInsets implements ExtHostEditorInsets {
private _handlePool = 0;
private _disposables = new DisposableStore();
private _insets = new Map<number, { editor: vscode.TextEditor, inset: vscode.WebviewEditorInset, onDidReceiveMessage: Emitter<any> }>();
constructor(
private readonly _proxy: MainThreadEditorInsetsShape,
private readonly _editors: ExtHostEditors
) {
// dispose editor inset whenever the hosting editor goes away
this._disposables.add(_editors.onDidChangeVisibleTextEditors(() => {
const visibleEditor = _editors.getVisibleTextEditors();
this._insets.forEach(value => {
if (visibleEditor.indexOf(value.editor) < 0) {
value.inset.dispose(); // will remove from `this._insets`
}
});
}));
}
dispose(): void {
this._insets.forEach(value => value.inset.dispose());
this._disposables.dispose();
}
createWebviewEditorInset(editor: vscode.TextEditor, range: vscode.Range, options?: vscode.WebviewOptions): vscode.WebviewEditorInset {
let apiEditor: ExtHostTextEditor | undefined;
for (const candidate of this._editors.getVisibleTextEditors()) {
if (candidate === editor) {
apiEditor = <ExtHostTextEditor>candidate;
break;
}
}
if (!apiEditor) {
throw new Error('not a visible editor');
}
const that = this;
const handle = this._handlePool++;
const onDidReceiveMessage = new Emitter<any>();
const onDidDispose = new Emitter<void>();
const webview = new class implements vscode.Webview {
private _html: string = '';
private _options: vscode.WebviewOptions;
set options(value: vscode.WebviewOptions) {
this._options = value;
that._proxy.$setOptions(handle, value);
}
get options(): vscode.WebviewOptions {
return this._options;
}
set html(value: string) {
this._html = value;
that._proxy.$setHtml(handle, value);
}
get html(): string {
return this._html;
}
get onDidReceiveMessage(): vscode.Event<any> {
return onDidReceiveMessage.event;
}
postMessage(message: any): Thenable<boolean> {
return that._proxy.$postMessage(handle, message);
}
};
const inset = new class implements vscode.WebviewEditorInset {
readonly editor: vscode.TextEditor = editor;
readonly range: vscode.Range = range;
readonly webview: vscode.Webview = webview;
readonly onDidDispose: vscode.Event<void> = onDidDispose.event;
dispose(): void {
if (that._insets.has(handle)) {
that._insets.delete(handle);
that._proxy.$disposeEditorInset(handle);
onDidDispose.fire();
// final cleanup
onDidDispose.dispose();
onDidReceiveMessage.dispose();
}
}
};
this._proxy.$createEditorInset(handle, apiEditor.id, apiEditor.document.uri, typeConverters.Range.from(range), options || {});
this._insets.set(handle, { editor, inset, onDidReceiveMessage });
return inset;
}
$onDidDispose(handle: number): void {
const value = this._insets.get(handle);
if (value) {
value.inset.dispose();
}
}
$onDidReceiveMessage(handle: number, message: any): void {
const value = this._insets.get(handle);
if (value) {
value.onDidReceiveMessage.fire(message);
}
}
}

View File

@@ -18,6 +18,7 @@ import { revive } from 'vs/base/common/marshalling';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { URI } from 'vs/base/common/uri';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
interface CommandHandler {
callback: Function;
@@ -211,6 +212,35 @@ export class CommandsConverter {
this._commands.registerCommand(true, this._delegatingCommandId, this._executeConvertedCommand, this);
}
toInternal2(command: vscode.Command | undefined, disposables: DisposableStore): CommandDto | undefined {
if (!command) {
return undefined;
}
const result: CommandDto = {
$ident: undefined,
id: command.command,
title: command.title,
tooltip: command.tooltip
};
if (command.command && isNonEmptyArray(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);
disposables.add(toDisposable(() => this._heap.delete(id)));
result.$ident = id;
result.id = this._delegatingCommandId;
result.arguments = [id];
}
return result;
}
toInternal(command: vscode.Command): CommandDto;
toInternal(command: undefined): undefined;
toInternal(command: vscode.Command | undefined): CommandDto | undefined;

View File

@@ -162,6 +162,16 @@ export class ExtHostComments implements ExtHostCommentsShape {
commentController.$createCommentThreadTemplate(uriComponents, range);
}
async $updateCommentThreadTemplate(commentControllerHandle: number, threadHandle: number, range: IRange) {
const commentController = this._commentControllers.get(commentControllerHandle);
if (!commentController) {
return;
}
commentController.$updateCommentThreadTemplate(threadHandle, range);
}
$onCommentWidgetInputChange(commentControllerHandle: number, uriComponents: UriComponents, range: IRange, input: string): Promise<number | undefined> {
const commentController = this._commentControllers.get(commentControllerHandle);
@@ -582,7 +592,8 @@ export class ExtHostCommentThread implements vscode.CommentThread {
private _id: string | undefined,
private _uri: vscode.Uri,
private _range: vscode.Range,
private _comments: vscode.Comment[]
private _comments: vscode.Comment[],
extensionId: ExtensionIdentifier
) {
if (this._id === undefined) {
this._id = `${_commentController.id}.${this.handle}`;
@@ -593,7 +604,8 @@ export class ExtHostCommentThread implements vscode.CommentThread {
this.handle,
this._id,
this._uri,
extHostTypeConverter.Range.from(this._range)
extHostTypeConverter.Range.from(this._range),
extensionId
);
this._localDisposables = [];
@@ -741,7 +753,7 @@ class ExtHostCommentController implements vscode.CommentController {
}
constructor(
_extension: IExtensionDescription,
private _extension: IExtensionDescription,
private _handle: number,
private readonly _commandsConverter: CommandsConverter,
private _proxy: MainThreadCommentsShape,
@@ -755,23 +767,30 @@ class ExtHostCommentController implements vscode.CommentController {
createCommentThread(id: string, resource: vscode.Uri, range: vscode.Range, comments: vscode.Comment[]): vscode.CommentThread;
createCommentThread(arg0: vscode.Uri | string, arg1: vscode.Uri | vscode.Range, arg2: vscode.Range | vscode.Comment[], arg3?: vscode.Comment[]): vscode.CommentThread {
if (typeof arg0 === 'string') {
const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, arg0, arg1 as vscode.Uri, arg2 as vscode.Range, arg3 as vscode.Comment[]);
const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, arg0, arg1 as vscode.Uri, arg2 as vscode.Range, arg3 as vscode.Comment[], this._extension.identifier);
this._threads.set(commentThread.handle, commentThread);
return commentThread;
} else {
const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, undefined, arg0 as vscode.Uri, arg1 as vscode.Range, arg2 as vscode.Comment[]);
const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, undefined, arg0 as vscode.Uri, arg1 as vscode.Range, arg2 as vscode.Comment[], this._extension.identifier);
this._threads.set(commentThread.handle, commentThread);
return commentThread;
}
}
$createCommentThreadTemplate(uriComponents: UriComponents, range: IRange) {
const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, undefined, URI.revive(uriComponents), extHostTypeConverter.Range.to(range), []);
const commentThread = new ExtHostCommentThread(this._proxy, this._commandsConverter, this, undefined, URI.revive(uriComponents), extHostTypeConverter.Range.to(range), [], this._extension.identifier);
commentThread.collapsibleState = modes.CommentThreadCollapsibleState.Expanded;
this._threads.set(commentThread.handle, commentThread);
return commentThread;
}
$updateCommentThreadTemplate(threadHandle: number, range: IRange) {
let thread = this._threads.get(threadHandle);
if (thread) {
thread.range = extHostTypeConverter.Range.to(range);
}
}
$deleteCommentThread(threadHandle: number) {
let thread = this._threads.get(threadHandle);

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import { ExtHostDocumentsShape, IMainContext, MainContext, MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol';
@@ -13,7 +13,7 @@ import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocum
import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as vscode from 'vscode';
export class ExtHostDocuments implements ExtHostDocumentsShape {
export class ExtHostDocuments extends Disposable implements ExtHostDocumentsShape {
private _onDidAddDocument = new Emitter<vscode.TextDocument>();
private _onDidRemoveDocument = new Emitter<vscode.TextDocument>();
@@ -25,31 +25,25 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
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, Promise<ExtHostDocumentData>>();
constructor(mainContext: IMainContext, documentsAndEditors: ExtHostDocumentsAndEditors) {
super();
this._proxy = mainContext.getProxy(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);
this._register(this._documentsAndEditors.onDidRemoveDocuments(documents => {
for (const data of documents) {
this._onDidRemoveDocument.fire(data.document);
}
}));
this._register(this._documentsAndEditors.onDidAddDocuments(documents => {
for (const data of documents) {
this._onDidAddDocument.fire(data.document);
}
}));
}
public getAllDocumentData(): ExtHostDocumentData[] {

View File

@@ -15,7 +15,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics';
import { asPromise } from 'vs/base/common/async';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, MainThreadWebviewsShape, CodeInsetDto, SuggestDataDto, LinksListDto, ChainedCacheId } from './extHost.protocol';
import { MainContext, MainThreadLanguageFeaturesShape, ExtHostLanguageFeaturesShape, ObjectIdentifier, IRawColorInfo, IMainContext, IdObject, ISerializedRegExp, ISerializedIndentationRule, ISerializedOnEnterRule, ISerializedLanguageConfiguration, WorkspaceSymbolDto, SuggestResultDto, WorkspaceSymbolsDto, CodeActionDto, ISerializedDocumentFilter, WorkspaceEditDto, ISerializedSignatureHelpProviderMetadata, LinkDto, CodeLensDto, SuggestDataDto, LinksListDto, ChainedCacheId, CodeLensListDto } from './extHost.protocol';
import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings';
import { IPosition } from 'vs/editor/common/core/position';
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
@@ -25,12 +25,10 @@ import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ExtHostWebview } from 'vs/workbench/api/common/extHostWebview';
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
import { generateUuid } from 'vs/base/common/uuid';
import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { LRUCache } from 'vs/base/common/map';
import { IURITransformer } from 'vs/base/common/uriIpc';
import { DisposableStore, dispose } from 'vs/base/common/lifecycle';
// --- adapter
@@ -105,35 +103,50 @@ class CodeLensAdapter {
private static _badCmd: vscode.Command = { command: 'missing', title: '!!MISSING: command!!' };
private readonly _cache = new Cache<vscode.CodeLens>();
private readonly _disposables = new Map<number, DisposableStore>();
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _commands: CommandsConverter,
private readonly _heapService: ExtHostHeapService,
private readonly _provider: vscode.CodeLensProvider
) { }
provideCodeLenses(resource: URI, token: CancellationToken): Promise<CodeLensDto[]> {
provideCodeLenses(resource: URI, token: CancellationToken): Promise<CodeLensListDto | undefined> {
const doc = this._documents.getDocument(resource);
return asPromise(() => this._provider.provideCodeLenses(doc, token)).then(lenses => {
const result: CodeLensDto[] = [];
if (isNonEmptyArray(lenses)) {
for (const lens of lenses) {
const id = this._heapService.keep(lens);
result.push(ObjectIdentifier.mixin({
range: typeConvert.Range.from(lens.range),
command: this._commands.toInternal(lens.command)
}, id));
}
if (!lenses || token.isCancellationRequested) {
return undefined;
}
const cacheId = this._cache.add(lenses);
const disposables = new DisposableStore();
this._disposables.set(cacheId, disposables);
const result: CodeLensListDto = {
cacheId,
lenses: [],
};
for (let i = 0; i < lenses.length; i++) {
result.lenses.push({
cacheId: [cacheId, i],
range: typeConvert.Range.from(lenses[i].range),
command: this._commands.toInternal2(lenses[i].command, disposables)
});
}
return result;
});
}
resolveCodeLens(symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined> {
const lens = this._heapService.get<vscode.CodeLens>(ObjectIdentifier.of(symbol));
if (!lens) {
const lens = symbol.cacheId && this._cache.get(...symbol.cacheId);
const disposables = symbol.cacheId && this._disposables.get(symbol.cacheId[0]);
if (!lens || !disposables) {
return Promise.resolve(undefined);
}
@@ -146,50 +159,15 @@ class CodeLensAdapter {
return resolve.then(newLens => {
newLens = newLens || lens;
symbol.command = this._commands.toInternal(newLens.command || CodeLensAdapter._badCmd);
symbol.command = this._commands.toInternal2(newLens.command || CodeLensAdapter._badCmd, disposables);
return symbol;
});
}
}
class CodeInsetAdapter {
constructor(
private readonly _documents: ExtHostDocuments,
private readonly _heapService: ExtHostHeapService,
private readonly _provider: vscode.CodeInsetProvider
) { }
provideCodeInsets(resource: URI, token: CancellationToken): Promise<CodeInsetDto[] | undefined> {
const doc = this._documents.getDocument(resource);
return asPromise(() => this._provider.provideCodeInsets(doc, token)).then(insets => {
if (Array.isArray(insets)) {
return insets.map(inset => {
const $ident = this._heapService.keep(inset);
const id = generateUuid();
return {
$ident,
id,
range: typeConvert.Range.from(inset.range),
height: inset.height
};
});
}
return undefined;
});
}
resolveCodeInset(symbol: CodeInsetDto, webview: vscode.Webview, token: CancellationToken): Promise<CodeInsetDto> {
const inset = this._heapService.get<vscode.CodeInset>(ObjectIdentifier.of(symbol));
if (!inset) {
return Promise.resolve(symbol);
}
return asPromise(() => this._provider.resolveCodeInset(inset, webview, token)).then(newInset => {
newInset = newInset || inset;
return symbol;
});
releaseCodeLenses(cachedId: number): void {
dispose(this._disposables.get(cachedId));
this._disposables.delete(cachedId);
this._cache.delete(cachedId);
}
}
@@ -625,6 +603,7 @@ class SuggestAdapter {
private _provider: vscode.CompletionItemProvider;
private _cache = new Cache<vscode.CompletionItem>();
private _disposables = new Map<number, DisposableStore>();
constructor(documents: ExtHostDocuments, commands: CommandsConverter, provider: vscode.CompletionItemProvider) {
this._documents = documents;
@@ -650,13 +629,12 @@ class SuggestAdapter {
return undefined;
}
let list = Array.isArray(value) ? new CompletionList(value) : value;
let pid: number | undefined;
const list = Array.isArray(value) ? new CompletionList(value) : value;
// keep result for providers that support resolving
if (SuggestAdapter.supportsResolving(this._provider)) {
pid = this._cache.add(list.items);
}
const pid: number = SuggestAdapter.supportsResolving(this._provider) ? this._cache.add(list.items) : this._cache.add([]);
const disposables = new DisposableStore();
this._disposables.set(pid, disposables);
// the default text edit range
const wordRangeBeforePos = (doc.getWordRangeAtPosition(pos) as Range || new Range(pos, pos))
@@ -670,7 +648,7 @@ class SuggestAdapter {
};
for (let i = 0; i < list.items.length; i++) {
const suggestion = this._convertCompletionItem(list.items[i], pos, pid && [pid, i] || undefined);
const suggestion = this._convertCompletionItem(list.items[i], pos, [pid, i]);
// check for bad completion item
// for the converter did warn
if (suggestion) {
@@ -705,15 +683,22 @@ class SuggestAdapter {
}
releaseCompletionItems(id: number): any {
dispose(this._disposables.get(id));
this._disposables.delete(id);
this._cache.delete(id);
}
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: ChainedCacheId | undefined): SuggestDataDto | undefined {
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: ChainedCacheId): SuggestDataDto | undefined {
if (typeof item.label !== 'string' || item.label.length === 0) {
console.warn('INVALID text edit -> must have at least a label');
return undefined;
}
const disposables = this._disposables.get(id[0]);
if (!disposables) {
throw Error('DisposableStore is missing...');
}
const result: SuggestDataDto = {
//
x: id,
@@ -728,7 +713,7 @@ class SuggestAdapter {
i: item.keepWhitespace ? modes.CompletionItemInsertTextRule.KeepWhitespace : 0,
k: item.commitCharacters,
l: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
m: this._commands.toInternal(item.command),
m: this._commands.toInternal2(item.command, disposables),
};
// 'insertText'-logic
@@ -1040,7 +1025,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov
| DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter
| ColorProviderAdapter | FoldingProviderAdapter | CodeInsetAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter;
| ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter;
class AdapterData {
constructor(
@@ -1061,7 +1046,6 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
private _diagnostics: ExtHostDiagnostics;
private _adapter = new Map<number, AdapterData>();
private readonly _logService: ILogService;
private _webviewProxy: MainThreadWebviewsShape;
constructor(
mainContext: IMainContext,
@@ -1079,7 +1063,6 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
this._heapService = heapMonitor;
this._diagnostics = diagnostics;
this._logService = logService;
this._webviewProxy = mainContext.getProxy(MainContext.MainThreadWebviews);
}
private _transformDocumentSelector(selector: vscode.DocumentSelector): Array<ISerializedDocumentFilter> {
@@ -1182,7 +1165,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
const handle = this._nextHandle();
const eventHandle = typeof provider.onDidChangeCodeLenses === 'function' ? this._nextHandle() : undefined;
this._adapter.set(handle, new AdapterData(new CodeLensAdapter(this._documents, this._commands.converter, this._heapService, provider), extension));
this._adapter.set(handle, new AdapterData(new CodeLensAdapter(this._documents, this._commands.converter, provider), extension));
this._proxy.$registerCodeLensSupport(handle, this._transformDocumentSelector(selector), eventHandle);
let result = this._createDisposable(handle);
@@ -1194,43 +1177,16 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape {
return result;
}
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<modes.ICodeLensSymbol[]> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), []);
$provideCodeLenses(handle: number, resource: UriComponents, token: CancellationToken): Promise<CodeLensListDto | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.provideCodeLenses(URI.revive(resource), token), undefined);
}
$resolveCodeLens(handle: number, symbol: modes.ICodeLensSymbol, token: CancellationToken): Promise<modes.ICodeLensSymbol | undefined> {
$resolveCodeLens(handle: number, symbol: CodeLensDto, token: CancellationToken): Promise<CodeLensDto | undefined> {
return this._withAdapter(handle, CodeLensAdapter, adapter => adapter.resolveCodeLens(symbol, token), undefined);
}
// --- code insets
registerCodeInsetProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable {
const handle = this._nextHandle();
const eventHandle = typeof provider.onDidChangeCodeInsets === 'function' ? this._nextHandle() : undefined;
this._adapter.set(handle, new AdapterData(new CodeInsetAdapter(this._documents, this._heapService, provider), extension));
this._proxy.$registerCodeInsetSupport(handle, this._transformDocumentSelector(selector), eventHandle);
let result = this._createDisposable(handle);
if (eventHandle !== undefined && provider.onDidChangeCodeInsets) {
const subscription = provider.onDidChangeCodeInsets(_ => this._proxy.$emitCodeLensEvent(eventHandle));
result = Disposable.from(result, subscription);
}
return result;
}
$provideCodeInsets(handle: number, resource: UriComponents, token: CancellationToken): Promise<codeInset.ICodeInsetSymbol[] | undefined> {
return this._withAdapter(handle, CodeInsetAdapter, adapter => adapter.provideCodeInsets(URI.revive(resource), token), undefined);
}
$resolveCodeInset(handle: number, _resource: UriComponents, symbol: codeInset.ICodeInsetSymbol, token: CancellationToken): Promise<codeInset.ICodeInsetSymbol> {
const webviewHandle = Math.random();
const webview = new ExtHostWebview(webviewHandle, this._webviewProxy, { enableScripts: true });
return this._withAdapter(handle, CodeInsetAdapter, async (adapter, extension) => {
await this._webviewProxy.$createWebviewCodeInset(webviewHandle, symbol.id, { enableCommandUris: true, enableScripts: true }, extension ? extension.identifier : undefined, extension ? extension.extensionLocation : undefined);
return adapter.resolveCodeInset(symbol, webview, token);
}, symbol);
$releaseCodeLenses(handle: number, cacheId: number): void {
this._withAdapter(handle, CodeLensAdapter, adapter => Promise.resolve(adapter.releaseCodeLenses(cacheId)), undefined);
}
// --- declaration

View File

@@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import * as vscode from 'vscode';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState, WebviewInsetHandle } from './extHost.protocol';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol';
import { Disposable } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as modes from 'vs/editor/common/modes';
@@ -16,7 +16,7 @@ import * as modes from 'vs/editor/common/modes';
type IconPath = URI | { light: URI, dark: URI };
export class ExtHostWebview implements vscode.Webview {
private readonly _handle: WebviewPanelHandle | WebviewInsetHandle;
private readonly _handle: WebviewPanelHandle;
private readonly _proxy: MainThreadWebviewsShape;
private _html: string;
private _options: vscode.WebviewOptions;
@@ -26,7 +26,7 @@ export class ExtHostWebview implements vscode.Webview {
public readonly onDidReceiveMessage: Event<any> = this._onMessageEmitter.event;
constructor(
handle: WebviewPanelHandle | WebviewInsetHandle,
handle: WebviewPanelHandle,
proxy: MainThreadWebviewsShape,
options: vscode.WebviewOptions
) {

View File

@@ -1,7 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import '../browser/extensionHost.contribution.common';
import './mainThreadWebview';

View File

@@ -1,437 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { onUnexpectedError } from 'vs/base/common/errors';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as map from 'vs/base/common/map';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import * as modes from 'vs/editor/common/modes';
import { localize } from 'vs/nls';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import product from 'vs/platform/product/node/product';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewInsetHandle, WebviewPanelHandle, WebviewPanelShowOptions } from 'vs/workbench/api/common/extHost.protocol';
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor';
import { CodeInsetController } from 'vs/workbench/contrib/codeinset/electron-browser/codeInset.contribution';
import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor';
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService';
import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { extHostNamedCustomer } from '../common/extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews extends Disposable implements MainThreadWebviewsShape {
private static readonly standardSupportedLinkSchemes = new Set([
'http',
'https',
'mailto',
product.urlProtocol,
'vscode',
'vscode-insiders'
]);
private static revivalPool = 0;
private readonly _proxy: ExtHostWebviewsShape;
private readonly _webviews = new Map<WebviewPanelHandle, WebviewEditorInput>();
private readonly _webviewsElements = new Map<WebviewInsetHandle, WebviewElement>();
private readonly _revivers = new Map<string, IDisposable>();
private _activeWebview: WebviewPanelHandle | undefined = undefined;
constructor(
context: IExtHostContext,
@ILifecycleService lifecycleService: ILifecycleService,
@IExtensionService extensionService: IExtensionService,
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
@IEditorService private readonly _editorService: IEditorService,
@IWebviewEditorService private readonly _webviewService: IWebviewEditorService,
@IOpenerService private readonly _openerService: IOpenerService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
) {
super();
this._proxy = context.getProxy(ExtHostContext.ExtHostWebviews);
this._register(_editorService.onDidActiveEditorChange(this.onActiveEditorChanged, this));
this._register(_editorService.onDidVisibleEditorsChange(this.onVisibleEditorsChanged, this));
// This reviver's only job is to activate webview extensions
// This should trigger the real reviver to be registered from the extension host side.
this._register(_webviewService.registerReviver({
canRevive: (webview) => {
const viewType = webview.state.viewType;
if (viewType) {
extensionService.activateByEvent(`onWebviewPanel:${viewType}`);
}
return false;
},
reviveWebview: () => { throw new Error('not implemented'); }
}));
this._register(lifecycleService.onBeforeShutdown(e => {
e.veto(this._onBeforeShutdown());
}, this));
}
public $createWebviewPanel(
handle: WebviewPanelHandle,
viewType: string,
title: string,
showOptions: { viewColumn?: EditorViewColumn, preserveFocus?: boolean },
options: WebviewInputOptions,
extensionId: ExtensionIdentifier,
extensionLocation: UriComponents
): void {
const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null);
if (showOptions) {
mainThreadShowOptions.preserveFocus = !!showOptions.preserveFocus;
mainThreadShowOptions.group = viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn);
}
const webview = this._webviewService.createWebview(this.getInternalWebviewId(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), {
location: URI.revive(extensionLocation),
id: extensionId
}, this.createWebviewEventDelegate(handle));
webview.state = {
viewType: viewType,
state: undefined
};
this._webviews.set(handle, webview);
/* __GDPR__
"webviews:createWebviewPanel" : {
"extensionId" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._telemetryService.publicLog('webviews:createWebviewPanel', { extensionId: extensionId.value });
}
$createWebviewCodeInset(
handle: WebviewInsetHandle,
symbolId: string,
options: modes.IWebviewOptions,
extensionId: ExtensionIdentifier,
extensionLocation: UriComponents
): void {
// todo@joh main is for the lack of a code-inset service
// which we maybe wanna have... this is how it now works
// 1) create webview element
// 2) find the code inset controller that request it
// 3) let the controller adopt the widget
// 4) continue to forward messages to the webview
const webview = this._instantiationService.createInstance(
WebviewElement,
{
extension: {
location: URI.revive(extensionLocation),
id: extensionId
},
enableFindWidget: false,
},
{
allowScripts: options.enableScripts,
}
);
let found = false;
for (const editor of this._codeEditorService.listCodeEditors()) {
const ctrl = CodeInsetController.get(editor);
if (ctrl && ctrl.acceptWebview(symbolId, webview)) {
found = true;
break;
}
}
if (!found) {
webview.dispose();
return;
}
// this will leak... the adopted webview will be disposed by the
// code inset controller. we might need a dispose-event here so that
// we can clean up things.
this._webviewsElements.set(handle, webview);
}
public $disposeWebview(handle: WebviewPanelHandle): void {
const webview = this.getWebview(handle);
webview.dispose();
}
public $setTitle(handle: WebviewPanelHandle, value: string): void {
const webview = this.getWebview(handle);
webview.setName(value);
}
public $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void {
const webview = this.getWebview(handle);
webview.iconPath = reviveWebviewIcon(value);
}
public $setHtml(handle: WebviewPanelHandle | WebviewInsetHandle, value: string): void {
if (typeof handle === 'number') {
this.getWebviewElement(handle).html = value;
} else {
const webview = this.getWebview(handle);
webview.html = value;
}
}
public $setOptions(handle: WebviewPanelHandle | WebviewInsetHandle, options: modes.IWebviewOptions): void {
if (typeof handle === 'number') {
this.getWebviewElement(handle).options = reviveWebviewOptions(options as any /*todo@mat */);
} else {
const webview = this.getWebview(handle);
webview.setOptions(reviveWebviewOptions(options as any /*todo@mat */));
}
}
public $reveal(handle: WebviewPanelHandle, showOptions: WebviewPanelShowOptions): void {
const webview = this.getWebview(handle);
if (webview.isDisposed()) {
return;
}
const targetGroup = this._editorGroupService.getGroup(viewColumnToEditorGroup(this._editorGroupService, showOptions.viewColumn)) || this._editorGroupService.getGroup(webview.group || 0);
if (targetGroup) {
this._webviewService.revealWebview(webview, targetGroup, !!showOptions.preserveFocus);
}
}
public async $postMessage(handle: WebviewPanelHandle | WebviewInsetHandle, message: any): Promise<boolean> {
if (typeof handle === 'number') {
this.getWebviewElement(handle).sendMessage(message);
return true;
} else {
const webview = this.getWebview(handle);
const editors = this._editorService.visibleControls
.filter(e => e instanceof WebviewEditor)
.map(e => e as WebviewEditor)
.filter(e => e.input!.matches(webview));
if (editors.length > 0) {
editors[0].sendMessage(message);
return true;
}
if (webview.webview) {
webview.webview.sendMessage(message);
return true;
}
return false;
}
}
public $registerSerializer(viewType: string): void {
if (this._revivers.has(viewType)) {
throw new Error(`Reviver for ${viewType} already registered`);
}
this._revivers.set(viewType, this._webviewService.registerReviver({
canRevive: (webview) => {
return webview.state && webview.state.viewType === viewType;
},
reviveWebview: async (webview): Promise<void> => {
const viewType = webview.state.viewType;
const handle = 'revival-' + MainThreadWebviews.revivalPool++;
this._webviews.set(handle, webview);
webview._events = this.createWebviewEventDelegate(handle);
let state = undefined;
if (webview.state.state) {
try {
state = JSON.parse(webview.state.state);
} catch {
// noop
}
}
try {
await this._proxy.$deserializeWebviewPanel(handle, viewType, webview.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webview.group || 0), webview.options);
} catch (error) {
onUnexpectedError(error);
webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType);
}
}
}));
}
public $unregisterSerializer(viewType: string): void {
const reviver = this._revivers.get(viewType);
if (!reviver) {
throw new Error(`No reviver for ${viewType} registered`);
}
reviver.dispose();
this._revivers.delete(viewType);
}
private getInternalWebviewId(viewType: string): string {
return `mainThreadWebview-${viewType}`;
}
private _onBeforeShutdown(): boolean {
this._webviews.forEach((webview) => {
if (!webview.isDisposed() && webview.state && this._revivers.has(webview.state.viewType)) {
webview.state.state = webview.webviewState;
}
});
return false; // Don't veto shutdown
}
private createWebviewEventDelegate(handle: WebviewPanelHandle) {
return {
onDidClickLink: (uri: URI) => this.onDidClickLink(handle, uri),
onMessage: (message: any) => this._proxy.$onMessage(handle, message),
onDispose: () => {
this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => {
this._webviews.delete(handle);
});
}
};
}
private onActiveEditorChanged() {
const activeEditor = this._editorService.activeControl;
let newActiveWebview: { input: WebviewEditorInput, handle: WebviewPanelHandle } | undefined = undefined;
if (activeEditor && activeEditor.input instanceof WebviewEditorInput) {
for (const handle of map.keys(this._webviews)) {
const input = this._webviews.get(handle)!;
if (input.matches(activeEditor.input)) {
newActiveWebview = { input, handle };
break;
}
}
}
if (newActiveWebview && newActiveWebview.handle === this._activeWebview) {
// Webview itself unchanged but position may have changed
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, {
active: true,
visible: true,
position: editorGroupToViewColumn(this._editorGroupService, newActiveWebview.input.group || 0)
});
return;
}
// Broadcast view state update for currently active
if (typeof this._activeWebview !== 'undefined') {
const oldActiveWebview = this._webviews.get(this._activeWebview);
if (oldActiveWebview) {
this._proxy.$onDidChangeWebviewPanelViewState(this._activeWebview, {
active: false,
visible: this._editorService.visibleControls.some(editor => !!editor.input && editor.input.matches(oldActiveWebview)),
position: editorGroupToViewColumn(this._editorGroupService, oldActiveWebview.group || 0),
});
}
}
// Then for newly active
if (newActiveWebview) {
this._proxy.$onDidChangeWebviewPanelViewState(newActiveWebview.handle, {
active: true,
visible: true,
position: editorGroupToViewColumn(this._editorGroupService, activeEditor ? activeEditor.group : ACTIVE_GROUP),
});
this._activeWebview = newActiveWebview.handle;
} else {
this._activeWebview = undefined;
}
}
private onVisibleEditorsChanged(): void {
this._webviews.forEach((input, handle) => {
for (const workbenchEditor of this._editorService.visibleControls) {
if (workbenchEditor.input && workbenchEditor.input.matches(input)) {
const editorPosition = editorGroupToViewColumn(this._editorGroupService, workbenchEditor.group!);
input.updateGroup(workbenchEditor.group!.id);
this._proxy.$onDidChangeWebviewPanelViewState(handle, {
active: handle === this._activeWebview,
visible: true,
position: editorPosition
});
break;
}
}
});
}
private onDidClickLink(handle: WebviewPanelHandle, link: URI): void {
if (!link) {
return;
}
const webview = this.getWebview(handle);
if (this.isSupportedLink(webview, link)) {
this._openerService.open(link);
}
}
private isSupportedLink(webview: WebviewEditorInput, link: URI): boolean {
if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) {
return true;
}
return !!webview.options.enableCommandUris && link.scheme === 'command';
}
private getWebview(handle: WebviewPanelHandle): WebviewEditorInput {
const webview = this._webviews.get(handle);
if (!webview) {
throw new Error('Unknown webview handle:' + handle);
}
return webview;
}
private getWebviewElement(handle: number): WebviewElement {
const webview = this._webviewsElements.get(handle);
if (!webview) {
throw new Error('Unknown webview handle:' + handle);
}
return webview;
}
private static getDeserializationFailedContents(viewType: string) {
return `<!DOCTYPE html>
<html>
<head>
<base href="https://code.visualstudio.com/raw/">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src https: data:; media-src https:; script-src 'none'; style-src vscode-core-resource: https: 'unsafe-inline'; child-src 'none'; frame-src 'none';">
</head>
<body>${localize('errorMessage', "An error occurred while restoring view:{0}", viewType)}</body>
</html>`;
}
}
function reviveWebviewOptions(options: WebviewInputOptions): WebviewInputOptions {
return {
...options,
localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(r => URI.revive(r)) : undefined,
};
}
function reviveWebviewIcon(
value: { light: UriComponents, dark: UriComponents } | undefined
): { light: URI, dark: URI } | undefined {
if (!value) {
return undefined;
}
return {
light: URI.revive(value.light),
dark: URI.revive(value.dark)
};
}

View File

@@ -67,6 +67,7 @@ import { withNullAsUndefined } from 'vs/base/common/types';
import { values } from 'vs/base/common/collections';
import { Schemas } from 'vs/base/common/network';
import { IURITransformer } from 'vs/base/common/uriIpc';
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
export interface IExtensionApiFactory {
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -109,6 +110,7 @@ export function createApiFactory(
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace);
rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration);
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors));
const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol));
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics, extHostLogService));
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
@@ -160,7 +162,7 @@ export function createApiFactory(
// Check document selectors for being overly generic. Technically this isn't a problem but
// in practice many extensions say they support `fooLang` but need fs-access to do so. Those
// extension should specify then the `file`-scheme, e.g `{ scheme: 'fooLang', language: 'fooLang' }`
// extension should specify then the `file`-scheme, e.g. `{ scheme: 'fooLang', language: 'fooLang' }`
// We only inform once, it is not a warning because we just want to raise awareness and because
// we cannot say if the extension is doing it right or wrong...
const checkSelector = (function () {
@@ -306,10 +308,6 @@ export function createApiFactory(
registerCodeLensProvider(selector: vscode.DocumentSelector, provider: vscode.CodeLensProvider): vscode.Disposable {
return extHostLanguageFeatures.registerCodeLensProvider(extension, checkSelector(selector), provider);
},
registerCodeInsetProvider(selector: vscode.DocumentSelector, provider: vscode.CodeInsetProvider): vscode.Disposable {
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerCodeInsetProvider(extension, checkSelector(selector), provider);
},
registerDefinitionProvider(selector: vscode.DocumentSelector, provider: vscode.DefinitionProvider): vscode.Disposable {
return extHostLanguageFeatures.registerDefinitionProvider(extension, checkSelector(selector), provider);
},
@@ -486,6 +484,10 @@ export function createApiFactory(
createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options);
},
createWebviewTextEditorInset(editor: vscode.TextEditor, range: vscode.Range, options: vscode.WebviewOptions): vscode.WebviewEditorInset {
checkProposedApiEnabled(extension);
return extHostEditorInsets.createWebviewEditorInset(editor, range, options);
},
createTerminal(nameOrOptions?: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
if (typeof nameOrOptions === 'object') {
nameOrOptions.runInBackground = nameOrOptions.runInBackground && extension.enableProposedApi;

View File

@@ -712,7 +712,7 @@ export class ExtHostTask implements ExtHostTaskShape {
paths[i] = resolver.resolve(ws, paths[i]);
}
}
result.process = win32.findExecutable(
result.process = await win32.findExecutable(
resolver.resolve(ws, toResolve.process.name),
toResolve.process.cwd !== undefined ? resolver.resolve(ws, toResolve.process.cwd) : undefined,
paths

View File

@@ -176,7 +176,7 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi
this._pidPromiseComplete(processId);
this._pidPromiseComplete = null;
} else {
// Recreate the promise if this is the nth processId set (eg. reused task terminals)
// Recreate the promise if this is the nth processId set (e.g. reused task terminals)
this._pidPromise.then(pid => {
if (pid !== processId) {
this._pidPromise = Promise.resolve(processId);